1 | /* SCCS Id: @(#)hacklib.c 3.3 99/04/10 */ 2 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 3 | /* Copyright (c) Robert Patrick Rankin, 1991 */ 4 | /* NetHack may be freely redistributed. See license for details. */ 5 | 6 | /* We could include only config.h, except for the overlay definitions... */ 7 | #include "hack.h" 8 | /*= 9 | Assorted 'small' utility routines. They're virtually independent of 10 | NetHack, except that rounddiv may call panic(). 11 | 12 | return type routine name argument type(s) 13 | boolean digit (char) 14 | boolean letter (char) 15 | char highc (char) 16 | char lowc (char) 17 | char * lcase (char *) 18 | char * mungspaces (char *) 19 | char * eos (char *) 20 | char * s_suffix (const char *) 21 | char * xcrypt (const char *, char *) 22 | boolean onlyspace (const char *) 23 | char * tabexpand (char *) 24 | char * visctrl (char) 25 | const char * ordin (int) 26 | char * sitoa (int) 27 | int sgn (int) 28 | int rounddiv (long, int) 29 | int distmin (int, int, int, int) 30 | int dist2 (int, int, int, int) 31 | boolean online2 (int, int) 32 | boolean pmatch (const char *, const char *) 33 | int strncmpi (const char *, const char *, int) 34 | char * strstri (const char *, const char *) 35 | boolean fuzzymatch (const char *,const char *,const char *,boolean) 36 | void setrandom (void) 37 | int getyear (void) 38 | char * yymmdd (time_t) 39 | long yyyymmdd (time_t) 40 | int phase_of_the_moon (void) 41 | boolean friday_13th (void) 42 | int night (void) 43 | int midnight (void) 44 | =*/ 45 | #ifdef LINT 46 | # define Static /* pacify lint */ 47 | #else 48 | # define Static static 49 | #endif 50 | 51 | #ifdef OVLB 52 | boolean 53 | digit(c) /* is 'c' a digit? */ 54 | char c; 55 | { 56 | return((boolean)('0' <= c && c <= '9')); 57 | } 58 | 59 | boolean 60 | letter(c) /* is 'c' a letter? note: '@' classed as letter */ 61 | char c; 62 | { 63 | return((boolean)(('@' <= c && c <= 'Z') || ('a' <= c && c <= 'z'))); 64 | } 65 | #endif /* OVLB */ 66 | 67 | #ifdef OVL1 68 | char 69 | highc(c) /* force 'c' into uppercase */ 70 | char c; 71 | { 72 | return((char)(('a' <= c && c <= 'z') ? (c & ~040) : c)); 73 | } 74 | 75 | char 76 | lowc(c) /* force 'c' into lowercase */ 77 | char c; 78 | { 79 | return((char)(('A' <= c && c <= 'Z') ? (c | 040) : c)); 80 | } 81 | #endif /* OVL1 */ 82 | 83 | #ifdef OVLB 84 | char * 85 | lcase(s) /* convert a string into all lowercase */ 86 | char *s; 87 | { 88 | register char *p; 89 | 90 | for (p = s; *p; p++) 91 | if ('A' <= *p && *p <= 'Z') *p |= 040; 92 | return s; 93 | } 94 | 95 | /* remove excess whitespace from a string buffer (in place) */ 96 | char * 97 | mungspaces(bp) 98 | char *bp; 99 | { 100 | register char c, *p, *p2; 101 | boolean was_space = TRUE; 102 | 103 | for (p = p2 = bp; (c = *p) != '\0'; p++) { 104 | if (c == '\t') c = ' '; 105 | if (c != ' ' || !was_space) *p2++ = c; 106 | was_space = (c == ' '); 107 | } 108 | if (was_space && p2 > bp) p2--; 109 | *p2 = '\0'; 110 | return bp; 111 | } 112 | 113 | #endif /* OVLB */ 114 | 115 | #ifdef OVL0 116 | char * 117 | eos(s) /* return the end of a string (pointing at '\0') */ 118 | register char *s; 119 | { 120 | while (*s) s++; /* s += strlen(s); */ 121 | return s; 122 | } 123 | 124 | char * 125 | s_suffix(s) /* return a name converted to possessive */ 126 | const char *s; 127 | { 128 | Static char buf[BUFSZ]; 129 | 130 | Strcpy(buf, s); 131 | if(!strcmpi(buf, "it")) 132 | Strcat(buf, "s"); 133 | else if(*(eos(buf)-1) == 's') 134 | Strcat(buf, "'"); 135 | else 136 | Strcat(buf, "'s"); 137 | return buf; 138 | } 139 | 140 | char * 141 | xcrypt(str, buf) /* trivial text encryption routine (see makedefs) */ 142 | const char *str; 143 | char *buf; 144 | { 145 | register const char *p; 146 | register char *q; 147 | register int bitmask; 148 | 149 | for (bitmask = 1, p = str, q = buf; *p; q++) { 150 | *q = *p++; 151 | if (*q & (32|64)) *q ^= bitmask; 152 | if ((bitmask <<= 1) >= 32) bitmask = 1; 153 | } 154 | *q = '\0'; 155 | return buf; 156 | } 157 | #endif /* OVL0 */ 158 | 159 | #ifdef OVL2 160 | boolean 161 | onlyspace(s) /* is a string entirely whitespace? */ 162 | const char *s; 163 | { 164 | for (; *s; s++) 165 | if (*s != ' ' && *s != '\t') return FALSE; 166 | return TRUE; 167 | } 168 | #endif /* OVL2 */ 169 | 170 | #ifdef OVLB 171 | char * 172 | tabexpand(sbuf) /* expand tabs into proper number of spaces */ 173 | char *sbuf; 174 | { 175 | char buf[BUFSZ]; 176 | register char *bp, *s = sbuf; 177 | register int idx; 178 | 179 | if (!*s) return sbuf; 180 | 181 | /* warning: no bounds checking performed */ 182 | for (bp = buf, idx = 0; *s; s++) 183 | if (*s == '\t') { 184 | do *bp++ = ' '; while (++idx % 8); 185 | } else { 186 | *bp++ = *s; 187 | idx++; 188 | } 189 | *bp = 0; 190 | return strcpy(sbuf, buf); 191 | } 192 | 193 | char * 194 | visctrl(c) /* make a displayable string from a character */ 195 | char c; 196 | { 197 | Static char ccc[3]; 198 | 199 | c &= 0177; 200 | 201 | ccc[2] = '\0'; 202 | if (c < 040) { 203 | ccc[0] = '^'; 204 | ccc[1] = c | 0100; /* letter */ 205 | } else if (c == 0177) { 206 | ccc[0] = '^'; 207 | ccc[1] = c & ~0100; /* '?' */ 208 | } else { 209 | ccc[0] = c; /* printable character */ 210 | ccc[1] = '\0'; 211 | } 212 | return ccc; 213 | } 214 | #endif /* OVLB */ 215 | 216 | #ifdef OVL2 217 | const char * 218 | ordin(n) /* return the ordinal suffix of a number */ 219 | int n; /* note: should be non-negative */ 220 | { 221 | register int dd = n % 10; 222 | 223 | return (dd == 0 || dd > 3 || (n % 100) / 10 == 1) ? "th" : 224 | (dd == 1) ? "st" : (dd == 2) ? "nd" : "rd"; 225 | } 226 | #endif /* OVL2 */ 227 | 228 | #ifdef OVL1 229 | char * 230 | sitoa(n) /* make a signed digit string from a number */ 231 | int n; 232 | { 233 | Static char buf[13]; 234 | 235 | Sprintf(buf, (n < 0) ? "%d" : "+%d", n); 236 | return buf; 237 | } 238 | 239 | int 240 | sgn(n) /* return the sign of a number: -1, 0, or 1 */ 241 | int n; 242 | { 243 | return (n < 0) ? -1 : (n != 0); 244 | } 245 | #endif /* OVL1 */ 246 | 247 | #ifdef OVLB 248 | int 249 | rounddiv(x, y) /* calculate x/y, rounding as appropriate */ 250 | long x; 251 | int y; 252 | { 253 | int r, m; 254 | int divsgn = 1; 255 | 256 | if (y == 0) 257 | panic("division by zero in rounddiv"); 258 | else if (y < 0) { 259 | divsgn = -divsgn; y = -y; 260 | } 261 | if (x < 0) { 262 | divsgn = -divsgn; x = -x; 263 | } 264 | r = x / y; 265 | m = x % y; 266 | if (2*m >= y) r++; 267 | 268 | return divsgn * r; 269 | } 270 | #endif /* OVLB */ 271 | 272 | #ifdef OVL0 273 | int 274 | distmin(x0, y0, x1, y1) /* distance between two points, in moves */ 275 | int x0, y0, x1, y1; 276 | { 277 | register int dx = x0 - x1, dy = y0 - y1; 278 | if (dx < 0) dx = -dx; 279 | if (dy < 0) dy = -dy; 280 | /* The minimum number of moves to get from (x0,y0) to (x1,y1) is the 281 | : larger of the [absolute value of the] two deltas. 282 | */ 283 | return (dx < dy) ? dy : dx; 284 | } 285 | 286 | int 287 | dist2(x0, y0, x1, y1) /* square of euclidean distance between pair of pts */ 288 | int x0, y0, x1, y1; 289 | { 290 | register int dx = x0 - x1, dy = y0 - y1; 291 | return dx * dx + dy * dy; 292 | } 293 | 294 | boolean 295 | online2(x0, y0, x1, y1) /* are two points lined up (on a straight line)? */ 296 | int x0, y0, x1, y1; 297 | { 298 | int dx = x0 - x1, dy = y0 - y1; 299 | /* If either delta is zero then they're on an orthogonal line, 300 | * else if the deltas are equal (signs ignored) they're on a diagonal. 301 | */ 302 | return((boolean)(!dy || !dx || (dy == dx) || (dy + dx == 0))); /* (dy == -dx) */ 303 | } 304 | 305 | #endif /* OVL0 */ 306 | #ifdef OVLB 307 | 308 | boolean 309 | pmatch(patrn, strng) /* match a string against a pattern */ 310 | const char *patrn, *strng; 311 | { 312 | char s, p; 313 | /* 314 | : Simple pattern matcher: '*' matches 0 or more characters, '?' matches 315 | : any single character. Returns TRUE if 'strng' matches 'patrn'. 316 | */ 317 | pmatch_top: 318 | s = *strng++; p = *patrn++; /* get next chars and pre-advance */ 319 | if (!p) /* end of pattern */ 320 | return((boolean)(s == '\0')); /* matches iff end of string too */ 321 | else if (p == '*') /* wildcard reached */ 322 | return((boolean)((!*patrn || pmatch(patrn, strng-1)) ? TRUE : 323 | s ? pmatch(patrn-1, strng) : FALSE)); 324 | else if (p != s && (p != '?' || !s)) /* check single character */ 325 | return FALSE; /* doesn't match */ 326 | else /* return pmatch(patrn, strng); */ 327 | goto pmatch_top; /* optimize tail recursion */ 328 | } 329 | #endif /* OVLB */ 330 | 331 | #ifdef OVL2 332 | #ifndef STRNCMPI 333 | int 334 | strncmpi(s1, s2, n) /* case insensitive counted string comparison */ 335 | register const char *s1, *s2; 336 | register int n; /*(should probably be size_t, which is usually unsigned)*/ 337 | { /*{ aka strncasecmp }*/ 338 | register char t1, t2; 339 | 340 | while (n--) { 341 | if (!*s2) return (*s1 != 0); /* s1 >= s2 */ 342 | else if (!*s1) return -1; /* s1 < s2 */ 343 | t1 = lowc(*s1++); 344 | t2 = lowc(*s2++); 345 | if (t1 != t2) return (t1 > t2) ? 1 : -1; 346 | } 347 | return 0; /* s1 == s2 */ 348 | } 349 | #endif /* STRNCMPI */ 350 | #endif /* OVL2 */ 351 | 352 | #ifdef OVLB 353 | #ifndef STRSTRI 354 | 355 | char * 356 | strstri(str, sub) /* case insensitive substring search */ 357 | const char *str; 358 | const char *sub; 359 | { 360 | register const char *s1, *s2; 361 | register int i, k; 362 | # define TABSIZ 0x20 /* 0x40 would be case-sensitive */ 363 | char tstr[TABSIZ], tsub[TABSIZ]; /* nibble count tables */ 364 | # if 0 365 | assert( (TABSIZ & ~(TABSIZ-1)) == TABSIZ ); /* must be exact power of 2 */ 366 | assert( &lowc != 0 ); /* can't be unsafe macro */ 367 | # endif 368 | 369 | /* special case: empty substring */ 370 | if (!*sub) return (char *) str; 371 | 372 | /* do some useful work while determining relative lengths */ 373 | for (i = 0; i < TABSIZ; i++) tstr[i] = tsub[i] = 0; /* init */ 374 | for (k = 0, s1 = str; *s1; k++) tstr[*s1++ & (TABSIZ-1)]++; 375 | for ( s2 = sub; *s2; --k) tsub[*s2++ & (TABSIZ-1)]++; 376 | 377 | /* evaluate the info we've collected */ 378 | if (k < 0) return (char *) 0; /* sub longer than str, so can't match */ 379 | for (i = 0; i < TABSIZ; i++) /* does sub have more 'x's than str? */ 380 | if (tsub[i] > tstr[i]) return (char *) 0; /* match not possible */ 381 | 382 | /* now actually compare the substring repeatedly to parts of the string */ 383 | for (i = 0; i <= k; i++) { 384 | s1 = &str[i]; 385 | s2 = sub; 386 | while (lowc(*s1++) == lowc(*s2++)) 387 | if (!*s2) return (char *) &str[i]; /* full match */ 388 | } 389 | return (char *) 0; /* not found */ 390 | } 391 | #endif /* STRSTRI */ 392 | 393 | /* compare two strings for equality, ignoring the presence of specified 394 | characters (typically whitespace) and possibly ignoring case */ 395 | boolean 396 | fuzzymatch(s1, s2, ignore_chars, caseblind) 397 | const char *s1, *s2; 398 | const char *ignore_chars; 399 | boolean caseblind; 400 | { 401 | register char c1, c2; 402 | 403 | do { 404 | while ((c1 = *s1++) != '\0' && index(ignore_chars, c1) != 0) continue; 405 | while ((c2 = *s2++) != '\0' && index(ignore_chars, c2) != 0) continue; 406 | if (!c1 || !c2) break; /* stop when end of either string is reached */ 407 | 408 | if (caseblind) { 409 | c1 = lowc(c1); 410 | c2 = lowc(c2); 411 | } 412 | } while (c1 == c2); 413 | 414 | /* match occurs only when the end of both strings has been reached */ 415 | return (boolean)(!c1 && !c2); 416 | } 417 | 418 | #endif /* OVLB */ 419 | #ifdef OVL2 420 | 421 | /* 422 | * Time routines 423 | * 424 | * The time is used for: 425 | * - seed for rand() 426 | * - year on tombstone and yyyymmdd in record file 427 | * - phase of the moon (various monsters react to NEW_MOON or FULL_MOON) 428 | * - night and midnight (the undead are dangerous at midnight) 429 | * - determination of what files are "very old" 430 | */ 431 | 432 | #if defined(AMIGA) && !defined(AZTEC_C) && !defined(__SASC_60) && !defined(_DCC) 433 | extern struct tm *FDECL(localtime,(time_t *)); 434 | #endif 435 | static struct tm *NDECL(getlt); 436 | 437 | void 438 | setrandom() 439 | { 440 | /* the types are different enough here that sweeping the different 441 | * routine names into one via #defines is even more confusing 442 | */ 443 | #ifdef RANDOM /* srandom() from sys/share/random.c */ 444 | srandom((unsigned int) time((time_t *)0)); 445 | #else 446 | # if defined(BSD) || defined(ULTRIX) || defined(CYGWIN32) /* system srandom() */ 447 | # ifdef BSD 448 | # if defined(SUNOS4) 449 | (void) 450 | # endif 451 | srandom((int) time((long *)0)); 452 | # else 453 | srandom((int) time((time_t *)0)); 454 | # endif 455 | # else 456 | # ifdef UNIX /* system srand48() */ 457 | srand48((long) time((time_t *)0)); 458 | # else /* poor quality system routine */ 459 | srand((int) time((time_t *)0)); 460 | # endif 461 | # endif 462 | #endif 463 | } 464 | 465 | static struct tm * 466 | getlt() 467 | { 468 | time_t date; 469 | 470 | #ifdef BSD 471 | (void) time((long *)(&date)); 472 | #else 473 | (void) time(&date); 474 | #endif 475 | #if (defined(ULTRIX) && !(defined(ULTRIX_PROTO) || defined(NHSTDC))) || defined(BSD) 476 | return(localtime((long *)(&date))); 477 | #else 478 | return(localtime(&date)); 479 | #endif 480 | } 481 | 482 | int 483 | getyear() 484 | { 485 | return(1900 + getlt()->tm_year); 486 | } 487 | 488 | #if 0 489 | /* This routine is no longer used since in 2000 it will yield "100mmdd". */ 490 | char * 491 | yymmdd(date) 492 | time_t date; 493 | { 494 | Static char datestr[10]; 495 | struct tm *lt; 496 | 497 | if (date == 0) 498 | lt = getlt(); 499 | else 500 | #if (defined(ULTRIX) && !(defined(ULTRIX_PROTO) || defined(NHSTDC))) || defined(BSD) 501 | lt = localtime((long *)(&date)); 502 | #else 503 | lt = localtime(&date); 504 | #endif 505 | 506 | Sprintf(datestr, "%02d%02d%02d", 507 | lt->tm_year, lt->tm_mon + 1, lt->tm_mday); 508 | return(datestr); 509 | } 510 | #endif 511 | 512 | long 513 | yyyymmdd(date) 514 | time_t date; 515 | { 516 | long datenum; 517 | struct tm *lt; 518 | 519 | if (date == 0) 520 | lt = getlt(); 521 | else 522 | #if (defined(ULTRIX) && !(defined(ULTRIX_PROTO) || defined(NHSTDC))) || defined(BSD) 523 | lt = localtime((long *)(&date)); 524 | #else 525 | lt = localtime(&date); 526 | #endif 527 | 528 | /* just in case somebody's localtime supplies (year % 100) 529 | rather than the expected (year - 1900) */ 530 | if (lt->tm_year < 70) 531 | datenum = (long)lt->tm_year + 2000L; 532 | else 533 | datenum = (long)lt->tm_year + 1900L; 534 | /* yyyy --> yyyymm */ 535 | datenum = datenum * 100L + (long)(lt->tm_mon + 1); 536 | /* yyyymm --> yyyymmdd */ 537 | datenum = datenum * 100L + (long)lt->tm_mday; 538 | return datenum; 539 | } 540 | 541 | /* 542 | * moon period = 29.53058 days ~= 30, year = 365.2422 days 543 | * days moon phase advances on first day of year compared to preceding year 544 | * = 365.2422 - 12*29.53058 ~= 11 545 | * years in Metonic cycle (time until same phases fall on the same days of 546 | * the month) = 18.6 ~= 19 547 | * moon phase on first day of year (epact) ~= (11*(year%19) + 29) % 30 548 | * (29 as initial condition) 549 | * current phase in days = first day phase + days elapsed in year 550 | * 6 moons ~= 177 days 551 | * 177 ~= 8 reported phases * 22 552 | * + 11/22 for rounding 553 | */ 554 | int 555 | phase_of_the_moon() /* 0-7, with 0: new, 4: full */ 556 | { 557 | register struct tm *lt = getlt(); 558 | register int epact, diy, goldn; 559 | 560 | diy = lt->tm_yday; 561 | goldn = (lt->tm_year % 19) + 1; 562 | epact = (11 * goldn + 18) % 30; 563 | if ((epact == 25 && goldn > 11) || epact == 24) 564 | epact++; 565 | 566 | return( (((((diy + epact) * 6) + 11) % 177) / 22) & 7 ); 567 | } 568 | 569 | boolean 570 | friday_13th() 571 | { 572 | register struct tm *lt = getlt(); 573 | 574 | return((boolean)(lt->tm_wday == 5 /* friday */ && lt->tm_mday == 13)); 575 | } 576 | 577 | int 578 | night() 579 | { 580 | register int hour = getlt()->tm_hour; 581 | 582 | return(hour < 6 || hour > 21); 583 | } 584 | 585 | int 586 | midnight() 587 | { 588 | return(getlt()->tm_hour == 0); 589 | } 590 | #endif /* OVL2 */ 591 | 592 | /*hacklib.c*/