NHDT->ANH, nethack->anethack, nhdat->anhdat
[aNetHack.git] / src / hacklib.c
blobccc7eb0e1549b17989948d2b19ff2b62a379c443
1 /* aNetHack 0.0.1 hacklib.c $ANH-Date: 1472006251 2016/08/24 02:37:31 $ $ANH-Branch: master $:$ANH-Revision: 1.48 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* Copyright (c) Robert Patrick Rankin, 1991 */
4 /* aNetHack may be freely redistributed. See license for details. */
6 #include "hack.h" /* for config.h+extern.h */
7 /*=
8 Assorted 'small' utility routines. They're virtually independent of
9 aNetHack, except that rounddiv may call panic(). setrandom calls one
10 of srandom(), srand48(), or srand() depending upon configuration.
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 * ucase (char *)
19 char * upstart (char *)
20 char * mungspaces (char *)
21 char * trimspaces (char *)
22 char * strip_newline (char *)
23 char * eos (char *)
24 boolean str_end_is (const char *, const char *)
25 char * strkitten (char *,char)
26 void copynchars (char *,const char *,int)
27 char chrcasecpy (int,int)
28 char * strcasecpy (char *,const char *)
29 char * s_suffix (const char *)
30 char * ing_suffix (const char *)
31 char * xcrypt (const char *, char *)
32 boolean onlyspace (const char *)
33 char * tabexpand (char *)
34 char * visctrl (char)
35 char * strsubst (char *, const char *, const char *)
36 const char * ordin (int)
37 char * sitoa (int)
38 int sgn (int)
39 int rounddiv (long, int)
40 int distmin (int, int, int, int)
41 int dist2 (int, int, int, int)
42 boolean online2 (int, int)
43 boolean pmatch (const char *, const char *)
44 boolean pmatchi (const char *, const char *)
45 boolean pmatchz (const char *, const char *)
46 int strncmpi (const char *, const char *, int)
47 char * strstri (const char *, const char *)
48 boolean fuzzymatch (const char *,const char *,
49 const char *, boolean)
50 void setrandom (void)
51 time_t getnow (void)
52 int getyear (void)
53 char * yymmdd (time_t)
54 long yyyymmdd (time_t)
55 long hhmmss (time_t)
56 char * yyyymmddhhmmss (time_t)
57 time_t time_from_yyyymmddhhmmss (char *)
58 int phase_of_the_moon (void)
59 boolean friday_13th (void)
60 int night (void)
61 int midnight (void)
62 =*/
63 #ifdef LINT
64 #define Static /* pacify lint */
65 #else
66 #define Static static
67 #endif
69 static boolean FDECL(pmatch_internal, (const char *, const char *,
70 BOOLEAN_P, const char *));
72 /* is 'c' a digit? */
73 boolean
74 digit(c)
75 char c;
77 return (boolean) ('0' <= c && c <= '9');
80 /* is 'c' a letter? note: '@' classed as letter */
81 boolean
82 letter(c)
83 char c;
85 return (boolean) ('@' <= c && c <= 'Z') || ('a' <= c && c <= 'z');
88 /* force 'c' into uppercase */
89 char
90 highc(c)
91 char c;
93 return (char) (('a' <= c && c <= 'z') ? (c & ~040) : c);
96 /* force 'c' into lowercase */
97 char
98 lowc(c)
99 char c;
101 return (char) (('A' <= c && c <= 'Z') ? (c | 040) : c);
104 /* convert a string into all lowercase */
105 char *
106 lcase(s)
107 char *s;
109 register char *p;
111 for (p = s; *p; p++)
112 if ('A' <= *p && *p <= 'Z')
113 *p |= 040;
114 return s;
117 /* convert a string into all uppercase */
118 char *
119 ucase(s)
120 char *s;
122 register char *p;
124 for (p = s; *p; p++)
125 if ('a' <= *p && *p <= 'z')
126 *p &= ~040;
127 return s;
130 /* convert first character of a string to uppercase */
131 char *
132 upstart(s)
133 char *s;
135 if (s)
136 *s = highc(*s);
137 return s;
140 /* remove excess whitespace from a string buffer (in place) */
141 char *
142 mungspaces(bp)
143 char *bp;
145 register char c, *p, *p2;
146 boolean was_space = TRUE;
148 for (p = p2 = bp; (c = *p) != '\0'; p++) {
149 if (c == '\n')
150 break; /* treat newline the same as end-of-string */
151 if (c == '\t')
152 c = ' ';
153 if (c != ' ' || !was_space)
154 *p2++ = c;
155 was_space = (c == ' ');
157 if (was_space && p2 > bp)
158 p2--;
159 *p2 = '\0';
160 return bp;
163 /* remove leading and trailing whitespace, in place */
164 char*
165 trimspaces(txt)
166 char* txt;
168 char* end;
170 while (*txt == ' ' || *txt == '\t')
171 txt++;
172 end = eos(txt);
173 while (--end >= txt && (*end == ' ' || *end == '\t'))
174 *end = '\0';
176 return txt;
179 /* remove \n from end of line; remove \r too if one is there */
180 char *
181 strip_newline(str)
182 char *str;
184 char *p = index(str, '\n');
186 if (p) {
187 if (p > str && *(p - 1) == '\r')
188 --p;
189 *p = '\0';
191 return str;
194 /* return the end of a string (pointing at '\0') */
195 char *
196 eos(s)
197 register char *s;
199 while (*s)
200 s++; /* s += strlen(s); */
201 return s;
204 /* determine whether 'str' ends in 'chkstr' */
205 boolean
206 str_end_is(str, chkstr)
207 const char *str, *chkstr;
209 int clen = (int) strlen(chkstr);
211 if ((int) strlen(str) >= clen)
212 return (boolean) (!strncmp(eos((char *) str) - clen, chkstr, clen));
213 return FALSE;
216 /* append a character to a string (in place): strcat(s, {c,'\0'}); */
217 char *
218 strkitten(s, c)
219 char *s;
220 char c;
222 char *p = eos(s);
224 *p++ = c;
225 *p = '\0';
226 return s;
229 /* truncating string copy */
230 void
231 copynchars(dst, src, n)
232 char *dst;
233 const char *src;
234 int n;
236 /* copies at most n characters, stopping sooner if terminator reached;
237 treats newline as input terminator; unlike strncpy, always supplies
238 '\0' terminator so dst must be able to hold at least n+1 characters */
239 while (n > 0 && *src != '\0' && *src != '\n') {
240 *dst++ = *src++;
241 --n;
243 *dst = '\0';
246 /* convert char nc into oc's case; mostly used by strcasecpy */
247 char
248 chrcasecpy(oc, nc)
249 int oc, nc;
251 #if 0 /* this will be necessary if we switch to <ctype.h> */
252 oc = (int) (unsigned char) oc;
253 nc = (int) (unsigned char) nc;
254 #endif
255 if ('a' <= oc && oc <= 'z') {
256 /* old char is lower case; if new char is upper case, downcase it */
257 if ('A' <= nc && nc <= 'Z')
258 nc += 'a' - 'A'; /* lowc(nc) */
259 } else if ('A' <= oc && oc <= 'Z') {
260 /* old char is upper case; if new char is lower case, upcase it */
261 if ('a' <= nc && nc <= 'z')
262 nc += 'A' - 'a'; /* highc(nc) */
264 return (char) nc;
267 /* overwrite string, preserving old chars' case;
268 for case-insensitive editions of makeplural() and makesingular();
269 src might be shorter, same length, or longer than dst */
270 char *
271 strcasecpy(dst, src)
272 char *dst;
273 const char *src;
275 char *result = dst;
276 int ic, oc, dst_exhausted = 0;
278 /* while dst has characters, replace each one with corresponding
279 character from src, converting case in the process if they differ;
280 once dst runs out, propagate the case of its last character to any
281 remaining src; if dst starts empty, it must be a pointer to the
282 tail of some other string because we examine the char at dst[-1] */
283 while ((ic = (int) *src++) != '\0') {
284 if (!dst_exhausted && !*dst)
285 dst_exhausted = 1;
286 oc = (int) *(dst - dst_exhausted);
287 *dst++ = chrcasecpy(oc, ic);
289 *dst = '\0';
290 return result;
293 /* return a name converted to possessive */
294 char *
295 s_suffix(s)
296 const char *s;
298 Static char buf[BUFSZ];
300 Strcpy(buf, s);
301 if (!strcmpi(buf, "it")) /* it -> its */
302 Strcat(buf, "s");
303 else if (!strcmpi(buf, "you")) /* you -> your */
304 Strcat(buf, "r");
305 else if (*(eos(buf) - 1) == 's') /* Xs -> Xs' */
306 Strcat(buf, "'");
307 else /* X -> X's */
308 Strcat(buf, "'s");
309 return buf;
312 /* construct a gerund (a verb formed by appending "ing" to a noun) */
313 char *
314 ing_suffix(s)
315 const char *s;
317 static const char vowel[] = "aeiouwy";
318 static char buf[BUFSZ];
319 char onoff[10];
320 char *p;
322 Strcpy(buf, s);
323 p = eos(buf);
324 onoff[0] = *p = *(p + 1) = '\0';
325 if ((p >= &buf[3] && !strcmpi(p - 3, " on"))
326 || (p >= &buf[4] && !strcmpi(p - 4, " off"))
327 || (p >= &buf[5] && !strcmpi(p - 5, " with"))) {
328 p = rindex(buf, ' ');
329 Strcpy(onoff, p);
330 *p = '\0';
332 if (p >= &buf[3] && !index(vowel, *(p - 1))
333 && index(vowel, *(p - 2)) && !index(vowel, *(p - 3))) {
334 /* tip -> tipp + ing */
335 *p = *(p - 1);
336 *(p + 1) = '\0';
337 } else if (p >= &buf[2] && !strcmpi(p - 2, "ie")) { /* vie -> vy + ing */
338 *(p - 2) = 'y';
339 *(p - 1) = '\0';
340 } else if (p >= &buf[1] && *(p - 1) == 'e') /* grease -> greas + ing */
341 *(p - 1) = '\0';
342 Strcat(buf, "ing");
343 if (onoff[0])
344 Strcat(buf, onoff);
345 return buf;
348 /* trivial text encryption routine (see makedefs) */
349 char *
350 xcrypt(str, buf)
351 const char *str;
352 char *buf;
354 register const char *p;
355 register char *q;
356 register int bitmask;
358 for (bitmask = 1, p = str, q = buf; *p; q++) {
359 *q = *p++;
360 if (*q & (32 | 64))
361 *q ^= bitmask;
362 if ((bitmask <<= 1) >= 32)
363 bitmask = 1;
365 *q = '\0';
366 return buf;
369 /* is a string entirely whitespace? */
370 boolean
371 onlyspace(s)
372 const char *s;
374 for (; *s; s++)
375 if (*s != ' ' && *s != '\t')
376 return FALSE;
377 return TRUE;
380 /* expand tabs into proper number of spaces */
381 char *
382 tabexpand(sbuf)
383 char *sbuf;
385 char buf[BUFSZ];
386 register char *bp, *s = sbuf;
387 register int idx;
389 if (!*s)
390 return sbuf;
391 /* warning: no bounds checking performed */
392 for (bp = buf, idx = 0; *s; s++)
393 if (*s == '\t') {
395 *bp++ = ' ';
396 while (++idx % 8);
397 } else {
398 *bp++ = *s;
399 idx++;
401 *bp = 0;
402 return strcpy(sbuf, buf);
405 #define VISCTRL_NBUF 5
406 /* make a displayable string from a character */
407 char *
408 visctrl(c)
409 char c;
411 Static char visctrl_bufs[VISCTRL_NBUF][5];
412 static int nbuf = 0;
413 register int i = 0;
414 char *ccc = visctrl_bufs[nbuf];
415 nbuf = (nbuf + 1) % VISCTRL_NBUF;
417 if ((uchar) c & 0200) {
418 ccc[i++] = 'M';
419 ccc[i++] = '-';
421 c &= 0177;
422 if (c < 040) {
423 ccc[i++] = '^';
424 ccc[i++] = c | 0100; /* letter */
425 } else if (c == 0177) {
426 ccc[i++] = '^';
427 ccc[i++] = c & ~0100; /* '?' */
428 } else {
429 ccc[i++] = c; /* printable character */
431 ccc[i] = '\0';
432 return ccc;
435 /* substitute a word or phrase in a string (in place) */
436 /* caller is responsible for ensuring that bp points to big enough buffer */
437 char *
438 strsubst(bp, orig, replacement)
439 char *bp;
440 const char *orig, *replacement;
442 char *found, buf[BUFSZ];
444 if (bp) {
445 found = strstr(bp, orig);
446 if (found) {
447 Strcpy(buf, found + strlen(orig));
448 Strcpy(found, replacement);
449 Strcat(bp, buf);
452 return bp;
455 /* return the ordinal suffix of a number */
456 const char *
457 ordin(n)
458 int n; /* note: should be non-negative */
460 register int dd = n % 10;
462 return (dd == 0 || dd > 3 || (n % 100) / 10 == 1) ? "th"
463 : (dd == 1) ? "st" : (dd == 2) ? "nd" : "rd";
466 /* make a signed digit string from a number */
467 char *
468 sitoa(n)
469 int n;
471 Static char buf[13];
473 Sprintf(buf, (n < 0) ? "%d" : "+%d", n);
474 return buf;
477 /* return the sign of a number: -1, 0, or 1 */
479 sgn(n)
480 int n;
482 return (n < 0) ? -1 : (n != 0);
485 /* calculate x/y, rounding as appropriate */
487 rounddiv(x, y)
488 long x;
489 int y;
491 int r, m;
492 int divsgn = 1;
494 if (y == 0)
495 panic("division by zero in rounddiv");
496 else if (y < 0) {
497 divsgn = -divsgn;
498 y = -y;
500 if (x < 0) {
501 divsgn = -divsgn;
502 x = -x;
504 r = x / y;
505 m = x % y;
506 if (2 * m >= y)
507 r++;
509 return divsgn * r;
512 /* distance between two points, in moves */
514 distmin(x0, y0, x1, y1)
515 int x0, y0, x1, y1;
517 register int dx = x0 - x1, dy = y0 - y1;
519 if (dx < 0)
520 dx = -dx;
521 if (dy < 0)
522 dy = -dy;
523 /* The minimum number of moves to get from (x0,y0) to (x1,y1) is the
524 * larger of the [absolute value of the] two deltas.
526 return (dx < dy) ? dy : dx;
529 /* square of euclidean distance between pair of pts */
531 dist2(x0, y0, x1, y1)
532 int x0, y0, x1, y1;
534 register int dx = x0 - x1, dy = y0 - y1;
536 return dx * dx + dy * dy;
539 /* integer square root function without using floating point */
541 isqrt(val)
542 int val;
544 int rt = 0;
545 int odd = 1;
547 * This could be replaced by a faster algorithm, but has not been because:
548 * + the simple algorithm is easy to read;
549 * + this algorithm does not require 64-bit support;
550 * + in current usage, the values passed to isqrt() are not really that
551 * large, so the performance difference is negligible;
552 * + isqrt() is used in only few places, which are not bottle-necks.
554 while (val >= odd) {
555 val = val - odd;
556 odd = odd + 2;
557 rt = rt + 1;
559 return rt;
562 /* are two points lined up (on a straight line)? */
563 boolean
564 online2(x0, y0, x1, y1)
565 int x0, y0, x1, y1;
567 int dx = x0 - x1, dy = y0 - y1;
568 /* If either delta is zero then they're on an orthogonal line,
569 * else if the deltas are equal (signs ignored) they're on a diagonal.
571 return (boolean) (!dy || !dx || dy == dx || dy == -dx);
574 /* guts of pmatch(), pmatchi(), and pmatchz();
575 match a string against a pattern */
576 static boolean
577 pmatch_internal(patrn, strng, ci, sk)
578 const char *patrn, *strng;
579 boolean ci; /* True => case-insensitive, False => case-sensitive */
580 const char *sk; /* set of characters to skip */
582 char s, p;
584 * Simple pattern matcher: '*' matches 0 or more characters, '?' matches
585 * any single character. Returns TRUE if 'strng' matches 'patrn'.
587 pmatch_top:
588 if (!sk) {
589 s = *strng++;
590 p = *patrn++; /* get next chars and pre-advance */
591 } else {
592 /* fuzzy match variant of pmatch; particular characters are ignored */
593 do {
594 s = *strng++;
595 } while (index(sk, s));
596 do {
597 p = *patrn++;
598 } while (index(sk, p));
600 if (!p) /* end of pattern */
601 return (boolean) (s == '\0'); /* matches iff end of string too */
602 else if (p == '*') /* wildcard reached */
603 return (boolean) ((!*patrn
604 || pmatch_internal(patrn, strng - 1, ci, sk))
605 ? TRUE
606 : s ? pmatch_internal(patrn - 1, strng, ci, sk)
607 : FALSE);
608 else if ((ci ? lowc(p) != lowc(s) : p != s) /* check single character */
609 && (p != '?' || !s)) /* & single-char wildcard */
610 return FALSE; /* doesn't match */
611 else /* return pmatch_internal(patrn, strng, ci, sk); */
612 goto pmatch_top; /* optimize tail recursion */
615 /* case-sensitive wildcard match */
616 boolean
617 pmatch(patrn, strng)
618 const char *patrn, *strng;
620 return pmatch_internal(patrn, strng, FALSE, (const char *) 0);
623 /* case-insensitive wildcard match */
624 boolean
625 pmatchi(patrn, strng)
626 const char *patrn, *strng;
628 return pmatch_internal(patrn, strng, TRUE, (const char *) 0);
631 /* case-insensitive wildcard fuzzymatch */
632 boolean
633 pmatchz(patrn, strng)
634 const char *patrn, *strng;
636 /* ignore spaces, tabs (just in case), dashes, and underscores */
637 static const char fuzzychars[] = " \t-_";
639 return pmatch_internal(patrn, strng, TRUE, fuzzychars);
642 #ifndef STRNCMPI
643 /* case insensitive counted string comparison */
645 strncmpi(s1, s2, n) /*{ aka strncasecmp }*/
646 register const char *s1, *s2;
647 register int n; /*(should probably be size_t, which is unsigned)*/
649 register char t1, t2;
651 while (n--) {
652 if (!*s2)
653 return (*s1 != 0); /* s1 >= s2 */
654 else if (!*s1)
655 return -1; /* s1 < s2 */
656 t1 = lowc(*s1++);
657 t2 = lowc(*s2++);
658 if (t1 != t2)
659 return (t1 > t2) ? 1 : -1;
661 return 0; /* s1 == s2 */
663 #endif /* STRNCMPI */
665 #ifndef STRSTRI
666 /* case insensitive substring search */
667 char *
668 strstri(str, sub)
669 const char *str;
670 const char *sub;
672 register const char *s1, *s2;
673 register int i, k;
674 #define TABSIZ 0x20 /* 0x40 would be case-sensitive */
675 char tstr[TABSIZ], tsub[TABSIZ]; /* nibble count tables */
676 #if 0
677 assert( (TABSIZ & ~(TABSIZ-1)) == TABSIZ ); /* must be exact power of 2 */
678 assert( &lowc != 0 ); /* can't be unsafe macro */
679 #endif
681 /* special case: empty substring */
682 if (!*sub)
683 return (char *) str;
685 /* do some useful work while determining relative lengths */
686 for (i = 0; i < TABSIZ; i++)
687 tstr[i] = tsub[i] = 0; /* init */
688 for (k = 0, s1 = str; *s1; k++)
689 tstr[*s1++ & (TABSIZ - 1)]++;
690 for (s2 = sub; *s2; --k)
691 tsub[*s2++ & (TABSIZ - 1)]++;
693 /* evaluate the info we've collected */
694 if (k < 0)
695 return (char *) 0; /* sub longer than str, so can't match */
696 for (i = 0; i < TABSIZ; i++) /* does sub have more 'x's than str? */
697 if (tsub[i] > tstr[i])
698 return (char *) 0; /* match not possible */
700 /* now actually compare the substring repeatedly to parts of the string */
701 for (i = 0; i <= k; i++) {
702 s1 = &str[i];
703 s2 = sub;
704 while (lowc(*s1++) == lowc(*s2++))
705 if (!*s2)
706 return (char *) &str[i]; /* full match */
708 return (char *) 0; /* not found */
710 #endif /* STRSTRI */
712 /* compare two strings for equality, ignoring the presence of specified
713 characters (typically whitespace) and possibly ignoring case */
714 boolean
715 fuzzymatch(s1, s2, ignore_chars, caseblind)
716 const char *s1, *s2;
717 const char *ignore_chars;
718 boolean caseblind;
720 register char c1, c2;
722 do {
723 while ((c1 = *s1++) != '\0' && index(ignore_chars, c1) != 0)
724 continue;
725 while ((c2 = *s2++) != '\0' && index(ignore_chars, c2) != 0)
726 continue;
727 if (!c1 || !c2)
728 break; /* stop when end of either string is reached */
730 if (caseblind) {
731 c1 = lowc(c1);
732 c2 = lowc(c2);
734 } while (c1 == c2);
736 /* match occurs only when the end of both strings has been reached */
737 return (boolean) (!c1 && !c2);
741 * Time routines
743 * The time is used for:
744 * - seed for rand()
745 * - year on tombstone and yyyymmdd in record file
746 * - phase of the moon (various monsters react to NEW_MOON or FULL_MOON)
747 * - night and midnight (the undead are dangerous at midnight)
748 * - determination of what files are "very old"
751 /* TIME_type: type of the argument to time(); we actually use &(time_t) */
752 #if defined(BSD) && !defined(POSIX_TYPES)
753 #define TIME_type long *
754 #else
755 #define TIME_type time_t *
756 #endif
757 /* LOCALTIME_type: type of the argument to localtime() */
758 #if (defined(ULTRIX) && !(defined(ULTRIX_PROTO) || defined(NHSTDC))) \
759 || (defined(BSD) && !defined(POSIX_TYPES))
760 #define LOCALTIME_type long *
761 #else
762 #define LOCALTIME_type time_t *
763 #endif
765 #if defined(AMIGA) && !defined(AZTEC_C) && !defined(__SASC_60) \
766 && !defined(_DCC) && !defined(__GNUC__)
767 extern struct tm *FDECL(localtime, (time_t *));
768 #endif
769 STATIC_DCL struct tm *NDECL(getlt);
771 void
772 setrandom()
774 unsigned long seed = (unsigned long) getnow(); /* time((TIME_type) 0) */
776 #if defined(UNIX) || defined(VMS)
778 unsigned long pid = (unsigned long) getpid();
780 /* Quick dirty band-aid to prevent PRNG prediction */
781 if (pid) {
782 if (!(pid & 3L))
783 pid -= 1L;
784 seed *= pid;
787 #endif
789 /* the types are different enough here that sweeping the different
790 * routine names into one via #defines is even more confusing
792 #ifdef RANDOM /* srandom() from sys/share/random.c */
793 srandom((unsigned int) seed);
794 #else
795 #if defined(__APPLE__) || defined(BSD) || defined(LINUX) || defined(ULTRIX) \
796 || defined(CYGWIN32) /* system srandom() */
797 #if defined(BSD) && !defined(POSIX_TYPES) && defined(SUNOS4)
798 (void)
799 #endif
800 srandom((int) seed);
801 #else
802 #ifdef UNIX /* system srand48() */
803 srand48((long) seed);
804 #else /* poor quality system routine */
805 srand((int) seed);
806 #endif
807 #endif
808 #endif
811 time_t
812 getnow()
814 time_t datetime = 0;
816 (void) time((TIME_type) &datetime);
817 return datetime;
820 STATIC_OVL struct tm *
821 getlt()
823 time_t date = getnow();
825 return localtime((LOCALTIME_type) &date);
829 getyear()
831 return (1900 + getlt()->tm_year);
834 #if 0
835 /* This routine is no longer used since in 20YY it yields "1YYmmdd". */
836 char *
837 yymmdd(date)
838 time_t date;
840 Static char datestr[10];
841 struct tm *lt;
843 if (date == 0)
844 lt = getlt();
845 else
846 lt = localtime((LOCALTIME_type) &date);
848 Sprintf(datestr, "%02d%02d%02d",
849 lt->tm_year, lt->tm_mon + 1, lt->tm_mday);
850 return datestr;
852 #endif
854 long
855 yyyymmdd(date)
856 time_t date;
858 long datenum;
859 struct tm *lt;
861 if (date == 0)
862 lt = getlt();
863 else
864 lt = localtime((LOCALTIME_type) &date);
866 /* just in case somebody's localtime supplies (year % 100)
867 rather than the expected (year - 1900) */
868 if (lt->tm_year < 70)
869 datenum = (long) lt->tm_year + 2000L;
870 else
871 datenum = (long) lt->tm_year + 1900L;
872 /* yyyy --> yyyymm */
873 datenum = datenum * 100L + (long) (lt->tm_mon + 1);
874 /* yyyymm --> yyyymmdd */
875 datenum = datenum * 100L + (long) lt->tm_mday;
876 return datenum;
879 long
880 hhmmss(date)
881 time_t date;
883 long timenum;
884 struct tm *lt;
886 if (date == 0)
887 lt = getlt();
888 else
889 lt = localtime((LOCALTIME_type) &date);
891 timenum = lt->tm_hour * 10000L + lt->tm_min * 100L + lt->tm_sec;
892 return timenum;
895 char *
896 yyyymmddhhmmss(date)
897 time_t date;
899 long datenum;
900 static char datestr[15];
901 struct tm *lt;
903 if (date == 0)
904 lt = getlt();
905 else
906 #if (defined(ULTRIX) && !(defined(ULTRIX_PROTO) || defined(NHSTDC))) \
907 || defined(BSD)
908 lt = localtime((long *) (&date));
909 #else
910 lt = localtime(&date);
911 #endif
912 /* just in case somebody's localtime supplies (year % 100)
913 rather than the expected (year - 1900) */
914 if (lt->tm_year < 70)
915 datenum = (long) lt->tm_year + 2000L;
916 else
917 datenum = (long) lt->tm_year + 1900L;
918 Sprintf(datestr, "%04ld%02d%02d%02d%02d%02d", datenum, lt->tm_mon + 1,
919 lt->tm_mday, lt->tm_hour, lt->tm_min, lt->tm_sec);
920 debugpline1("yyyymmddhhmmss() produced date string %s", datestr);
921 return datestr;
924 time_t
925 time_from_yyyymmddhhmmss(buf)
926 char *buf;
928 int k;
929 time_t timeresult = (time_t) 0;
930 struct tm t, *lt;
931 char *g, *p, y[5], mo[3], md[3], h[3], mi[3], s[3];
933 if (buf && strlen(buf) == 14) {
934 g = buf;
935 p = y; /* year */
936 for (k = 0; k < 4; ++k)
937 *p++ = *g++;
938 *p = '\0';
939 p = mo; /* month */
940 for (k = 0; k < 2; ++k)
941 *p++ = *g++;
942 *p = '\0';
943 p = md; /* day */
944 for (k = 0; k < 2; ++k)
945 *p++ = *g++;
946 *p = '\0';
947 p = h; /* hour */
948 for (k = 0; k < 2; ++k)
949 *p++ = *g++;
950 *p = '\0';
951 p = mi; /* minutes */
952 for (k = 0; k < 2; ++k)
953 *p++ = *g++;
954 *p = '\0';
955 p = s; /* seconds */
956 for (k = 0; k < 2; ++k)
957 *p++ = *g++;
958 *p = '\0';
959 lt = getlt();
960 if (lt) {
961 t = *lt;
962 t.tm_year = atoi(y) - 1900;
963 t.tm_mon = atoi(mo) - 1;
964 t.tm_mday = atoi(md);
965 t.tm_hour = atoi(h);
966 t.tm_min = atoi(mi);
967 t.tm_sec = atoi(s);
968 timeresult = mktime(&t);
970 if ((int) timeresult == -1)
971 debugpline1("time_from_yyyymmddhhmmss(%s) would have returned -1",
972 buf ? buf : "");
973 else
974 return timeresult;
976 return (time_t) 0;
980 * moon period = 29.53058 days ~= 30, year = 365.2422 days
981 * days moon phase advances on first day of year compared to preceding year
982 * = 365.2422 - 12*29.53058 ~= 11
983 * years in Metonic cycle (time until same phases fall on the same days of
984 * the month) = 18.6 ~= 19
985 * moon phase on first day of year (epact) ~= (11*(year%19) + 29) % 30
986 * (29 as initial condition)
987 * current phase in days = first day phase + days elapsed in year
988 * 6 moons ~= 177 days
989 * 177 ~= 8 reported phases * 22
990 * + 11/22 for rounding
993 phase_of_the_moon() /* 0-7, with 0: new, 4: full */
995 register struct tm *lt = getlt();
996 register int epact, diy, goldn;
998 diy = lt->tm_yday;
999 goldn = (lt->tm_year % 19) + 1;
1000 epact = (11 * goldn + 18) % 30;
1001 if ((epact == 25 && goldn > 11) || epact == 24)
1002 epact++;
1004 return ((((((diy + epact) * 6) + 11) % 177) / 22) & 7);
1007 boolean
1008 friday_13th()
1010 register struct tm *lt = getlt();
1012 /* tm_wday (day of week; 0==Sunday) == 5 => Friday */
1013 return (boolean) (lt->tm_wday == 5 && lt->tm_mday == 13);
1017 night()
1019 register int hour = getlt()->tm_hour;
1021 return (hour < 6 || hour > 21);
1025 midnight()
1027 return (getlt()->tm_hour == 0);
1030 /*hacklib.c*/