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