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*/