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