1 /* NetHack 3.6 hacklib.c $NHDT-Date: 1552639487 2019/03/15 08:44:47 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.67 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /*-Copyright (c) Michael Allison, 2007. */
4 /* Copyright (c) Robert Patrick Rankin, 1991 */
5 /* NetHack may be freely redistributed. See license for details. */
7 /* JNetHack Copyright */
8 /* (c) Issei Numata, Naoki Hamada, Shigehiro Miyashita, 1994-2000 */
9 /* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2019 */
10 /* JNetHack may be freely redistributed. See license for details. */
12 #include "hack.h" /* for config.h+extern.h */
14 Assorted 'small' utility routines. They're virtually independent of
15 NetHack, except that rounddiv may call panic(). setrandom calls one
16 of srandom(), srand48(), or srand() depending upon configuration.
18 return type routine name argument type(s)
25 char * upstart (char *)
26 char * mungspaces (char *)
27 char * trimspaces (char *)
28 char * strip_newline (char *)
29 char * stripchars (char *, const char *, const char *)
31 boolean str_end_is (const char *, const char *)
32 char * strkitten (char *,char)
33 void copynchars (char *,const char *,int)
34 char chrcasecpy (int,int)
35 char * strcasecpy (char *,const char *)
36 char * s_suffix (const char *)
37 char * ing_suffix (const char *)
38 char * xcrypt (const char *, char *)
39 boolean onlyspace (const char *)
40 char * tabexpand (char *)
42 char * strsubst (char *, const char *, const char *)
43 int strNsubst (char *,const char *,const char *,int)
44 const char * ordin (int)
47 int rounddiv (long, int)
48 int distmin (int, int, int, int)
49 int dist2 (int, int, int, int)
50 boolean online2 (int, int)
51 boolean pmatch (const char *, const char *)
52 boolean pmatchi (const char *, const char *)
53 boolean pmatchz (const char *, const char *)
54 int strncmpi (const char *, const char *, int)
55 char * strstri (const char *, const char *)
56 boolean fuzzymatch (const char *, const char *,
57 const char *, boolean)
60 void reseed_random (fn)
63 char * yymmdd (time_t)
64 long yyyymmdd (time_t)
66 char * yyyymmddhhmmss (time_t)
67 time_t time_from_yyyymmddhhmmss (char *)
68 int phase_of_the_moon (void)
69 boolean friday_13th (void)
72 void strbuf_init (strbuf *, const char *)
73 void strbuf_append (strbuf *, const char *)
74 void strbuf_reserve (strbuf *, int)
75 void strbuf_empty (strbuf *)
76 void strbuf_nl_to_crlf (strbuf_t *)
79 #define Static /* pacify lint */
84 static boolean FDECL(pmatch_internal, (const char *, const char *,
85 BOOLEAN_P, const char *));
92 return (boolean) ('0' <= c && c <= '9');
95 /* is 'c' a letter? note: '@' classed as letter */
100 return (boolean) ('@' <= c && c <= 'Z') || ('a' <= c && c <= 'z');
103 /* force 'c' into uppercase */
108 return (char) (('a' <= c && c <= 'z') ? (c & ~040) : c);
111 /* force 'c' into lowercase */
116 return (char) (('A' <= c && c <= 'Z') ? (c | 040) : c);
119 /* convert a string into all lowercase */
127 #if 1 /*JP*//*
\8a¿
\8e\9a\82Í
\8f¬
\95¶
\8e\9a\89»
\82µ
\82È
\82¢*/
128 if (is_kanji(*(unsigned char *)p)) p++; else
130 if ('A' <= *p && *p <= 'Z')
135 /* convert a string into all uppercase */
143 #if 1 /*JP*//*
\8a¿
\8e\9a\82Í
\91å
\95¶
\8e\9a\89»
\82µ
\82È
\82¢*/
144 if (is_kanji(*(unsigned char *)p)) p++; else
146 if ('a' <= *p && *p <= 'z')
151 /* convert first character of a string to uppercase */
161 /* remove excess whitespace from a string buffer (in place) */
166 register char c, *p, *p2;
167 boolean was_space = TRUE;
169 for (p = p2 = bp; (c = *p) != '\0'; p++) {
171 break; /* treat newline the same as end-of-string */
174 if (c != ' ' || !was_space)
176 was_space = (c == ' ');
178 if (was_space && p2 > bp)
184 /* skip leading whitespace; remove trailing whitespace, in place */
191 /* leading whitespace will remain in the buffer */
192 while (*txt == ' ' || *txt == '\t')
195 while (--end >= txt && (*end == ' ' || *end == '\t'))
201 /* remove \n from end of line; remove \r too if one is there */
206 char *p = rindex(str, '\n');
209 if (p > str && *(p - 1) == '\r')
216 /* return the end of a string (pointing at '\0') */
222 s++; /* s += strlen(s); */
226 /* determine whether 'str' ends in 'chkstr' */
228 str_end_is(str, chkstr)
229 const char *str, *chkstr;
231 int clen = (int) strlen(chkstr);
233 if ((int) strlen(str) >= clen)
234 return (boolean) (!strncmp(eos((char *) str) - clen, chkstr, clen));
238 /* append a character to a string (in place): strcat(s, {c,'\0'}); */
251 /* truncating string copy */
253 copynchars(dst, src, n)
258 /* copies at most n characters, stopping sooner if terminator reached;
259 treats newline as input terminator; unlike strncpy, always supplies
260 '\0' terminator so dst must be able to hold at least n+1 characters */
261 while (n > 0 && *src != '\0' && *src != '\n') {
268 /* convert char nc into oc's case; mostly used by strcasecpy */
273 #if 0 /* this will be necessary if we switch to <ctype.h> */
274 oc = (int) (unsigned char) oc;
275 nc = (int) (unsigned char) nc;
277 if ('a' <= oc && oc <= 'z') {
278 /* old char is lower case; if new char is upper case, downcase it */
279 if ('A' <= nc && nc <= 'Z')
280 nc += 'a' - 'A'; /* lowc(nc) */
281 } else if ('A' <= oc && oc <= 'Z') {
282 /* old char is upper case; if new char is lower case, upcase it */
283 if ('a' <= nc && nc <= 'z')
284 nc += 'A' - 'a'; /* highc(nc) */
289 /* overwrite string, preserving old chars' case;
290 for case-insensitive editions of makeplural() and makesingular();
291 src might be shorter, same length, or longer than dst */
298 int ic, oc, dst_exhausted = 0;
300 /* while dst has characters, replace each one with corresponding
301 character from src, converting case in the process if they differ;
302 once dst runs out, propagate the case of its last character to any
303 remaining src; if dst starts empty, it must be a pointer to the
304 tail of some other string because we examine the char at dst[-1] */
305 while ((ic = (int) *src++) != '\0') {
306 if (!dst_exhausted && !*dst)
308 oc = (int) *(dst - dst_exhausted);
309 *dst++ = chrcasecpy(oc, ic);
315 /* return a name converted to possessive */
320 Static char buf[BUFSZ];
324 if (!strcmpi(buf, "it")) /* it -> its */
326 else if (!strcmpi(buf, "you")) /* you -> your */
328 else if (*(eos(buf) - 1) == 's') /* Xs -> Xs' */
332 #else /* X -> X
\82Ì */
338 /* construct a gerund (a verb formed by appending "ing" to a noun) */
343 static const char vowel[] = "aeiouwy";
344 static char buf[BUFSZ];
350 onoff[0] = *p = *(p + 1) = '\0';
351 if ((p >= &buf[3] && !strcmpi(p - 3, " on"))
352 || (p >= &buf[4] && !strcmpi(p - 4, " off"))
353 || (p >= &buf[5] && !strcmpi(p - 5, " with"))) {
354 p = rindex(buf, ' ');
358 if (p >= &buf[3] && !index(vowel, *(p - 1))
359 && index(vowel, *(p - 2)) && !index(vowel, *(p - 3))) {
360 /* tip -> tipp + ing */
363 } else if (p >= &buf[2] && !strcmpi(p - 2, "ie")) { /* vie -> vy + ing */
366 } else if (p >= &buf[1] && *(p - 1) == 'e') /* grease -> greas + ing */
374 /* trivial text encryption routine (see makedefs) */
380 register const char *p;
382 register int bitmask;
384 for (bitmask = 1, p = str, q = buf; *p; q++) {
388 if ((bitmask <<= 1) >= 32)
395 /* is a string entirely whitespace? */
401 if (*s != ' ' && *s != '\t')
406 /* expand tabs into proper number of spaces */
412 register char *bp, *s = sbuf;
417 /* warning: no bounds checking performed */
418 for (bp = buf, idx = 0; *s; s++)
428 return strcpy(sbuf, buf);
431 #define VISCTRL_NBUF 5
432 /* make a displayable string from a character */
437 Static char visctrl_bufs[VISCTRL_NBUF][5];
440 char *ccc = visctrl_bufs[nbuf];
441 nbuf = (nbuf + 1) % VISCTRL_NBUF;
443 if ((uchar) c & 0200) {
450 ccc[i++] = c | 0100; /* letter */
451 } else if (c == 0177) {
453 ccc[i++] = c & ~0100; /* '?' */
455 ccc[i++] = c; /* printable character */
461 /* strip all the chars in stuff_to_strip from orig */
462 /* caller is responsible for ensuring that bp is a
463 valid pointer to a BUFSZ buffer */
465 stripchars(bp, stuff_to_strip, orig)
467 const char *stuff_to_strip, *orig;
473 while (*orig && i < (BUFSZ - 1)) {
474 if (!index(stuff_to_strip, *orig)) {
482 impossible("no output buf in stripchars");
486 /* substitute a word or phrase in a string (in place) */
487 /* caller is responsible for ensuring that bp points to big enough buffer */
489 strsubst(bp, orig, replacement)
491 const char *orig, *replacement;
493 char *found, buf[BUFSZ];
496 /* [this could be replaced by strNsubst(bp, orig, replacement, 1)] */
497 found = strstr(bp, orig);
499 Strcpy(buf, found + strlen(orig));
500 Strcpy(found, replacement);
507 /* substitute the Nth occurrence of a substring within a string (in place);
508 if N is 0, substitute all occurrences; returns the number of subsitutions;
509 maximum output length is BUFSZ (BUFSZ-1 chars + terminating '\0') */
511 strNsubst(inoutbuf, orig, replacement, n)
512 char *inoutbuf; /* current string, and result buffer */
513 const char *orig, /* old substring; if "" then insert in front of Nth char */
514 *replacement; /* new substring; if "" then delete old substring */
515 int n; /* which occurrence to replace; 0 => all */
517 char *bp, *op, workbuf[BUFSZ];
519 unsigned len = (unsigned) strlen(orig);
520 int ocount = 0, /* number of times 'orig' has been matched */
521 rcount = 0; /* number of subsitutions made */
523 for (bp = inoutbuf, op = workbuf; *bp && op < &workbuf[BUFSZ - 1]; ) {
524 if ((!len || !strncmp(bp, orig, len)) && (++ocount == n || n == 0)) {
525 /* Nth match found */
526 for (rp = replacement; *rp && op < &workbuf[BUFSZ - 1]; )
530 bp += len; /* skip 'orig' */
534 /* no match (or len==0) so retain current character */
537 if (!len && n == ocount + 1) {
538 /* special case: orig=="" (!len) and n==strlen(inoutbuf)+1,
539 insert in front of terminator (in other words, append);
540 [when orig=="", ocount will have been incremented once for
542 for (rp = replacement; *rp && op < &workbuf[BUFSZ - 1]; )
548 Strcpy(inoutbuf, workbuf);
553 /* return the ordinal suffix of a number */
556 int n; /* note: should be non-negative */
558 register int dd = n % 10;
560 return (dd == 0 || dd > 3 || (n % 100) / 10 == 1) ? "th"
561 : (dd == 1) ? "st" : (dd == 2) ? "nd" : "rd";
564 /* make a signed digit string from a number */
571 Sprintf(buf, (n < 0) ? "%d" : "+%d", n);
575 /* return the sign of a number: -1, 0, or 1 */
580 return (n < 0) ? -1 : (n != 0);
583 /* calculate x/y, rounding as appropriate */
593 panic("division by zero in rounddiv");
610 /* distance between two points, in moves */
612 distmin(x0, y0, x1, y1)
615 register int dx = x0 - x1, dy = y0 - y1;
621 /* The minimum number of moves to get from (x0,y0) to (x1,y1) is the
622 * larger of the [absolute value of the] two deltas.
624 return (dx < dy) ? dy : dx;
627 /* square of euclidean distance between pair of pts */
629 dist2(x0, y0, x1, y1)
632 register int dx = x0 - x1, dy = y0 - y1;
634 return dx * dx + dy * dy;
637 /* integer square root function without using floating point */
645 * This could be replaced by a faster algorithm, but has not been because:
646 * + the simple algorithm is easy to read;
647 * + this algorithm does not require 64-bit support;
648 * + in current usage, the values passed to isqrt() are not really that
649 * large, so the performance difference is negligible;
650 * + isqrt() is used in only few places, which are not bottle-necks.
660 /* are two points lined up (on a straight line)? */
662 online2(x0, y0, x1, y1)
665 int dx = x0 - x1, dy = y0 - y1;
666 /* If either delta is zero then they're on an orthogonal line,
667 * else if the deltas are equal (signs ignored) they're on a diagonal.
669 return (boolean) (!dy || !dx || dy == dx || dy == -dx);
672 /* guts of pmatch(), pmatchi(), and pmatchz();
673 match a string against a pattern */
675 pmatch_internal(patrn, strng, ci, sk)
676 const char *patrn, *strng;
677 boolean ci; /* True => case-insensitive, False => case-sensitive */
678 const char *sk; /* set of characters to skip */
682 * Simple pattern matcher: '*' matches 0 or more characters, '?' matches
683 * any single character. Returns TRUE if 'strng' matches 'patrn'.
688 p = *patrn++; /* get next chars and pre-advance */
690 /* fuzzy match variant of pmatch; particular characters are ignored */
693 } while (index(sk, s));
696 } while (index(sk, p));
698 if (!p) /* end of pattern */
699 return (boolean) (s == '\0'); /* matches iff end of string too */
700 else if (p == '*') /* wildcard reached */
701 return (boolean) ((!*patrn
702 || pmatch_internal(patrn, strng - 1, ci, sk))
704 : s ? pmatch_internal(patrn - 1, strng, ci, sk)
706 else if ((ci ? lowc(p) != lowc(s) : p != s) /* check single character */
707 && (p != '?' || !s)) /* & single-char wildcard */
708 return FALSE; /* doesn't match */
709 else /* return pmatch_internal(patrn, strng, ci, sk); */
710 goto pmatch_top; /* optimize tail recursion */
713 /* case-sensitive wildcard match */
716 const char *patrn, *strng;
718 return pmatch_internal(patrn, strng, FALSE, (const char *) 0);
721 /* case-insensitive wildcard match */
723 pmatchi(patrn, strng)
724 const char *patrn, *strng;
726 return pmatch_internal(patrn, strng, TRUE, (const char *) 0);
729 /* case-insensitive wildcard fuzzymatch */
731 pmatchz(patrn, strng)
732 const char *patrn, *strng;
734 /* ignore spaces, tabs (just in case), dashes, and underscores */
735 static const char fuzzychars[] = " \t-_";
737 return pmatch_internal(patrn, strng, TRUE, fuzzychars);
741 /* case insensitive counted string comparison */
743 strncmpi(s1, s2, n) /*{ aka strncasecmp }*/
744 register const char *s1, *s2;
745 register int n; /*(should probably be size_t, which is unsigned)*/
747 register char t1, t2;
751 return (*s1 != 0); /* s1 >= s2 */
753 return -1; /* s1 < s2 */
757 return (t1 > t2) ? 1 : -1;
759 return 0; /* s1 == s2 */
761 #endif /* STRNCMPI */
764 /* case insensitive substring search */
770 register const char *s1, *s2;
772 #define TABSIZ 0x20 /* 0x40 would be case-sensitive */
773 char tstr[TABSIZ], tsub[TABSIZ]; /* nibble count tables */
775 assert( (TABSIZ & ~(TABSIZ-1)) == TABSIZ ); /* must be exact power of 2 */
776 assert( &lowc != 0 ); /* can't be unsafe macro */
779 /* special case: empty substring */
783 /* do some useful work while determining relative lengths */
784 for (i = 0; i < TABSIZ; i++)
785 tstr[i] = tsub[i] = 0; /* init */
786 for (k = 0, s1 = str; *s1; k++)
787 tstr[*s1++ & (TABSIZ - 1)]++;
788 for (s2 = sub; *s2; --k)
789 tsub[*s2++ & (TABSIZ - 1)]++;
791 /* evaluate the info we've collected */
793 return (char *) 0; /* sub longer than str, so can't match */
794 for (i = 0; i < TABSIZ; i++) /* does sub have more 'x's than str? */
795 if (tsub[i] > tstr[i])
796 return (char *) 0; /* match not possible */
798 /* now actually compare the substring repeatedly to parts of the string */
799 for (i = 0; i <= k; i++) {
802 while (lowc(*s1++) == lowc(*s2++))
804 return (char *) &str[i]; /* full match */
806 return (char *) 0; /* not found */
810 /* compare two strings for equality, ignoring the presence of specified
811 characters (typically whitespace) and possibly ignoring case */
813 fuzzymatch(s1, s2, ignore_chars, caseblind)
815 const char *ignore_chars;
818 register char c1, c2;
821 while ((c1 = *s1++) != '\0' && index(ignore_chars, c1) != 0)
823 while ((c2 = *s2++) != '\0' && index(ignore_chars, c2) != 0)
826 break; /* stop when end of either string is reached */
834 /* match occurs only when the end of both strings has been reached */
835 return (boolean) (!c1 && !c2);
841 * The time is used for:
843 * - year on tombstone and yyyymmdd in record file
844 * - phase of the moon (various monsters react to NEW_MOON or FULL_MOON)
845 * - night and midnight (the undead are dangerous at midnight)
846 * - determination of what files are "very old"
849 /* TIME_type: type of the argument to time(); we actually use &(time_t) */
850 #if defined(BSD) && !defined(POSIX_TYPES)
851 #define TIME_type long *
853 #define TIME_type time_t *
855 /* LOCALTIME_type: type of the argument to localtime() */
856 #if (defined(ULTRIX) && !(defined(ULTRIX_PROTO) || defined(NHSTDC))) \
857 || (defined(BSD) && !defined(POSIX_TYPES))
858 #define LOCALTIME_type long *
860 #define LOCALTIME_type time_t *
863 #if defined(AMIGA) && !defined(AZTEC_C) && !defined(__SASC_60) \
864 && !defined(_DCC) && !defined(__GNUC__)
865 extern struct tm *FDECL(localtime, (time_t *));
867 STATIC_DCL struct tm *NDECL(getlt);
869 /* Sets the seed for the random number generator */
875 int FDECL((*fn), (int));
877 init_isaac64(seed, fn);
880 #else /* USE_ISAAC64 */
886 int FDECL((*fn), (int)) UNUSED;
888 /* the types are different enough here that sweeping the different
889 * routine names into one via #defines is even more confusing
891 # ifdef RANDOM /* srandom() from sys/share/random.c */
892 srandom((unsigned int) seed);
894 # if defined(__APPLE__) || defined(BSD) || defined(LINUX) || defined(ULTRIX) \
895 || defined(CYGWIN32) /* system srandom() */
896 # if defined(BSD) && !defined(POSIX_TYPES) && defined(SUNOS4)
901 # ifdef UNIX /* system srand48() */
902 srand48((long) seed);
903 # else /* poor quality system routine */
910 #endif /* USE_ISAAC64 */
912 /* An appropriate version of this must always be provided in
913 port-specific code somewhere. It returns a number suitable
914 as seed for the random number generator */
915 extern unsigned long NDECL(sys_random_seed);
918 * Initializes the random number generator.
923 int FDECL((*fn), (int));
925 set_random(sys_random_seed(), fn);
928 /* Reshuffles the random number generator. */
931 int FDECL((*fn), (int));
933 /* only reseed if we are certain that the seed generation is unguessable
935 if (has_strong_rngseed)
944 (void) time((TIME_type) &datetime);
948 STATIC_OVL struct tm *
951 time_t date = getnow();
953 return localtime((LOCALTIME_type) &date);
959 return (1900 + getlt()->tm_year);
963 /* This routine is no longer used since in 20YY it yields "1YYmmdd". */
968 Static char datestr[10];
974 lt = localtime((LOCALTIME_type) &date);
976 Sprintf(datestr, "%02d%02d%02d",
977 lt->tm_year, lt->tm_mon + 1, lt->tm_mday);
992 lt = localtime((LOCALTIME_type) &date);
994 /* just in case somebody's localtime supplies (year % 100)
995 rather than the expected (year - 1900) */
996 if (lt->tm_year < 70)
997 datenum = (long) lt->tm_year + 2000L;
999 datenum = (long) lt->tm_year + 1900L;
1000 /* yyyy --> yyyymm */
1001 datenum = datenum * 100L + (long) (lt->tm_mon + 1);
1002 /* yyyymm --> yyyymmdd */
1003 datenum = datenum * 100L + (long) lt->tm_mday;
1017 lt = localtime((LOCALTIME_type) &date);
1019 timenum = lt->tm_hour * 10000L + lt->tm_min * 100L + lt->tm_sec;
1024 yyyymmddhhmmss(date)
1028 static char datestr[15];
1034 #if (defined(ULTRIX) && !(defined(ULTRIX_PROTO) || defined(NHSTDC))) \
1036 lt = localtime((long *) (&date));
1038 lt = localtime(&date);
1040 /* just in case somebody's localtime supplies (year % 100)
1041 rather than the expected (year - 1900) */
1042 if (lt->tm_year < 70)
1043 datenum = (long) lt->tm_year + 2000L;
1045 datenum = (long) lt->tm_year + 1900L;
1046 Sprintf(datestr, "%04ld%02d%02d%02d%02d%02d", datenum, lt->tm_mon + 1,
1047 lt->tm_mday, lt->tm_hour, lt->tm_min, lt->tm_sec);
1048 debugpline1("yyyymmddhhmmss() produced date string %s", datestr);
1053 time_from_yyyymmddhhmmss(buf)
1057 time_t timeresult = (time_t) 0;
1059 char *g, *p, y[5], mo[3], md[3], h[3], mi[3], s[3];
1061 if (buf && strlen(buf) == 14) {
1064 for (k = 0; k < 4; ++k)
1068 for (k = 0; k < 2; ++k)
1072 for (k = 0; k < 2; ++k)
1076 for (k = 0; k < 2; ++k)
1079 p = mi; /* minutes */
1080 for (k = 0; k < 2; ++k)
1083 p = s; /* seconds */
1084 for (k = 0; k < 2; ++k)
1090 t.tm_year = atoi(y) - 1900;
1091 t.tm_mon = atoi(mo) - 1;
1092 t.tm_mday = atoi(md);
1093 t.tm_hour = atoi(h);
1094 t.tm_min = atoi(mi);
1096 timeresult = mktime(&t);
1098 if ((int) timeresult == -1)
1099 debugpline1("time_from_yyyymmddhhmmss(%s) would have returned -1",
1108 * moon period = 29.53058 days ~= 30, year = 365.2422 days
1109 * days moon phase advances on first day of year compared to preceding year
1110 * = 365.2422 - 12*29.53058 ~= 11
1111 * years in Metonic cycle (time until same phases fall on the same days of
1112 * the month) = 18.6 ~= 19
1113 * moon phase on first day of year (epact) ~= (11*(year%19) + 29) % 30
1114 * (29 as initial condition)
1115 * current phase in days = first day phase + days elapsed in year
1116 * 6 moons ~= 177 days
1117 * 177 ~= 8 reported phases * 22
1118 * + 11/22 for rounding
1121 phase_of_the_moon() /* 0-7, with 0: new, 4: full */
1123 register struct tm *lt = getlt();
1124 register int epact, diy, goldn;
1127 goldn = (lt->tm_year % 19) + 1;
1128 epact = (11 * goldn + 18) % 30;
1129 if ((epact == 25 && goldn > 11) || epact == 24)
1132 return ((((((diy + epact) * 6) + 11) % 177) / 22) & 7);
1138 register struct tm *lt = getlt();
1140 /* tm_wday (day of week; 0==Sunday) == 5 => Friday */
1141 return (boolean) (lt->tm_wday == 5 && lt->tm_mday == 13);
1147 register int hour = getlt()->tm_hour;
1149 return (hour < 6 || hour > 21);
1155 return (getlt()->tm_hour == 0);
1158 /* strbuf_init() initializes strbuf state for use */
1167 /* strbuf_append() appends given str to strbuf->str */
1169 strbuf_append(strbuf, str)
1173 int len = (int) strlen(str) + 1;
1175 strbuf_reserve(strbuf,
1176 len + (strbuf->str ? (int) strlen(strbuf->str) : 0));
1177 Strcat(strbuf->str, str);
1180 /* strbuf_reserve() ensure strbuf->str has storage for len characters */
1182 strbuf_reserve(strbuf, len)
1186 if (strbuf->str == NULL) {
1187 strbuf->str = strbuf->buf;
1188 strbuf->str[0] = '\0';
1189 strbuf->len = (int) sizeof strbuf->buf;
1192 if (len > strbuf->len) {
1193 char *oldbuf = strbuf->str;
1195 strbuf->len = len + (int) sizeof strbuf->buf;
1196 strbuf->str = (char *) alloc(strbuf->len);
1197 Strcpy(strbuf->str, oldbuf);
1198 if (oldbuf != strbuf->buf)
1199 free((genericptr_t) oldbuf);
1203 /* strbuf_empty() frees allocated memory and set strbuf to initial state */
1205 strbuf_empty(strbuf)
1208 if (strbuf->str != NULL && strbuf->str != strbuf->buf)
1209 free((genericptr_t) strbuf->str);
1210 strbuf_init(strbuf);
1213 /* strbuf_nl_to_crlf() converts all occurences of \n to \r\n */
1215 strbuf_nl_to_crlf(strbuf)
1219 int len = (int) strlen(strbuf->str);
1221 char *cp = strbuf->str;
1227 strbuf_reserve(strbuf, len + count + 1);
1228 for (cp = strbuf->str + len + count; count; --cp)
1229 if ((*cp = cp[-count]) == '\n') {