/*********************************************************************** * * * This software is part of the ast package * * Copyright (c) 1982-2009 AT&T Intellectual Property * * and is licensed under the * * Common Public License, Version 1.0 * * by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * * * * Information and Software Systems Research * * AT&T Research * * Florham Park NJ * * * * David Korn * * * ***********************************************************************/ #pragma prototyped /* * string processing routines for Korn shell * */ #include #include #include "defs.h" #include #include #include "shtable.h" #include "lexstates.h" #include "national.h" #if !SHOPT_MULTIBYTE #define mbchar(p) (*(unsigned char*)p++) #endif #if _hdr_wctype # include #endif #if !_lib_iswprint && !defined(iswprint) # define iswprint(c) (((c)&~0377) || isprint(c)) #endif /* * Table lookup routine * is searched for string and corresponding value is returned * This is only used for small tables and is used to save non-sharable memory */ const Shtable_t *sh_locate(register const char *sp,const Shtable_t *table,int size) { register int first; register const Shtable_t *tp; register int c; static const Shtable_t empty = {0,0}; if(sp==0 || (first= *sp)==0) return(&empty); tp=table; while((c= *tp->sh_name) && (CC_NATIVE!=CC_ASCII || c <= first)) { if(first == c && strcmp(sp,tp->sh_name)==0) return(tp); tp = (Shtable_t*)((char*)tp+size); } return(&empty); } /* * shtab_options lookup routine */ #define sep(c) ((c)=='-'||(c)=='_') int sh_lookopt(register const char *sp, int *invert) { register int first; register const Shtable_t *tp; register int c; register const char *s, *t, *sw, *tw; int amb; int hit; int inv; int no; if(sp==0) return(0); if(*sp=='n' && *(sp+1)=='o' && (*(sp+2)!='t' || *(sp+3)!='i')) { sp+=2; if(sep(*sp)) sp++; *invert = !*invert; } if((first= *sp)==0) return(0); tp=shtab_options; amb=hit=0; for(;;) { t=tp->sh_name; if(no = *t=='n' && *(t+1)=='o' && *(t+2)!='t') t+=2; if(!(c= *t)) break; if(first == c) { if(strcmp(sp,t)==0) { *invert ^= no; return(tp->sh_number); } s=sw=sp; tw=t; for(;;) { if(!*s || *s=='=') { if (*s == '=' && !strtol(s+1, NiL, 0)) no = !no; if (!*t) { *invert ^= no; return(tp->sh_number); } if (hit || amb) { hit = 0; amb = 1; } else { hit = tp->sh_number; inv = no; } break; } else if(!*t) break; else if(sep(*s)) sw = ++s; else if(sep(*t)) tw = ++t; else if(*s==*t) { s++; t++; } else if(s==sw && t==tw) break; else { if(t!=tw) { while(*t && !sep(*t)) t++; if(!*t) break; tw = ++t; } while (s>sw && *s!=*t) s--; } } } tp = (Shtable_t*)((char*)tp+sizeof(*shtab_options)); } if(hit) *invert ^= inv; return(hit); } /* * look for the substring in and replace with * The new string is put on top of the stack */ char *sh_substitute(const char *string,const char *oldsp,char *newsp) /*@ assume string!=NULL && oldsp!=NULL && newsp!=NULL; return x satisfying x==NULL || strlen(x)==(strlen(in string)+strlen(in newsp)-strlen(in oldsp)); @*/ { register const char *sp = string; register const char *cp; const char *savesp = 0; stakseek(0); if(*sp==0) return((char*)0); if(*(cp=oldsp) == 0) goto found; #if SHOPT_MULTIBYTE mbinit(); #endif /* SHOPT_MULTIBYTE */ do { /* skip to first character which matches start of oldsp */ while(*sp && (savesp==sp || *sp != *cp)) { #if SHOPT_MULTIBYTE /* skip a whole character at a time */ int c = mbsize(sp); if(c < 0) sp++; while(c-- > 0) #endif /* SHOPT_MULTIBYTE */ stakputc(*sp++); } if(*sp == 0) return((char*)0); savesp = sp; for(;*cp;cp++) { if(*cp != *sp++) break; } if(*cp==0) /* match found */ goto found; sp = savesp; cp = oldsp; } while(*sp); return((char*)0); found: /* copy new */ stakputs(newsp); /* copy rest of string */ stakputs(sp); return(stakfreeze(1)); } /* * TRIM(sp) * Remove escape characters from characters in and eliminate quoted nulls. */ void sh_trim(register char *sp) /*@ assume sp!=NULL; promise strlen(in sp) <= in strlen(sp); @*/ { register char *dp; register int c; if(sp) { dp = sp; while(c= *sp) { #if SHOPT_MULTIBYTE int len; if(mbwide() && (len=mbsize(sp))>1) { memmove(dp, sp, len); dp += len; sp += len; continue; } #endif /* SHOPT_MULTIBYTE */ sp++; if(c == '\\') c = *sp++; if(c) *dp++ = c; } *dp = 0; } } /* * copy to changing upper case to lower case * must be big enough to hold * and may point to the same place. */ void sh_utol(register char const *str1,register char *str2) /*@ assume str1!=0 && str2!=0 return x satisfying strlen(in str1)==strlen(in str2); @*/ { register int c; for(; c= *((unsigned char*)str1); str1++,str2++) { if(isupper(c)) *str2 = tolower(c); else *str2 = c; } *str2 = 0; } /* * print quoting chars so that it can be read by the shell * puts null terminated result on stack, but doesn't freeze it */ char *sh_fmtq(const char *string) { register const char *cp = string, *op; register int c, state; int offset; if(!cp) return((char*)0); offset = staktell(); #if SHOPT_MULTIBYTE state = ((c= mbchar(cp))==0); #else state = ((c= *(unsigned char*)cp++)==0); #endif if(isaletter(c)) { #if SHOPT_MULTIBYTE while((c=mbchar(cp)),isaname(c)); #else while((c = *(unsigned char*)cp++),isaname(c)); #endif if(c==0) return((char*)string); if(c=='=') { if(*cp==0) return((char*)string); c = cp - string; stakwrite(string,c); string = cp; #if SHOPT_MULTIBYTE c = mbchar(cp); #else c = *(unsigned char*)cp++; #endif } } if(c==0 || c=='#' || c=='~') state = 1; #if SHOPT_MULTIBYTE for(;c;c= mbchar(cp)) #else for(;c; c= *(unsigned char*)cp++) #endif { #if SHOPT_MULTIBYTE if(c=='\'' || !iswprint(c)) #else if(c=='\'' || !isprint(c)) #endif /* SHOPT_MULTIBYTE */ state = 2; else if(c==']' || (c!=':' && c<=0xff && (c=sh_lexstates[ST_NORM][c]) && c!=S_EPAT)) state |=1; } if(state<2) { if(state==1) stakputc('\''); if(c = --cp - string) stakwrite(string,c); if(state==1) stakputc('\''); } else { stakwrite("$'",2); cp = string; #if SHOPT_MULTIBYTE while(op = cp, c= mbchar(cp)) #else while(op = cp, c= *(unsigned char*)cp++) #endif { state=1; switch(c) { case ('a'==97?'\033':39): c = 'E'; break; case '\n': c = 'n'; break; case '\r': c = 'r'; break; case '\t': c = 't'; break; case '\f': c = 'f'; break; case '\b': c = 'b'; break; case '\a': c = 'a'; break; case '\\': case '\'': break; default: #if SHOPT_MULTIBYTE if(!iswprint(c)) { while(op quoting chars so that it can be read by the shell * puts null terminated result on stack, but doesn't freeze it * single!=0 limits quoting to '...' * fold>0 prints raw newlines and inserts appropriately * escaped newlines every (fold-x) chars */ char *sh_fmtqf(const char *string, int single, int fold) { register const char *cp = string; register const char *bp; register const char *vp; register int c; register int n; register int q; register int a; int offset; if (--fold < 8) fold = 0; if (!cp || !*cp || !single && !fold || fold && strlen(string) < fold) return sh_fmtq(cp); offset = staktell(); single = single ? 1 : 3; c = mbchar(string); a = isaletter(c) ? '=' : 0; vp = cp + 1; do { q = 0; n = fold; bp = cp; while ((!n || n-- > 0) && (c = mbchar(cp))) { if (a && !isaname(c)) a = 0; #if SHOPT_MULTIBYTE if (c >= 0x200) continue; if (c == '\'' || !iswprint(c)) #else if (c == '\'' || !isprint(c)) #endif /* SHOPT_MULTIBYTE */ { q = single; break; } if (c == '\n') q = 1; else if (c == a) { stakwrite(bp, cp - bp); bp = cp; vp = cp + 1; a = 0; } else if ((c == '#' || c == '~') && cp == vp || c == ']' || c != ':' && (c = sh_lexstates[ST_NORM][c]) && c != S_EPAT) q = 1; } if (q & 2) { stakputc('$'); stakputc('\''); cp = bp; n = fold - 3; q = 1; while (c = mbchar(cp)) { switch (c) { case ('a'==97?'\033':39): c = 'E'; break; case '\n': q = 0; n = fold - 1; break; case '\r': c = 'r'; break; case '\t': c = 't'; break; case '\f': c = 'f'; break; case '\b': c = 'b'; break; case '\a': c = 'a'; break; case '\\': if (*cp == 'n') { c = '\n'; q = 0; n = fold - 1; break; } case '\'': break; default: #if SHOPT_MULTIBYTE if(!iswprint(c)) #else if(!isprint(c)) #endif { if ((n -= 4) <= 0) { stakwrite("'\\\n$'", 5); n = fold - 7; } sfprintf(staksp, "\\%03o", c); continue; } q = 0; break; } if ((n -= q + 1) <= 0) { if (!q) { stakputc('\''); cp = bp; break; } stakwrite("'\\\n$'", 5); n = fold - 5; } if (q) stakputc('\\'); else q = 1; stakputc(c); bp = cp; } if (!c) stakputc('\''); } else if (q & 1) { stakputc('\''); cp = bp; n = fold ? (fold - 2) : 0; while (c = mbchar(cp)) { if (c == '\n') n = fold - 1; else if (n && --n <= 0) { n = fold - 2; stakwrite(bp, --cp - bp); bp = cp; stakwrite("'\\\n'", 4); } else if (n == 1 && *cp == '\'') { n = fold - 5; stakwrite(bp, --cp - bp); bp = cp; stakwrite("'\\\n\\''", 6); } else if (c == '\'') { stakwrite(bp, cp - bp - 1); bp = cp; if (n && (n -= 4) <= 0) { n = fold - 5; stakwrite("'\\\n\\''", 6); } else stakwrite("'\\''", 4); } } stakwrite(bp, cp - bp - 1); stakputc('\''); } else if (n = fold) { cp = bp; while (c = mbchar(cp)) { if (--n <= 0) { n = fold; stakwrite(bp, --cp - bp); bp = cp; stakwrite("\\\n", 2); } } stakwrite(bp, cp - bp - 1); } else stakwrite(bp, cp - bp); if (c) { stakputc('\\'); stakputc('\n'); } } while (c); stakputc(0); return(stakptr(offset)); } #if SHOPT_MULTIBYTE int sh_strchr(const char *string, register const char *dp) { wchar_t c, d; register const char *cp=string; mbinit(); d = mbchar(dp); mbinit(); while(c = mbchar(cp)) { if(c==d) return(cp-string); } if(d==0) return(cp-string); return(-1); } #endif /* SHOPT_MULTIBYTE */ const char *_sh_translate(const char *message) { #if ERROR_VERSION >= 20000317L return(ERROR_translate(0,0,e_dict,message)); #else #if ERROR_VERSION >= 20000101L return(ERROR_translate(e_dict,message)); #else return(ERROR_translate(message,1)); #endif #endif } /* * change '['identifier']' to identifier * character before must be a '[' * returns pointer to last character */ char *sh_checkid(char *str, char *last) { register unsigned char *cp = (unsigned char*)str; register unsigned char *v = cp; register int c; if(c= *cp++,isaletter(c)) while(c= *cp++,isaname(c)); if(c==']' && (!last || ((char*)cp==last))) { /* eliminate [ and ] */ while(v < cp) { v[-1] = *v; v++; } if(last) last -=2; else { while(*v) { v[-2] = *v; v++; } v[-2] = 0; last = (char*)v; } } return(last); } #if _AST_VERSION <= 20000317L char *fmtident(const char *string) { return((char*)string); } #endif