1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 1982-2010 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Common Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
8 * *
9 * A copy of the License is available at *
10 * http://www.opensource.org/licenses/cpl1.0.txt *
11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
12 * *
13 * Information and Software Systems Research *
14 * AT&T Research *
15 * Florham Park NJ *
16 * *
17 * David Korn <dgk@research.att.com> *
18 * *
19 ***********************************************************************/
20 #pragma prototyped
21 /*
22 * string processing routines for Korn shell
23 *
24 */
25
26 #include <ast.h>
27 #include <ast_wchar.h>
28 #include "defs.h"
29 #include <stak.h>
30 #include <ccode.h>
31 #include "shtable.h"
32 #include "lexstates.h"
33 #include "national.h"
34
35 #if !SHOPT_MULTIBYTE
36 #define mbchar(p) (*(unsigned char*)p++)
37 #endif
38
39 #if _hdr_wctype
40 # include <wctype.h>
41 #endif
42
43 #if !_lib_iswprint && !defined(iswprint)
44 # define iswprint(c) (((c)&~0377) || isprint(c))
45 #endif
46
47
48 /*
49 * Table lookup routine
50 * <table> is searched for string <sp> and corresponding value is returned
51 * This is only used for small tables and is used to save non-sharable memory
52 */
53
sh_locate(register const char * sp,const Shtable_t * table,int size)54 const Shtable_t *sh_locate(register const char *sp,const Shtable_t *table,int size)
55 {
56 register int first;
57 register const Shtable_t *tp;
58 register int c;
59 static const Shtable_t empty = {0,0};
60 if(sp==0 || (first= *sp)==0)
61 return(&empty);
62 tp=table;
63 while((c= *tp->sh_name) && (CC_NATIVE!=CC_ASCII || c <= first))
64 {
65 if(first == c && strcmp(sp,tp->sh_name)==0)
66 return(tp);
67 tp = (Shtable_t*)((char*)tp+size);
68 }
69 return(&empty);
70 }
71
72 /*
73 * shtab_options lookup routine
74 */
75
76 #define sep(c) ((c)=='-'||(c)=='_')
77
sh_lookopt(register const char * sp,int * invert)78 int sh_lookopt(register const char *sp, int *invert)
79 {
80 register int first;
81 register const Shtable_t *tp;
82 register int c;
83 register const char *s, *t, *sw, *tw;
84 int amb;
85 int hit;
86 int inv;
87 int no;
88 if(sp==0)
89 return(0);
90 if(*sp=='n' && *(sp+1)=='o' && (*(sp+2)!='t' || *(sp+3)!='i'))
91 {
92 sp+=2;
93 if(sep(*sp))
94 sp++;
95 *invert = !*invert;
96 }
97 if((first= *sp)==0)
98 return(0);
99 tp=shtab_options;
100 amb=hit=0;
101 for(;;)
102 {
103 t=tp->sh_name;
104 if(no = *t=='n' && *(t+1)=='o' && *(t+2)!='t')
105 t+=2;
106 if(!(c= *t))
107 break;
108 if(first == c)
109 {
110 if(strcmp(sp,t)==0)
111 {
112 *invert ^= no;
113 return(tp->sh_number);
114 }
115 s=sw=sp;
116 tw=t;
117 for(;;)
118 {
119 if(!*s || *s=='=')
120 {
121 if (*s == '=' && !strtol(s+1, NiL, 0))
122 no = !no;
123 if (!*t)
124 {
125 *invert ^= no;
126 return(tp->sh_number);
127 }
128 if (hit || amb)
129 {
130 hit = 0;
131 amb = 1;
132 }
133 else
134 {
135 hit = tp->sh_number;
136 inv = no;
137 }
138 break;
139 }
140 else if(!*t)
141 break;
142 else if(sep(*s))
143 sw = ++s;
144 else if(sep(*t))
145 tw = ++t;
146 else if(*s==*t)
147 {
148 s++;
149 t++;
150 }
151 else if(s==sw && t==tw)
152 break;
153 else
154 {
155 if(t!=tw)
156 {
157 while(*t && !sep(*t))
158 t++;
159 if(!*t)
160 break;
161 tw = ++t;
162 }
163 while (s>sw && *s!=*t)
164 s--;
165 }
166 }
167 }
168 tp = (Shtable_t*)((char*)tp+sizeof(*shtab_options));
169 }
170 if(hit)
171 *invert ^= inv;
172 return(hit);
173 }
174
175 /*
176 * look for the substring <oldsp> in <string> and replace with <newsp>
177 * The new string is put on top of the stack
178 */
sh_substitute(const char * string,const char * oldsp,char * newsp)179 char *sh_substitute(const char *string,const char *oldsp,char *newsp)
180 /*@
181 assume string!=NULL && oldsp!=NULL && newsp!=NULL;
182 return x satisfying x==NULL ||
183 strlen(x)==(strlen(in string)+strlen(in newsp)-strlen(in oldsp));
184 @*/
185 {
186 register const char *sp = string;
187 register const char *cp;
188 const char *savesp = 0;
189 stakseek(0);
190 if(*sp==0)
191 return((char*)0);
192 if(*(cp=oldsp) == 0)
193 goto found;
194 #if SHOPT_MULTIBYTE
195 mbinit();
196 #endif /* SHOPT_MULTIBYTE */
197 do
198 {
199 /* skip to first character which matches start of oldsp */
200 while(*sp && (savesp==sp || *sp != *cp))
201 {
202 #if SHOPT_MULTIBYTE
203 /* skip a whole character at a time */
204 int c = mbsize(sp);
205 if(c < 0)
206 sp++;
207 while(c-- > 0)
208 #endif /* SHOPT_MULTIBYTE */
209 stakputc(*sp++);
210 }
211 if(*sp == 0)
212 return((char*)0);
213 savesp = sp;
214 for(;*cp;cp++)
215 {
216 if(*cp != *sp++)
217 break;
218 }
219 if(*cp==0)
220 /* match found */
221 goto found;
222 sp = savesp;
223 cp = oldsp;
224 }
225 while(*sp);
226 return((char*)0);
227
228 found:
229 /* copy new */
230 stakputs(newsp);
231 /* copy rest of string */
232 stakputs(sp);
233 return(stakfreeze(1));
234 }
235
236 /*
237 * TRIM(sp)
238 * Remove escape characters from characters in <sp> and eliminate quoted nulls.
239 */
240
sh_trim(register char * sp)241 void sh_trim(register char *sp)
242 /*@
243 assume sp!=NULL;
244 promise strlen(in sp) <= in strlen(sp);
245 @*/
246 {
247 register char *dp;
248 register int c;
249 if(sp)
250 {
251 dp = sp;
252 while(c= *sp)
253 {
254 #if SHOPT_MULTIBYTE
255 int len;
256 if(mbwide() && (len=mbsize(sp))>1)
257 {
258 memmove(dp, sp, len);
259 dp += len;
260 sp += len;
261 continue;
262 }
263 #endif /* SHOPT_MULTIBYTE */
264 sp++;
265 if(c == '\\')
266 c = *sp++;
267 if(c)
268 *dp++ = c;
269 }
270 *dp = 0;
271 }
272 }
273
274 /*
275 * copy <str1> to <str2> changing upper case to lower case
276 * <str2> must be big enough to hold <str1>
277 * <str1> and <str2> may point to the same place.
278 */
279
sh_utol(register char const * str1,register char * str2)280 void sh_utol(register char const *str1,register char *str2)
281 /*@
282 assume str1!=0 && str2!=0
283 return x satisfying strlen(in str1)==strlen(in str2);
284 @*/
285 {
286 register int c;
287 for(; c= *((unsigned char*)str1); str1++,str2++)
288 {
289 if(isupper(c))
290 *str2 = tolower(c);
291 else
292 *str2 = c;
293 }
294 *str2 = 0;
295 }
296
297 /*
298 * print <str> quoting chars so that it can be read by the shell
299 * puts null terminated result on stack, but doesn't freeze it
300 */
sh_fmtq(const char * string)301 char *sh_fmtq(const char *string)
302 {
303 register const char *cp = string, *op;
304 register int c, state;
305 int offset;
306 if(!cp)
307 return((char*)0);
308 offset = staktell();
309 #if SHOPT_MULTIBYTE
310 state = ((c= mbchar(cp))==0);
311 #else
312 state = ((c= *(unsigned char*)cp++)==0);
313 #endif
314 if(isaletter(c))
315 {
316 #if SHOPT_MULTIBYTE
317 while((c=mbchar(cp)),isaname(c));
318 #else
319 while((c = *(unsigned char*)cp++),isaname(c));
320 #endif
321 if(c==0)
322 return((char*)string);
323 if(c=='=')
324 {
325 if(*cp==0)
326 return((char*)string);
327 c = cp - string;
328 stakwrite(string,c);
329 string = cp;
330 #if SHOPT_MULTIBYTE
331 c = mbchar(cp);
332 #else
333 c = *(unsigned char*)cp++;
334 #endif
335 }
336 }
337 if(c==0 || c=='#' || c=='~')
338 state = 1;
339 #if SHOPT_MULTIBYTE
340 for(;c;c= mbchar(cp))
341 #else
342 for(;c; c= *(unsigned char*)cp++)
343 #endif
344 {
345 #if SHOPT_MULTIBYTE
346 if(c=='\'' || !iswprint(c))
347 #else
348 if(c=='\'' || !isprint(c))
349 #endif /* SHOPT_MULTIBYTE */
350 state = 2;
351 else if(c==']' || (c!=':' && c<=0xff && (c=sh_lexstates[ST_NORM][c]) && c!=S_EPAT))
352 state |=1;
353 }
354 if(state<2)
355 {
356 if(state==1)
357 stakputc('\'');
358 if(c = --cp - string)
359 stakwrite(string,c);
360 if(state==1)
361 stakputc('\'');
362 }
363 else
364 {
365 stakwrite("$'",2);
366 cp = string;
367 #if SHOPT_MULTIBYTE
368 while(op = cp, c= mbchar(cp))
369 #else
370 while(op = cp, c= *(unsigned char*)cp++)
371 #endif
372 {
373 state=1;
374 switch(c)
375 {
376 case ('a'==97?'\033':39):
377 c = 'E';
378 break;
379 case '\n':
380 c = 'n';
381 break;
382 case '\r':
383 c = 'r';
384 break;
385 case '\t':
386 c = 't';
387 break;
388 case '\f':
389 c = 'f';
390 break;
391 case '\b':
392 c = 'b';
393 break;
394 case '\a':
395 c = 'a';
396 break;
397 case '\\': case '\'':
398 break;
399 default:
400 #if SHOPT_MULTIBYTE
401 if(!iswprint(c))
402 {
403 while(op<cp)
404 sfprintf(staksp,"\\%.3o",*(unsigned char*)op++);
405 continue;
406 }
407 #else
408 if(!isprint(c))
409 {
410 sfprintf(staksp,"\\%.3o",c);
411 continue;
412 }
413 #endif
414 state=0;
415 break;
416 }
417 if(state)
418 {
419 stakputc('\\');
420 stakputc(c);
421 }
422 else
423 stakwrite(op, cp-op);
424 }
425 stakputc('\'');
426 }
427 stakputc(0);
428 return(stakptr(offset));
429 }
430
431 /*
432 * print <str> quoting chars so that it can be read by the shell
433 * puts null terminated result on stack, but doesn't freeze it
434 * single!=0 limits quoting to '...'
435 * fold>0 prints raw newlines and inserts appropriately
436 * escaped newlines every (fold-x) chars
437 */
sh_fmtqf(const char * string,int single,int fold)438 char *sh_fmtqf(const char *string, int single, int fold)
439 {
440 register const char *cp = string;
441 register const char *bp;
442 register const char *vp;
443 register int c;
444 register int n;
445 register int q;
446 register int a;
447 int offset;
448
449 if (--fold < 8)
450 fold = 0;
451 if (!cp || !*cp || !single && !fold || fold && strlen(string) < fold)
452 return sh_fmtq(cp);
453 offset = staktell();
454 single = single ? 1 : 3;
455 c = mbchar(string);
456 a = isaletter(c) ? '=' : 0;
457 vp = cp + 1;
458 do
459 {
460 q = 0;
461 n = fold;
462 bp = cp;
463 while ((!n || n-- > 0) && (c = mbchar(cp)))
464 {
465 if (a && !isaname(c))
466 a = 0;
467 #if SHOPT_MULTIBYTE
468 if (c >= 0x200)
469 continue;
470 if (c == '\'' || !iswprint(c))
471 #else
472 if (c == '\'' || !isprint(c))
473 #endif /* SHOPT_MULTIBYTE */
474 {
475 q = single;
476 break;
477 }
478 if (c == '\n')
479 q = 1;
480 else if (c == a)
481 {
482 stakwrite(bp, cp - bp);
483 bp = cp;
484 vp = cp + 1;
485 a = 0;
486 }
487 else if ((c == '#' || c == '~') && cp == vp || c == ']' || c != ':' && (c = sh_lexstates[ST_NORM][c]) && c != S_EPAT)
488 q = 1;
489 }
490 if (q & 2)
491 {
492 stakputc('$');
493 stakputc('\'');
494 cp = bp;
495 n = fold - 3;
496 q = 1;
497 while (c = mbchar(cp))
498 {
499 switch (c)
500 {
501 case ('a'==97?'\033':39):
502 c = 'E';
503 break;
504 case '\n':
505 q = 0;
506 n = fold - 1;
507 break;
508 case '\r':
509 c = 'r';
510 break;
511 case '\t':
512 c = 't';
513 break;
514 case '\f':
515 c = 'f';
516 break;
517 case '\b':
518 c = 'b';
519 break;
520 case '\a':
521 c = 'a';
522 break;
523 case '\\':
524 if (*cp == 'n')
525 {
526 c = '\n';
527 q = 0;
528 n = fold - 1;
529 break;
530 }
531 case '\'':
532 break;
533 default:
534 #if SHOPT_MULTIBYTE
535 if(!iswprint(c))
536 #else
537 if(!isprint(c))
538 #endif
539 {
540 if ((n -= 4) <= 0)
541 {
542 stakwrite("'\\\n$'", 5);
543 n = fold - 7;
544 }
545 sfprintf(staksp, "\\%03o", c);
546 continue;
547 }
548 q = 0;
549 break;
550 }
551 if ((n -= q + 1) <= 0)
552 {
553 if (!q)
554 {
555 stakputc('\'');
556 cp = bp;
557 break;
558 }
559 stakwrite("'\\\n$'", 5);
560 n = fold - 5;
561 }
562 if (q)
563 stakputc('\\');
564 else
565 q = 1;
566 stakputc(c);
567 bp = cp;
568 }
569 if (!c)
570 stakputc('\'');
571 }
572 else if (q & 1)
573 {
574 stakputc('\'');
575 cp = bp;
576 n = fold ? (fold - 2) : 0;
577 while (c = mbchar(cp))
578 {
579 if (c == '\n')
580 n = fold - 1;
581 else if (n && --n <= 0)
582 {
583 n = fold - 2;
584 stakwrite(bp, --cp - bp);
585 bp = cp;
586 stakwrite("'\\\n'", 4);
587 }
588 else if (n == 1 && *cp == '\'')
589 {
590 n = fold - 5;
591 stakwrite(bp, --cp - bp);
592 bp = cp;
593 stakwrite("'\\\n\\''", 6);
594 }
595 else if (c == '\'')
596 {
597 stakwrite(bp, cp - bp - 1);
598 bp = cp;
599 if (n && (n -= 4) <= 0)
600 {
601 n = fold - 5;
602 stakwrite("'\\\n\\''", 6);
603 }
604 else
605 stakwrite("'\\''", 4);
606 }
607 }
608 stakwrite(bp, cp - bp - 1);
609 stakputc('\'');
610 }
611 else if (n = fold)
612 {
613 cp = bp;
614 while (c = mbchar(cp))
615 {
616 if (--n <= 0)
617 {
618 n = fold;
619 stakwrite(bp, --cp - bp);
620 bp = cp;
621 stakwrite("\\\n", 2);
622 }
623 }
624 stakwrite(bp, cp - bp - 1);
625 }
626 else
627 stakwrite(bp, cp - bp);
628 if (c)
629 {
630 stakputc('\\');
631 stakputc('\n');
632 }
633 } while (c);
634 stakputc(0);
635 return(stakptr(offset));
636 }
637
638 #if SHOPT_MULTIBYTE
sh_strchr(const char * string,register const char * dp)639 int sh_strchr(const char *string, register const char *dp)
640 {
641 wchar_t c, d;
642 register const char *cp=string;
643 mbinit();
644 d = mbchar(dp);
645 mbinit();
646 while(c = mbchar(cp))
647 {
648 if(c==d)
649 return(cp-string);
650 }
651 if(d==0)
652 return(cp-string);
653 return(-1);
654 }
655 #endif /* SHOPT_MULTIBYTE */
656
_sh_translate(const char * message)657 const char *_sh_translate(const char *message)
658 {
659 #if ERROR_VERSION >= 20000317L
660 return(ERROR_translate(0,0,e_dict,message));
661 #else
662 #if ERROR_VERSION >= 20000101L
663 return(ERROR_translate(e_dict,message));
664 #else
665 return(ERROR_translate(message,1));
666 #endif
667 #endif
668 }
669
670 /*
671 * change '['identifier']' to identifier
672 * character before <str> must be a '['
673 * returns pointer to last character
674 */
sh_checkid(char * str,char * last)675 char *sh_checkid(char *str, char *last)
676 {
677 register unsigned char *cp = (unsigned char*)str;
678 register unsigned char *v = cp;
679 register int c;
680 if(c= *cp++,isaletter(c))
681 while(c= *cp++,isaname(c));
682 if(c==']' && (!last || ((char*)cp==last)))
683 {
684 /* eliminate [ and ] */
685 while(v < cp)
686 {
687 v[-1] = *v;
688 v++;
689 }
690 if(last)
691 last -=2;
692 else
693 {
694 while(*v)
695 {
696 v[-2] = *v;
697 v++;
698 }
699 v[-2] = 0;
700 last = (char*)v;
701 }
702 }
703 return(last);
704 }
705
706 #if _AST_VERSION <= 20000317L
fmtident(const char * string)707 char *fmtident(const char *string)
708 {
709 return((char*)string);
710 }
711 #endif
712