/* strings.c Copyright(1988) Dan Heller */ #include "mush.h" /* * reverse a string. Useful for uucp-style address comparisons. */ char * reverse(s) char s[]; { int n = strlen(s), m; char c; if (n < 1) return 0; if (n & 1) n = n/2 + 1, m = n - 2; else n /= 2, m = n - 1; for ( ; m >= 0; m--, n++) c = s[n], s[n] = s[m], s[m] = c; return s; } /* * lose the newline character, trailing whitespace, and return the end of p * test for '\n' separately since some _ctype_[] arrays may not have the * _S bit set for the newline character. see for more info. */ char * no_newln(p) register char *p; { register char *p2 = p + strlen(p); /* point it to the null terminator */ while (p2 > p && *--p2 == '\n' || isspace(*p2)) *p2 = 0; /* get rid of newline and trailing spaces */ return p2; } /* find any character in s1 that's in s2; return pointer to char in s1. */ char * any(s1, s2) register char *s1, *s2; { register char *p; if (!s1 || !*s1 || !s2 || !*s2) return NULL; for( ; *s1; s1++) { for(p = s2; *p; p++) if (*p == *s1) return s1; } return NULL; } /* check two lists of strings each of which contain substrings. * Each substring is delimited by any char in "delimiters" * return true if any elements in list1 are on list2. * thus: * string1 = "foo, bar, baz" * string2 = "foobar, baz, etc" * delimiters = ", \t" * example returns 1 because "baz" exists in both lists * NOTE: case is ignored. */ chk_two_lists(list1, list2, delimiters) register char *list1, *list2, *delimiters; { register char *p, c; register int found = 0; if (!list1 || !list2) return 0; if (p = any(list1, delimiters)) { if (p > list1) { c = *p; *p = 0; /* Check list2 against the first word of list1. * Swap places of list2 and list1 to step through list2. */ found = chk_two_lists(list2, list1, delimiters); *p = c; } if (found) return 1; for (p++; *p && index(delimiters, *p); p++) ; if (!*p) return 0; } else if (!any(list2, delimiters)) /* Do the trivial case of single words */ return !lcase_strncmp(list1, list2, -1); else p = list1; /* Either only list2 has delims or the first word of list1 * did not match anything in list2. Check list2 against the * rest of list1. This could be more efficient by using a * different function to avoid repeating the any() calls. */ return chk_two_lists(list2, p, delimiters); } bzero(addr, size) register char *addr; register int size; { while (size-- > 0) addr[size] = 0; } /* do an atoi() on the string passed and return in "val" the decimal value. * the function returns a pointer to the location in the string that is not * a digit. */ char * my_atoi(p, val) register char *p; register int *val; { int positive = 1; if (!p) return NULL; *val = 0; if (*p == '-') positive = -1, p++; while (isdigit(*p)) *val = (*val) * 10 + *p++ - '0'; *val *= positive; return p; } /* strcmp ignoring case */ lcase_strncmp(str1, str2, n) register char *str1, *str2; { while (*str1 && *str2 && --n != 0) if (lower(*str1) != lower(*str2)) break; else str1++, str2++; return lower(*str1) - lower(*str2); } /* strcpy converting everything to lower case (arbitrary) to ignore cases */ char * lcase_strcpy(dst, src) register char *dst, *src; { register char *s = dst; /* "lower" is a macro, don't increment its argument! */ while (*dst++ = lower(*src)) src++; return s; } /* this strcpy returns number of bytes copied */ Strcpy(dst, src) register char *dst, *src; { register int n = 0; if (!dst || !src) return 0; while (*dst++ = *src++) n++; return n; } char * savestr(s) register char *s; { register char *p; if (!s) s = ""; if (!(p = malloc((unsigned) (strlen(s) + 1)))) { error("out of memory saving %s", s); return NULL; } return strcpy(p, s); } /* copy a vector of strings into one string -- return the end of the string */ char * argv_to_string(p, argv) register char *p, **argv; { register int i; register char *ptr = p; *p = 0; if (!argv[0]) return ""; for (i = 0; argv[i]; i++) ptr += strlen(sprintf(ptr, "%s ", argv[i])); *--ptr = 0; /* get rid of the last space */ return ptr; } char * itoa(n) { static char buf[10]; return sprintf(buf, "%d", n); } /* * There are two different kinds of sprintf() --those that return char * and * those that return int. System-V returns int (the length of the resulting * string). BSD has historically returned a pointer to the resulting string * instead. Mush was originally written under BSD, so the usage has always * been to assume the char * method. Because the system-v method is far more * useful, mush should some day change to use that method, but until then, * this routine was written to allow all the unix'es to appear the same to * the programmer regardless of which sprintf is actually used. The "latest" * version of 4.3BSD (as of Fall 1988) has changed its format to go from the * historical BSD method to the sys-v method. It is no longer possible to * simply #ifdef this routine for sys-v --it is now required to use this * routine regardless of which sprintf is notice to your machine. However, * if you know your system's sprintf returns a char *, you can remove the * define in strings.h */ #include /*VARARGS*/ /*ARGSUSED*/ char * Sprintf(va_alist) va_dcl { char *buf, *fmt; va_list ap; va_start(ap); buf = va_arg(ap, char *); fmt = va_arg(ap, char *); #ifdef VPRINTF (void) vsprintf(buf, fmt, ap); #else { FILE foo; foo._cnt = BUFSIZ; foo._base = foo._ptr = buf; /* may have to be cast (unsigned char *) */ foo._flag = _IOWRT+_IOSTRG; (void) _doprnt(fmt, ap, &foo); *foo._ptr = '\0'; /* plant terminating null character */ } #endif /* VPRINTF */ va_end(ap); return buf; } void print_argv(argv) char **argv; { while (*argv) if (debug) wprint("(%s) ", *argv++); else wprint("%s ", *argv++); wprint("\n"); } /* * putstring -- put a string into a file. Expand \t's into tabs and \n's * into newlines. Append a \n and fflush(fp); */ void putstring(p, fp) register char *p; register FILE *fp; { for ( ; *p; ++p) if (*p != '\\') (void) fputc(*p, fp); else switch(*++p) { case 'n': (void) fputc('\n', fp); when 't': (void) fputc('\t', fp); otherwise: (void) fputc(*p, fp); } (void) fputc('\n', fp); (void) fflush(fp); } #define chtoi(c) ((int)(c) - (int)'0') /* m_xlate(str) converts strings of chars which contain ascii representations * of control characters appearing in str into the literal characters they * represent. The usual curses-mode character expansions (\Cx -> control-x) * are honored, as are most C escapes. Unrecognized portions are unchanged. */ char * m_xlate (str) register char *str; { register char *r, *s, *t; int dv, nd; /* * r will receive the new string, s will track the old one, * and t will step through escape sequences * This allows the translation to be done in place */ r = s = str; while (s && *s) { if (*s == '\\') { t = s + 1; /* * After each case below, t should point to the character * following the escape sequence */ switch(*t) { case '\0' : /* * Hmmm ... a backslash followed by the string * terminator. Copy the backslash ONLY. */ *r++ = *s++; break; case '0' : case '1' : case '2' : case '3' : case '4' : case '5' : case '6' : case '7' : /* * Convert up to 3 octal digits to their ascii value */ dv = chtoi(*t++); for (nd = 0; (isdigit(*t) && (nd < 2)); nd++) if (chtoi(*t) < 8) dv = (8 * dv) + chtoi(*t++); else break; if (dv < 256 && dv > 0) /* Valid octal number escaped */ *r++ = (char)dv; else /* Invalid octal number, so copy unchanged */ while (s < t) *r++ = *s++; break; case 'b' : *r++ = '\b'; t++; break; case 'C' : t++; if (*t == '?') *r++ = '\177'; else if (*t == '~') *r++ = '\036'; else if (*t == '/') *r++ = '\037'; else if (isalpha(*t) || *t > '\132' && *t < '\140') *r++ = *t & 037; else while (s <= t) *r++ = *s++; t++; break; case 'E' : *r++ = '\033'; t++; break; case 'f' : *r++ = '\f'; t++; break; case 'n' : *r++ = '\n'; t++; break; case 'r' : *r++ = '\r'; t++; break; case 't' : *r++ = '\t'; t++; break; case '\\' : *r++ = *t++; break; default : /* * Not recognized, so copy both characters */ *r++ = *s++; *r++ = *s++; break; } /* * Now make sure s also points to the character after the * escape sequence, by comparing to t */ if (t > s) s = t; } else *r++ = *s++; } *r = '\0'; return str; } /* * Convert control characters to ascii format (reverse effect of m_xlate()). */ char * ctrl_strcpy(s_out, s_in, bind_format) register char *s_out, *s_in; { #if !defined(M_XENIX) || (defined(M_XENIX) && !defined(CURSES)) extern char *_unctrl[]; #endif /* !M_XENIX || M_XENIX && !CURSES */ char *start = s_out; for (; *s_in; s_in++) if (*s_in == '\n') *s_out++ = '\\', *s_out++ = 'n'; else if (*s_in == '\r') *s_out++ = '\\', *s_out++ = 'r'; else if (*s_in == '\t') *s_out++ = '\\', *s_out++ = 't'; else if (*s_in == ESC) *s_out++ = '\\', *s_out++ = 'E'; else if (iscntrl(*s_in)) { if (bind_format) *s_out++ = '\\', *s_out++ = 'C'; else *s_out++ = '^'; *s_out++ = _unctrl[*s_in][1]; } else *s_out++ = *s_in; *s_out = 0; return start; } /* * This routine returns a pointer to the file portion of a path/file name. */ char * basename(path) register char *path; { char *file; if (file = rindex(path, '/')) return ++file; return path; }