/* stringutil.c - string utilities */ /* $Id: stringutil.c,v 1.53 2003/02/24 18:05:23 rmg Exp $ */ #include "copyright.h" #include "autoconf.h" #include "config.h" #include "alloc.h" /* required by mudconf */ #include "flags.h" /* required by mudconf */ #include "htab.h" /* required by mudconf */ #include "mudconf.h" /* required by code */ #include "db.h" /* required by externs */ #include "externs.h" /* required by code */ #include "ansi.h" /* required by code */ /* Provide strtok_r (reentrant strtok) if needed */ #ifndef HAVE_STRTOK_R char *strtok_r(s, sep, last) char *s; const char *sep; char **last; { if (!s) s = *last; #ifdef HAVE_STRCSPN s += strspn(s, sep); *last = s + strcspn(s, sep); #else /* HAVE_STRCSPN */ while (strchr(sep, *s) && *s) s++; *last = s; while (!strchr(sep, **last) && **last) (*last)++; #endif /* HAVE_STRCSPN */ if (s == *last) return NULL; if (**last) *((*last)++) = '\0'; return s; } #endif /* HAVE_STRTOK_R */ /* --------------------------------------------------------------------------- * ANSI character-to-number translation table. */ int ansi_nchartab[256] = { 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0, 0, I_ANSI_BBLUE, I_ANSI_BCYAN, 0, 0, 0, I_ANSI_BGREEN, 0, 0, 0, 0, 0, I_ANSI_BMAGENTA, 0, 0, 0, 0, I_ANSI_BRED, 0, 0, 0, 0, I_ANSI_BWHITE, I_ANSI_BBLACK, I_ANSI_BYELLOW, 0, 0, 0, 0, 0, 0, 0, 0, I_ANSI_BLUE, I_ANSI_CYAN, 0, 0, I_ANSI_BLINK, I_ANSI_GREEN, I_ANSI_HILITE, I_ANSI_INVERSE, 0, 0, 0, I_ANSI_MAGENTA, I_ANSI_NORMAL, 0, 0, 0, I_ANSI_RED, 0, 0, I_ANSI_UNDER, 0, I_ANSI_WHITE, I_ANSI_BLACK, I_ANSI_YELLOW, 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0 }; /* --------------------------------------------------------------------------- * ANSI number-to-character translation table. */ char ansi_lettab[I_ANSI_NUM] = { '\0', 'h', '\0', '\0', 'u', 'f', '\0', 'i', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', 'x', 'r', 'g', 'y', 'b', 'm', 'c', 'w', '\0', '\0', 'X', 'R', 'G', 'Y', 'B', 'M', 'C', 'W' }; /* --------------------------------------------------------------------------- * ANSI packed state definitions -- number-to-bitmask translation table. * * The mask specifies the state bits that are altered by a particular ansi * code. Bits are laid out as follows: * * 0x1000 -- No ansi. Every valid ansi code clears this bit. * 0x0800 -- inverse * 0x0400 -- flash * 0x0200 -- underline * 0x0100 -- highlight * 0x0080 -- "use default bg", set by ansi normal, cleared by other bg's * 0x0070 -- three bits of bg color * 0x0008 -- "use default fg", set by ansi normal, cleared by other fg's * 0x0007 -- three bits of fg color */ int ansi_mask_bits[I_ANSI_LIM] = { 0x1fff, 0x1100, 0x1100, 0, 0x1200, 0x1400, 0, 0x1800, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1100, 0x1100, 0, 0x1200, 0x1400, 0, 0x1800, 0, 0, 0x100f, 0x100f, 0x100f, 0x100f, 0x100f, 0x100f, 0x100f, 0x100f, 0, 0, 0x10f0, 0x10f0, 0x10f0, 0x10f0, 0x10f0, 0x10f0, 0x10f0, 0x10f0, 0, 0 }; /* --------------------------------------------------------------------------- * ANSI packed state definitions -- number-to-bitvalue translation table. */ int ansi_bits[I_ANSI_LIM] = { 0x0099, 0x0100, 0x0000, 0, 0x0200, 0x0400, 0, 0x0800, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0000, 0x0000, 0, 0x0000, 0x0000, 0, 0x0000, 0, 0, 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0, 0, 0x0000, 0x0010, 0x0020, 0x0030, 0x0040, 0x0050, 0x0060, 0x0070, 0, 0 }; /* --------------------------------------------------------------------------- * strip_ansi -- return a new string with escape codes removed */ char *strip_ansi(s) const char *s; { static char buf[LBUF_SIZE]; char *p = buf; if (s) { while (*s == ESC_CHAR) { skip_esccode(s); } while (*s) { *p++ = *s++; while (*s == ESC_CHAR) { skip_esccode(s); } } } *p = '\0'; return buf; } /* --------------------------------------------------------------------------- * strip_ansi_len -- count non-escape-code characters */ int strip_ansi_len(s) const char *s; { int n = 0; if (s) { while (*s == ESC_CHAR) { skip_esccode(s); } while (*s) { ++s, ++n; while (*s == ESC_CHAR) { skip_esccode(s); } } } return n; } /* normal_to_white -- This function implements the NOBLEED flag */ char *normal_to_white(raw) const char *raw; { static char buf[LBUF_SIZE]; char *p = (char *)raw; char *q = buf; char *just_after_csi; char *just_after_esccode = p; unsigned int param_val; int has_zero; while (p && *p) { if (*p == ESC_CHAR) { safe_known_str(just_after_esccode, p - just_after_esccode, buf, &q); if (p[1] == ANSI_CSI) { safe_chr(*p, buf, &q); ++p; safe_chr(*p, buf, &q); ++p; just_after_csi = p; has_zero = 0; while ((*p & 0xf0) == 0x30) { if (*p == '0') has_zero = 1; ++p; } while ((*p & 0xf0) == 0x20) { ++p; } if (*p == ANSI_END && has_zero) { /* it really was an ansi code, * go back and fix up the zero */ p = just_after_csi; param_val = 0; while ((*p & 0xf0) == 0x30) { if (*p < 0x3a) { param_val <<= 1; param_val += (param_val << 2) + (*p & 0x0f); safe_chr(*p, buf, &q); } else { if (param_val == 0) { /* ansi normal */ safe_known_str("m\033[37m\033[", 8, buf, &q); } else { /* some other color */ safe_chr(*p, buf, &q); } param_val = 0; } ++p; } while ((*p & 0xf0) == 0x20) { ++p; } safe_chr(*p, buf, &q); ++p; if (param_val == 0) { safe_known_str(ANSI_WHITE, 5, buf, &q); } } else { ++p; safe_known_str(just_after_csi, p - just_after_csi, buf, &q); } } else { safe_copy_esccode(p, buf, &q); } just_after_esccode = p; } else { ++p; } } safe_known_str(just_after_esccode, p - just_after_esccode, buf, &q); return buf; } char *ansi_transition_esccode(ansi_before, ansi_after) int ansi_before, ansi_after; { int ansi_bits_set, ansi_bits_clr; char *p; static char buffer[64]; if (ansi_before == ansi_after) return ""; buffer[0] = ESC_CHAR; buffer[1] = ANSI_CSI; p = buffer + 2; /* If they turn off any highlight bits, or they change from some color * to default color, we need to use ansi normal first. */ ansi_bits_set = (~ansi_before) & ansi_after; ansi_bits_clr = ansi_before & (~ansi_after); if ((ansi_bits_clr & 0xf00) || /* highlights off */ (ansi_bits_set & 0x088) || /* normal to color */ (ansi_bits_clr == 0x1000)) { /* explicit normal */ strcpy(p, "0;"); p += 2; ansi_bits_set = (~ansi_bits[0]) & ansi_after; ansi_bits_clr = ansi_bits[0] & (~ansi_after); } /* Next reproduce the highlight state */ if (ansi_bits_set & 0x100) { strcpy(p, "1;"); p += 2; } if (ansi_bits_set & 0x200) { strcpy(p, "4;"); p += 2; } if (ansi_bits_set & 0x400) { strcpy(p, "5;"); p += 2; } if (ansi_bits_set & 0x800) { strcpy(p, "7;"); p += 2; } /* Foreground color */ if ((ansi_bits_set | ansi_bits_clr) & 0x00f) { strcpy(p, "30;"); p += 3; p[-2] |= (ansi_after & 0x00f); } /* Background color */ if ((ansi_bits_set | ansi_bits_clr) & 0x0f0) { strcpy(p, "40;"); p += 3; p[-2] |= ((ansi_after & 0x0f0) >> 4); } /* Terminate */ if (p > buffer + 2) { p[-1] = ANSI_END; /* Buffer is already null-terminated by strcpy */ } else { buffer[0] = '\0'; } return buffer; } char *ansi_transition_mushcode(ansi_before, ansi_after) int ansi_before, ansi_after; { int ansi_bits_set, ansi_bits_clr; char *p; static char ansi_mushcode_fg[9] = "xrgybmcw"; static char ansi_mushcode_bg[9] = "XRGYBMCW"; static char buffer[64]; if (ansi_before == ansi_after) return ""; p = buffer; /* If they turn off any highlight bits, or they change from some color * to default color, we need to use ansi normal first. */ ansi_bits_set = (~ansi_before) & ansi_after; ansi_bits_clr = ansi_before & (~ansi_after); if ((ansi_bits_clr & 0xf00) || /* highlights off */ (ansi_bits_set & 0x088) || /* normal to color */ (ansi_bits_clr == 0x1000)) { /* explicit normal */ strcpy(p, "%xn"); p += 3; ansi_bits_set = (~ansi_bits[0]) & ansi_after; ansi_bits_clr = ansi_bits[0] & (~ansi_after); } /* Next reproduce the highlight state */ if (ansi_bits_set & 0x100) { strcpy(p, "%xh"); p += 3; } if (ansi_bits_set & 0x200) { strcpy(p, "%xu"); p += 3; } if (ansi_bits_set & 0x400) { strcpy(p, "%xf"); p += 3; } if (ansi_bits_set & 0x800) { strcpy(p, "%xi"); p += 3; } /* Foreground color */ if ((ansi_bits_set | ansi_bits_clr) & 0x00f) { strcpy(p, "%xx"); p += 3; p[-1] = ansi_mushcode_fg[(ansi_after & 0x00f)]; } /* Background color */ if ((ansi_bits_set | ansi_bits_clr) & 0x0f0) { strcpy(p, "%xX"); p += 3; p[-1] = ansi_mushcode_bg[(ansi_after & 0x0f0) >> 4]; } /* Terminate */ *p = '\0'; return buffer; } char *ansi_transition_letters(ansi_before, ansi_after) int ansi_before, ansi_after; { int ansi_bits_set, ansi_bits_clr; char *p; static char ansi_mushcode_fg[9] = "xrgybmcw"; static char ansi_mushcode_bg[9] = "XRGYBMCW"; static char buffer[64]; if (ansi_before == ansi_after) return ""; p = buffer; /* If they turn off any highlight bits, or they change from some color * to default color, we need to use ansi normal first. */ ansi_bits_set = (~ansi_before) & ansi_after; ansi_bits_clr = ansi_before & (~ansi_after); if ((ansi_bits_clr & 0xf00) || /* highlights off */ (ansi_bits_set & 0x088) || /* normal to color */ (ansi_bits_clr == 0x1000)) { /* explicit normal */ *p++ = 'n'; ansi_bits_set = (~ansi_bits[0]) & ansi_after; ansi_bits_clr = ansi_bits[0] & (~ansi_after); } /* Next reproduce the highlight state */ if (ansi_bits_set & 0x100) { *p++ = 'h'; } if (ansi_bits_set & 0x200) { *p++ = 'u'; } if (ansi_bits_set & 0x400) { *p++ = 'f'; } if (ansi_bits_set & 0x800) { *p++ = 'i'; } /* Foreground color */ if ((ansi_bits_set | ansi_bits_clr) & 0x00f) { *p++ = ansi_mushcode_fg[(ansi_after & 0x00f)]; } /* Background color */ if ((ansi_bits_set | ansi_bits_clr) & 0x0f0) { *p++ = ansi_mushcode_bg[(ansi_after & 0x0f0) >> 4]; } /* Terminate */ *p = '\0'; return buffer; } /* ansi_map_states -- Identify ansi state of every character in a string */ int ansi_map_states(s, m) const char *s; int **m; { static int ansi_map[LBUF_SIZE + 1]; int n, ansi_state; n = 0; ansi_state = ANST_NORMAL; while (*s) { if (*s == ESC_CHAR) { track_esccode(s, ansi_state); } else { ansi_map[n++] = (((int)*s++) << 16) | ansi_state; } } ansi_map[n] = ANST_NORMAL; *m = ansi_map; return n; } /* translate_string -- Convert (type = 1) raw character sequences into * MUSH substitutions or strip them (type = 0). */ char *translate_string(str, type) char *str; int type; { static char new[LBUF_SIZE]; char *bp; bp = new; if (type) { int ansi_state = ANST_NORMAL; int ansi_state_prev = ANST_NORMAL; while (*str) { switch (*str) { case ESC_CHAR: while (*str == ESC_CHAR) { track_esccode(str, ansi_state); } safe_str(ansi_transition_mushcode(ansi_state_prev, ansi_state), new, &bp); ansi_state_prev = ansi_state; continue; case ' ': if (str[1] == ' ') { safe_known_str("%b", 2, new, &bp); } else { safe_chr(' ', new, &bp); } break; case '\\': case '%': case '[': case ']': case '{': case '}': case '(': case ')': safe_chr('%', new, &bp); safe_chr(*str, new, &bp); break; case '\r': break; case '\n': safe_known_str("%r", 2, new, &bp); break; case '\t': safe_known_str("%t", 2, new, &bp); break; default: safe_chr(*str, new, &bp); } str++; } } else { while (*str) { switch (*str) { case ESC_CHAR: skip_esccode(str); continue; case '\r': break; case '\n': case '\t': safe_chr(' ', new, &bp); break; default: safe_chr(*str, new, &bp); } str++; } } *bp = '\0'; return new; } /* * capitalizes an entire string */ char *upcasestr(s) char *s; { char *p; for (p = s; p && *p; p++) *p = toupper(*p); return s; } /* * --------------------------------------------------------------------------- * munge_space: Compress multiple spaces to one space, * also remove leading and trailing spaces. */ char *munge_space(string) char *string; { char *buffer, *p, *q; buffer = alloc_lbuf("munge_space"); p = string; q = buffer; while (p && *p && isspace(*p)) p++; /* * remove initial spaces */ while (p && *p) { while (*p && !isspace(*p)) *q++ = *p++; while (*p && isspace(*++p)) ; if (*p) *q++ = ' '; } *q = '\0'; /* * remove terminal spaces and terminate * string */ return (buffer); } /* * --------------------------------------------------------------------------- * * trim_spaces: Remove leading and trailing spaces. */ char *trim_spaces(string) char *string; { char *buffer, *p, *q; buffer = alloc_lbuf("trim_spaces"); p = string; q = buffer; while (p && *p && isspace(*p)) /* remove inital spaces */ p++; while (p && *p) { while (*p && !isspace(*p)) /* copy nonspace chars */ *q++ = *p++; while (*p && isspace(*p)) /* compress spaces */ p++; if (*p) *q++ = ' '; /* leave one space */ } *q = '\0'; /* terminate string */ return (buffer); } /* * --------------------------------------------------------------------------- * grabto: Return portion of a string up to the indicated character. Also * returns a modified pointer to the string ready for another call. */ char *grabto(str, targ) char **str, targ; { char *savec, *cp; if (!str || !*str || !**str) return NULL; savec = cp = *str; while (*cp && *cp != targ) cp++; if (*cp) *cp++ = '\0'; *str = cp; return savec; } int string_compare(s1, s2) const char *s1, *s2; { if (mudstate.standalone || mudconf.space_compress) { while (isspace(*s1)) s1++; while (isspace(*s2)) s2++; while (*s1 && *s2 && ((tolower(*s1) == tolower(*s2)) || (isspace(*s1) && isspace(*s2)))) { if (isspace(*s1) && isspace(*s2)) { /* skip all * other spaces */ while (isspace(*s1)) s1++; while (isspace(*s2)) s2++; } else { s1++; s2++; } } if ((*s1) && (*s2)) return (1); if (isspace(*s1)) { while (isspace(*s1)) s1++; return (*s1); } if (isspace(*s2)) { while (isspace(*s2)) s2++; return (*s2); } if ((*s1) || (*s2)) return (1); return (0); } else { while (*s1 && *s2 && tolower(*s1) == tolower(*s2)) s1++, s2++; return (tolower(*s1) - tolower(*s2)); } } int string_prefix(string, prefix) const char *string, *prefix; { int count = 0; while (*string && *prefix && tolower(*string) == tolower(*prefix)) string++, prefix++, count++; if (*prefix == '\0') /* Matched all of prefix */ return (count); else return (0); } /* * accepts only nonempty matches starting at the beginning of a word */ const char *string_match(src, sub) const char *src, *sub; { if ((*sub != '\0') && (src)) { while (*src) { if (string_prefix(src, sub)) return src; /* else scan to beginning of next word */ while (*src && isalnum(*src)) src++; while (*src && !isalnum(*src)) src++; } } return 0; } /* * --------------------------------------------------------------------------- * replace_string: Returns an lbuf containing string STRING with all occurances * of OLD replaced by NEW. OLD and NEW may be different lengths. * (mitch 1 feb 91) * * edit_string: Like replace_string, but sensitive about ANSI codes, and * handles special ^ and $ cases. */ char *replace_string(old, new, string) const char *old, *new, *string; { char *result, *r, *s; int olen; if (string == NULL) return NULL; s = (char *)string; olen = strlen(old); r = result = alloc_lbuf("replace_string"); while (*s) { /* Copy up to the next occurrence of the first char of OLD */ while (*s && *s != *old) { safe_chr(*s, result, &r); s++; } /* * If we are really at an OLD, append NEW to the result and * bump the input string past the occurrence of * OLD. Otherwise, copy the char and try again. */ if (*s) { if (!strncmp(old, s, olen)) { safe_str((char *)new, result, &r); s += olen; } else { safe_chr(*s, result, &r); s++; } } } *r = '\0'; return result; } void edit_string(src, dst, from, to) char *src, **dst, *from, *to; { char *cp, *p; int ansi_state, to_ansi_set, to_ansi_clr, tlen, flen; /* We may have gotten an ANSI_NORMAL termination to OLD and NEW, * that the user probably didn't intend to be there. (If the * user really did want it there, he simply has to put a double * ANSI_NORMAL in; this is non-intuitive but without it we can't * let users swap one ANSI code for another using this.) Thus, * we chop off the terminating ANSI_NORMAL on both, if there is * one. */ p = from + strlen(from) - 4; if (p >= from && !strcmp(p, ANSI_NORMAL)) *p = '\0'; p = to + strlen(to) - 4; if (p >= to && !strcmp(p, ANSI_NORMAL)) *p = '\0'; /* Scan the contents of the TO string. Figure out whether we * have any embedded ANSI codes. */ ansi_state = ANST_NONE; track_all_esccodes(to, p, ansi_state); to_ansi_set = (~ANST_NONE) & ansi_state; to_ansi_clr = ANST_NONE & (~ansi_state); tlen = p - to; /* * Do the substitution. Idea for prefix/suffix from R'nice@TinyTIM */ cp = *dst = alloc_lbuf("edit_string"); if (!strcmp(from, "^")) { /* * Prepend 'to' to string */ safe_known_str(to, tlen, *dst, &cp); safe_copy_tracking(src, p, ansi_state, *dst, &cp); } else if (!strcmp(from, "$")) { /* * Append 'to' to string */ ansi_state = ANST_NONE; safe_copy_tracking(src, p, ansi_state, *dst, &cp); ansi_state |= to_ansi_set; ansi_state &= ~to_ansi_clr; safe_known_str(to, tlen, *dst, &cp); } else { /* * Replace all occurances of 'from' with 'to'. Handle the * special cases of from = \$ and \^. */ if (((from[0] == '\\') || (from[0] == '%')) && ((from[1] == '$') || (from[1] == '^')) && (from[2] == '\0')) from++; flen = strlen(from); ansi_state = ANST_NONE; while (*src) { /* Copy up to the next occurrence of the first * char of FROM. */ p = src; while (*src && (*src != *from)) { if (*src == ESC_CHAR) { track_esccode(src, ansi_state); } else { ++src; } } safe_known_str(p, src - p, *dst, &cp); /* If we are really at a FROM, append TO to the result * and bump the input string past the occurrence of * FROM. Otherwise, copy the char and try again. */ if (*src) { if (!strncmp(from, src, flen)) { /* Apply whatever ANSI transition * happens in TO */ ansi_state |= to_ansi_set; ansi_state &= ~to_ansi_clr; safe_known_str(to, tlen, *dst, &cp); src += flen; } else { /* We have to handle the case where * the first character in FROM is the * ANSI escape character. In that case * we move over and copy the entire * ANSI code. Otherwise we just copy * the character. */ if (*from == ESC_CHAR) { p = src; track_esccode(src, ansi_state); safe_known_str(p, src - p, *dst, &cp); } else { safe_chr(*src, *dst, &cp); ++src; } } } } } safe_str(ansi_transition_esccode(ansi_state, ANST_NONE), *dst, &cp); } int minmatch(str, target, min) char *str, *target; int min; { while (*str && *target && (tolower(*str) == tolower(*target))) { str++; target++; min--; } if (*str) return 0; if (!*target) return 1; return ((min <= 0) ? 1 : 0); } /* --------------------------------------------------------------------------- * safe_copy_str, safe_copy_long_str, safe_chr_real_fn - Copy buffers, * watching for overflows. */ INLINE void safe_copy_str(src, buff, bufp, max) char *src, *buff, **bufp; int max; { char *tp, *maxtp, *longtp; int n, len; tp = *bufp; if (src == NULL) { *tp = '\0'; return; } maxtp = buff + max; longtp = tp + 7; maxtp = (maxtp < longtp) ? maxtp : longtp; while (*src && (tp < maxtp)) *tp++ = *src++; if (*src == '\0') { /* copied whole src, and tp is at most maxtp */ *tp = '\0'; *bufp = tp; return; } len = strlen(src); n = max - (tp - buff); /* tp is either maxtp or longtp */ if (n <= 0) { *tp = '\0'; *bufp = tp; return; } n = ((len < n) ? len : n); memcpy(tp, src, n); tp += n; *tp = '\0'; *bufp = tp; } INLINE int safe_copy_str_fn(src, buff, bufp, max) const char *src; char *buff, **bufp; int max; { char *tp, *maxtp, *longtp; int n, len; tp = *bufp; if (src == NULL) { *tp = '\0'; return 0; } maxtp = buff + max; longtp = tp + 7; maxtp = (maxtp < longtp) ? maxtp : longtp; while (*src && (tp < maxtp)) *tp++ = *src++; if (*src == '\0') { /* copied whole src, and tp is at most maxtp */ *tp = '\0'; *bufp = tp; return 0; } len = strlen(src); n = max - (tp - buff); /* tp is either maxtp or longtp */ if (n <= 0) { len -= (tp - *bufp); *tp = '\0'; *bufp = tp; return (len); } n = ((len < n) ? len : n); memcpy(tp, src, n); tp += n; *tp = '\0'; *bufp = tp; return (len - n); } int safe_copy_long_str(src, buff, bufp, max) char *src, *buff, **bufp; int max; { int len, n; char *tp; tp = *bufp; if (src == NULL) { *tp = '\0'; return 0; } len = strlen(src); n = max - (tp - buff); if (n < 0) n = 0; strncpy (tp, src, n); buff[max] = '\0'; if (len <= n) { *bufp = tp + len; return (0); } else { *bufp = tp + n; return (len-n); } } INLINE void safe_known_str(src, known, buff, bufp) const char *src; char *buff, **bufp; int known; { int n; char *tp, *maxtp; tp = *bufp; if (!src) { *tp = '\0'; return; } maxtp = buff + LBUF_SIZE - 1; if (known > 7) { n = maxtp - tp; if (n <= 0) { *tp = '\0'; return; } n = ((known < n) ? known : n); memcpy(tp, src, n); tp += n; *tp = '\0'; *bufp = tp; return; } if (tp + known < maxtp) maxtp = tp + known; while (*src && (tp < maxtp)) *(tp)++ = *src++; *tp = '\0'; *bufp = tp; } INLINE int safe_chr_real_fn(src, buff, bufp, max) char src, *buff, **bufp; int max; { char *tp; int retval = 0; tp = *bufp; if ((tp - buff) < max) { *tp++ = src; *bufp = tp; *tp = '\0'; } else { buff[max] = '\0'; retval = 1; } return retval; } /* --------------------------------------------------------------------------- * More utilities. */ int matches_exit_from_list(str, pattern) char *str, *pattern; { char *s; while (*pattern) { for (s = str; /* check out this one */ (*s && (tolower(*s) == tolower(*pattern)) && *pattern && (*pattern != EXIT_DELIMITER)); s++, pattern++) ; /* Did we match it all? */ if (*s == '\0') { /* Make sure nothing afterwards */ while (*pattern && isspace(*pattern)) pattern++; /* Did we get it? */ if (!*pattern || (*pattern == EXIT_DELIMITER)) return 1; } /* We didn't get it, find next string to test */ while (*pattern && *pattern++ != EXIT_DELIMITER) ; while (isspace(*pattern)) pattern++; } return 0; } int ltos(s, num) char *s; long num; { /* Mark Vasoll's long int to string converter. */ char buf[20], *p; unsigned long anum; p = buf; /* absolute value */ anum = (num < 0) ? -num : num; /* build up the digits backwards by successive division */ while (anum > 9) { *p++ = '0' + (anum % 10); anum /= 10; } /* put in the sign if needed */ if (num < 0) *s++ = '-'; /* put in the last digit, this makes very fast single digits numbers */ *s++ = '0' + (char)anum; /* reverse the rest of the digits (if any) into the provided buf */ while (p-- > buf) *s++ = *p; /* terminate the resulting string */ *s = '\0'; return 0; } INLINE void safe_ltos(s, bufc, num) char *s; char **bufc; long num; { /* Mark Vasoll's long int to string converter. */ char buf[20], *p, *tp, *endp; unsigned long anum; p = buf; tp = *bufc; /* absolute value */ anum = (num < 0) ? -num : num; /* build up the digits backwards by successive division */ while (anum > 9) { *p++ = '0' + (anum % 10); anum /= 10; } if (tp > s + LBUF_SIZE - 21) { endp = s + LBUF_SIZE - 1; /* put in the sign if needed */ if (num < 0 && (tp < endp)) *tp++ = '-'; /* put in the last digit, this makes very fast single * digits numbers */ if (tp < endp) *tp++ = '0' + (char)anum; /* reverse the rest of the digits (if any) into the * provided buf */ while ((p-- > buf) && (tp < endp)) *tp++ = *p; } else { if (num < 0) *tp++ = '-'; *tp++ = '0' + (char)anum; while (p-- > buf) *tp++ = *p; } /* terminate the resulting string */ *tp = '\0'; *bufc = tp; }