1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1982-2014 AT&T Intellectual Property          *
5 *                      and is licensed under the                       *
6 *                 Eclipse Public License, Version 1.0                  *
7 *                    by AT&T Intellectual Property                     *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *          http://www.eclipse.org/org/documents/epl-v10.html           *
11 *         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
12 *                                                                      *
13 *              Information and Software Systems Research               *
14 *                            AT&T Research                             *
15 *                           Florham Park NJ                            *
16 *                                                                      *
17 *                    David Korn <dgkorn@gmail.com>                     *
18 *                                                                      *
19 ***********************************************************************/
20 #pragma prototyped
21 /*
22  * echo [arg...]
23  * print [-nrps] [-f format] [-u filenum] [arg...]
24  * printf  format [arg...]
25  *
26  *   David Korn
27  *   AT&T Labs
28  */
29 
30 #include	"defs.h"
31 #include	<error.h>
32 #include	"io.h"
33 #include	"name.h"
34 #include	"history.h"
35 #include	"builtins.h"
36 #include	"streval.h"
37 #include	<tmx.h>
38 #include	<ccode.h>
39 
40 union types_t
41 {
42 	unsigned char	c;
43 	short		h;
44 	int		i;
45 	long		l;
46 	Sflong_t	ll;
47 	Sfdouble_t	ld;
48 	double		d;
49 	float		f;
50 	char		*s;
51 	int		*ip;
52 	char		**p;
53 };
54 
55 struct printf
56 {
57 	Sffmt_t		hdr;
58 	int		argsize;
59 	int		intvar;
60 	char		**nextarg;
61 	char		*lastarg;
62 	char		cescape;
63 	char		err;
64 	Shell_t		*sh;
65 };
66 
67 struct printmap
68 {
69 	size_t		size;
70 	char		*name;
71 	char		map[3];
72 };
73 
74 static const struct printmap  Pmap[] =
75 {
76 	3,	"csv",			"#q",
77 	3,	"ere",			"R",
78 	4,	"html",			"H",
79 	17,	"nounicodeliterals",	"0q",
80 	7,	"pattern",		"P",
81 	15,	"unicodeliterals",	"+q",
82 	3,	"url",			"#H",
83 	0,	0,			0,
84 };
85 
86 
87 static int		extend(Sfio_t*,void*, Sffmt_t*);
88 static const char   	preformat[] = "";
89 static char		*genformat(Shell_t*,char*);
90 static int		fmtvecho(Shell_t*, const char*, struct printf*);
91 static ssize_t		fmtbase64(Shell_t*,Sfio_t*, char*, const char*, int);
92 
93 struct print
94 {
95 	Shell_t         *sh;
96 	const char	*options;
97 	char		raw;
98 	char		echon;
99 };
100 
101 static char* 	nullarg[] = { 0, 0 };
102 
B_echo(int argc,char * argv[],Shbltin_t * context)103 int    B_echo(int argc, char *argv[],Shbltin_t *context)
104 {
105 	static char bsd_univ;
106 	struct print prdata;
107 	prdata.options = sh_optecho+5;
108 	prdata.raw = prdata.echon = 0;
109 	prdata.sh = context->shp;
110 	NOT_USED(argc);
111 	/* This mess is because /bin/echo on BSD is different */
112 	if(!prdata.sh->universe)
113 	{
114 		register char *universe;
115 		if(universe=astconf("UNIVERSE",0,0))
116 			bsd_univ = (strcmp(universe,"ucb")==0);
117 		prdata.sh->universe = 1;
118 	}
119 	if(!bsd_univ)
120 		return(b_print(0,argv,(Shbltin_t*)&prdata));
121 	prdata.options = sh_optecho;
122 	prdata.raw = 1;
123 	while(argv[1] && *argv[1]=='-')
124 	{
125 		if(strcmp(argv[1],"-n")==0)
126 			prdata.echon = 1;
127 #if !SHOPT_ECHOE
128 		else if(strcmp(argv[1],"-e")==0)
129 			prdata.raw = 0;
130 		else if(strcmp(argv[1],"-ne")==0 || strcmp(argv[1],"-en")==0)
131 		{
132 			prdata.raw = 0;
133 			prdata.echon = 1;
134 		}
135 #endif /* SHOPT_ECHOE */
136 		else
137 			break;
138 		argv++;
139 	}
140 	return(b_print(0,argv,(Shbltin_t*)&prdata));
141 }
142 
b_printf(int argc,char * argv[],Shbltin_t * context)143 int    b_printf(int argc, char *argv[],Shbltin_t *context)
144 {
145 	struct print prdata;
146 	NOT_USED(argc);
147 	memset(&prdata,0,sizeof(prdata));
148 	prdata.sh = context->shp;
149 	prdata.options = sh_optprintf;
150 	return(b_print(-1,argv,(Shbltin_t*)&prdata));
151 }
152 
infof(Opt_t * op,Sfio_t * sp,const char * s,Optdisc_t * dp)153 static int infof(Opt_t* op, Sfio_t* sp, const char* s, Optdisc_t* dp)
154 {
155 	const struct printmap *pm;
156 	char c='%';
157 	for(pm=Pmap;pm->size>0;pm++)
158 		sfprintf(sp, "[+%c(%s)q?Equivalent to %%%s.]",c,pm->name,pm->map);
159 	return(1);
160 }
161 
162 /*
163  * argc==0 when called from echo
164  * argc==-1 when called from printf
165  */
166 
b_print(int argc,char * argv[],Shbltin_t * context)167 int    b_print(int argc, char *argv[], Shbltin_t *context)
168 {
169 	register Sfio_t *outfile;
170 	register int exitval=0,n, fd = 1;
171 	register Shell_t *shp = context->shp;
172 	const char *options, *msg = e_file+4;
173 	char *format = 0, *fmttype=0;
174 	int sflag = 0, nflag=0, rflag=0, vflag=0;
175 	Namval_t *vname=0;
176 	Optdisc_t disc;
177 	disc.version = OPT_VERSION;
178 	disc.infof = infof;
179 	opt_info.disc = &disc;
180 	if(argc>0)
181 	{
182 		options = sh_optprint;
183 		nflag = rflag = 0;
184 		format = 0;
185 	}
186 	else
187 	{
188 		struct print *pp = (struct print*)context;
189 		shp = pp->sh;
190 		options = pp->options;
191 		if(argc==0)
192 		{
193 			nflag = pp->echon;
194 			rflag = pp->raw;
195 			argv++;
196 			goto skip;
197 		}
198 	}
199 	while((n = optget(argv,options))) switch(n)
200 	{
201 		case 'n':
202 			nflag++;
203 			break;
204 		case 'p':
205 			fd = shp->coutpipe;
206 			msg = e_query;
207 			break;
208 		case 'f':
209 			format = opt_info.arg;
210 			break;
211 		case 's':
212 			/* print to history file */
213 			if(!sh_histinit((void*)shp))
214 				errormsg(SH_DICT,ERROR_system(1),e_history);
215 			outfile = shp->gd->hist_ptr->histfp;
216 			fd = sffileno(outfile);
217 			sh_onstate(shp,SH_HISTORY);
218 			sflag++;
219 			break;
220 		case 'e':
221 			rflag = 0;
222 			break;
223 		case 'r':
224 			rflag = 1;
225 			break;
226 		case 'u':
227 			if(opt_info.arg[0]=='p' && opt_info.arg[1]==0)
228 			{
229 				fd = shp->coutpipe;
230 				msg = e_query;
231 				break;
232 			}
233 			fd = (int)strtol(opt_info.arg,&opt_info.arg,10);
234 			if(*opt_info.arg)
235 				fd = -1;
236 			else if(!sh_iovalidfd(shp,fd))
237 				fd = -1;
238 			else if(!(shp->inuse_bits&(1<<fd)) && (sh_inuse(shp,fd) || (shp->gd->hist_ptr && fd==sffileno(shp->gd->hist_ptr->histfp))))
239 
240 				fd = -1;
241 			break;
242 		case 'j':
243 			fmttype = "json";
244 		case 'v':
245 			if(argc < 0)
246 			{
247 				if(!(vname = nv_open(opt_info.arg, shp->var_tree,NV_VARNAME|NV_NOARRAY)))
248 					errormsg(SH_DICT,2, "Cannot create variable %s", opt_info.arg);
249 			}
250 			else
251 				vflag='v';
252 			break;
253 		case 'C':
254 			vflag='C';
255 			break;
256 		case ':':
257 			/* The following is for backward compatibility */
258 #if OPT_VERSION >= 19990123
259 			if(strcmp(opt_info.name,"-R")==0)
260 #else
261 			if(strcmp(opt_info.option,"-R")==0)
262 #endif
263 			{
264 				rflag = 1;
265 				if(error_info.errors==0)
266 				{
267 					argv += opt_info.index+1;
268 					/* special case test for -Rn */
269 					if(strchr(argv[-1],'n'))
270 						nflag++;
271 					if(*argv && strcmp(*argv,"-n")==0)
272 					{
273 
274 						nflag++;
275 						argv++;
276 					}
277 					goto skip2;
278 				}
279 			}
280 			else
281 				errormsg(SH_DICT,2, "%s", opt_info.arg);
282 			break;
283 		case '?':
284 			errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg);
285 			break;
286 	}
287 	argv += opt_info.index;
288 	if(error_info.errors || (argc<0 && !(format = *argv++)))
289 		errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0));
290 	if(vflag && format)
291 		errormsg(SH_DICT,ERROR_usage(2),"-%c and -f are mutually exclusive",vflag);
292 skip:
293 	if(format)
294 		format = genformat(shp,format);
295 	/* handle special case of '-' operand for print */
296 	if(argc>0 && *argv && strcmp(*argv,"-")==0 && strcmp(argv[-1],"--"))
297 		argv++;
298 	if(vname)
299 	{
300 		if(!shp->strbuf2)
301 			shp->strbuf2 = sfstropen();
302 		outfile = shp->strbuf2;
303 		goto printv;
304 	}
305 skip2:
306 	if(fd < 0)
307 	{
308 		errno = EBADF;
309 		n = 0;
310 	}
311 	else if(sflag)
312 		n = IOREAD|IOWRITE|IOSEEK;
313 	else if(!(n=shp->fdstatus[fd]))
314 		n = sh_iocheckfd(shp,fd,fd);
315 	if(!(n&IOWRITE))
316 	{
317 		/* don't print error message for stdout for compatibility */
318 		if(fd==1)
319 			return(1);
320 		errormsg(SH_DICT,ERROR_system(1),msg);
321 	}
322 	if(!sflag && !(outfile=shp->sftable[fd]))
323 	{
324 		sh_onstate(shp,SH_NOTRACK);
325 		n = SF_WRITE|((n&IOREAD)?SF_READ:0);
326 		shp->sftable[fd] = outfile = sfnew(NIL(Sfio_t*),shp->outbuff,IOBSIZE,fd,n);
327 		sh_offstate(shp,SH_NOTRACK);
328 		sfpool(outfile,shp->outpool,SF_WRITE);
329 	}
330 	/* turn off share to guarantee atomic writes for printf */
331 	n = sfset(outfile,SF_SHARE|SF_PUBLIC,0);
332 printv:
333 	if(format)
334 	{
335 		/* printf style print */
336 		Sfio_t *pool;
337 		struct printf pdata;
338 		memset(&pdata, 0, sizeof(pdata));
339 		pdata.sh = shp;
340 		pdata.hdr.version = SFIO_VERSION;
341 		pdata.hdr.extf = extend;
342 		pdata.nextarg = argv;
343 		sh_offstate(shp,SH_STOPOK);
344 		pool=sfpool(sfstderr,NIL(Sfio_t*),SF_WRITE);
345 		do
346 		{
347 			if(shp->trapnote&SH_SIGSET)
348 				break;
349 			pdata.hdr.form = format;
350 			sfprintf(outfile,"%!",&pdata);
351 		} while(*pdata.nextarg && pdata.nextarg!=argv);
352 		if(pdata.nextarg == nullarg && pdata.argsize>0)
353 			sfwrite(outfile,stkptr(shp->stk,stktell(shp->stk)),pdata.argsize);
354 		if(sffileno(outfile)!=sffileno(sfstderr))
355 			sfsync(outfile);
356 		sfpool(sfstderr,pool,SF_WRITE);
357 		exitval = pdata.err;
358 	}
359 	else if(vflag)
360 	{
361 		while(*argv)
362 		{
363 			fmtbase64(shp,outfile,*argv++,fmttype,vflag=='C');
364 			if(!nflag)
365 				sfputc(outfile,'\n');
366 		}
367 	}
368 	else
369 	{
370 		/* echo style print */
371 		if(nflag && !argv[0])
372 			sfsync((Sfio_t*)0);
373 		else if(sh_echolist(shp,outfile,rflag,argv) && !nflag)
374 			sfputc(outfile,'\n');
375 	}
376 	if(vname)
377 		nv_putval(vname, sfstruse(outfile),0);
378 	else if(sflag)
379 	{
380 		hist_flush(shp->gd->hist_ptr);
381 		sh_offstate(shp,SH_HISTORY);
382 	}
383 	else if(n&SF_SHARE)
384 	{
385 		sfset(outfile,SF_SHARE|SF_PUBLIC,1);
386 		sfsync(outfile);
387 	}
388 	return(exitval);
389 }
390 
391 /*
392  * echo the argument list onto <outfile>
393  * if <raw> is non-zero then \ is not a special character.
394  * returns 0 for \c otherwise 1.
395  */
396 
sh_echolist(Shell_t * shp,Sfio_t * outfile,int raw,char * argv[])397 int sh_echolist(Shell_t *shp,Sfio_t *outfile, int raw, char *argv[])
398 {
399 	register char	*cp;
400 	register int	n;
401 	struct printf pdata;
402 	pdata.cescape = 0;
403 	pdata.err = 0;
404 	while(!pdata.cescape && (cp= *argv++))
405 	{
406 		if(!raw  && (n=fmtvecho(shp,cp,&pdata))>=0)
407 		{
408 			if(n)
409 				sfwrite(outfile,stkptr(shp->stk,stktell(shp->stk)),n);
410 		}
411 		else
412 			sfputr(outfile,cp,-1);
413 		if(*argv)
414 			sfputc(outfile,' ');
415 		sh_sigcheck(shp);
416 	}
417 	return(!pdata.cescape);
418 }
419 
420 /*
421  * modified version of stresc for generating formats
422  */
strformat(char * s)423 static char strformat(char *s)
424 {
425         register char*  t;
426         register int    c;
427         char*           b;
428         char*           p;
429 #if SHOPT_MULTIBYTE && defined(FMT_EXP_WIDE)
430 	int		w;
431 #endif
432 
433         b = t = s;
434         for (;;)
435         {
436                 switch (c = *s++)
437                 {
438                     case '\\':
439 			if(*s==0)
440 				break;
441 #if SHOPT_MULTIBYTE && defined(FMT_EXP_WIDE)
442                         c = chrexp(s - 1, &p, &w, FMT_EXP_CHAR|FMT_EXP_LINE|FMT_EXP_WIDE);
443 #else
444                         c = chresc(s - 1, &p);
445 #endif
446                         s = p;
447 #if SHOPT_MULTIBYTE
448 #if defined(FMT_EXP_WIDE)
449 			if(c<0) /* conversion failed => empty string */
450 				continue;
451 			if(w)
452 			{
453 				t += mbconv(t, c);
454 				continue;
455 			}
456 #else
457 			if(c>UCHAR_MAX && mbwide())
458 			{
459 				t += mbconv(t, c);
460 				continue;
461 			}
462 #endif /* FMT_EXP_WIDE */
463 #endif /* SHOPT_MULTIBYTE */
464 			if(c=='%')
465 				*t++ = '%';
466 			else if(c==0)
467 			{
468 				*t++ = '%';
469 				c = 'Z';
470 			}
471                         break;
472                     case 0:
473                         *t = 0;
474                         return(t - b);
475                 }
476                 *t++ = c;
477         }
478 }
479 
480 
genformat(Shell_t * shp,char * format)481 static char *genformat(Shell_t * shp,char *format)
482 {
483 	register char *fp;
484 	stkseek(shp->stk,0);
485 	sfputr(shp->stk,preformat,-1);
486 	sfputr(shp->stk,format,-1);
487 	fp = (char*)stkfreeze(shp->stk,1);
488 	strformat(fp+sizeof(preformat)-1);
489 	return(fp);
490 }
491 
fmthtml(Shell_t * shp,const char * string,int flags)492 static char *fmthtml(Shell_t *shp,const char *string, int flags)
493 {
494 	register const char *cp = string;
495 	register int c, offset = stktell(shp->stk);
496 	if(!(flags&SFFMT_ALTER))
497 	{
498 		while(c= *(unsigned char*)cp++)
499 		{
500 #if SHOPT_MULTIBYTE
501 			register int s;
502 			if((s=mbsize(cp-1)) > 1)
503 			{
504 				cp += (s-1);
505 				continue;
506 			}
507 #endif /* SHOPT_MULTIBYTE */
508 			if(c=='<')
509 				sfputr(shp->stk,"&lt",';');
510 			else if(c=='>')
511 				sfputr(shp->stk,"&gt",';');
512 			else if(c=='&')
513 				sfputr(shp->stk,"&amp",';');
514 			else if(c=='"')
515 				sfputr(shp->stk,"&quot",';');
516 			else if(c=='\'')
517 				sfputr(shp->stk,"&apos",';');
518 			else if(c==' ')
519 				sfputr(shp->stk,"&nbsp",';');
520 			else if(!isprint(c) && c!='\n' && c!='\r')
521 				sfprintf(shp->stk,"&#%X;",CCMAPC(c,CC_NATIVE,CC_ASCII));
522 			else
523 				sfputc(shp->stk,c);
524 		}
525 	}
526 	else
527 	{
528 		while(c= *(unsigned char*)cp++)
529 		{
530 			if(strchr("!*'();@&+$,#[]<>~.\"{}|\\-`^% ",c) || (!isprint(c) && c!='\n' && c!='\r'))
531 				sfprintf(stkstd,"%%%02X",CCMAPC(c,CC_NATIVE,CC_ASCII));
532 			else
533 				sfputc(shp->stk,c);
534 		}
535 	}
536 	sfputc(shp->stk,0);
537 	return(stkptr(shp->stk,offset));
538 }
539 
fmtbase64(Shell_t * shp,Sfio_t * iop,char * string,const char * fmt,int alt)540 static ssize_t fmtbase64(Shell_t *shp, Sfio_t *iop, char *string, const char *fmt,int alt)
541 {
542 	char			*cp;
543 	Sfdouble_t		d;
544 	ssize_t			size;
545 	Namval_t		*np = nv_open(string, shp->var_tree, NV_VARNAME|NV_NOASSIGN|NV_NOADD);
546 	Namarr_t		*ap;
547 	static union types_t	number;
548 	if(!np || nv_isnull(np))
549 	{
550 		if(sh_isoption(shp,SH_NOUNSET))
551 			errormsg(SH_DICT,ERROR_exit(1),e_notset,string);
552 		return(0);
553 	}
554 	if(nv_isattr(np,NV_INTEGER) && !nv_isarray(np))
555 	{
556 		d = nv_getnum(np);
557 		if(nv_isattr(np,NV_DOUBLE))
558 		{
559 			if(nv_isattr(np,NV_LONG))
560 			{
561 				size = sizeof(Sfdouble_t);
562 				number.ld = d;
563 			}
564 			else if(nv_isattr(np,NV_SHORT))
565 			{
566 				size = sizeof(float);
567 				number.f = (float)d;
568 			}
569 			else
570 			{
571 				size = sizeof(double);
572 				number.d = (double)d;
573 			}
574 		}
575 		else
576 		{
577 			if(nv_isattr(np,NV_LONG))
578 			{
579 				size =  sizeof(Sflong_t);
580 				number.ll = (Sflong_t)d;
581 			}
582 			else if(nv_isattr(np,NV_SHORT))
583 			{
584 				size =  sizeof(short);
585 				number.h = (short)d;
586 			}
587 			else
588 			{
589 				size =  sizeof(short);
590 				number.i = (int)d;
591 			}
592 		}
593 #if 1
594 		return(sfwrite(iop, (void*)&number, size));
595 #else
596 		if(sz)
597 			*sz = size;
598 		return((void*)&number);
599 #endif
600 	}
601 	if(nv_isattr(np,NV_BINARY))
602 #if 1
603 	{
604 		Namfun_t *fp;
605 		for(fp=np->nvfun; fp;fp=fp->next)
606 		{
607 			if(fp->disc && fp->disc->writef)
608 				break;
609 		}
610 		if(fp)
611 			return (*fp->disc->writef)(np, iop, 0, fp);
612 		else
613 		{
614 			int n = nv_size(np);
615 			if(nv_isarray(np))
616 			{
617 				nv_onattr(np,NV_RAW);
618 				cp = nv_getval(np);
619 				nv_offattr(np,NV_RAW);
620 			}
621 			else
622 				cp = (char*)np->nvalue.cp;
623 			if((size = n)==0)
624 				size = strlen(cp);
625 			size = sfwrite(iop, cp, size);
626 			return(n?n:size);
627 		}
628 	}
629 	else if(nv_isarray(np) && (ap=nv_arrayptr(np)) && array_elem(ap) && (ap->flags&(ARRAY_UNDEF|ARRAY_SCAN)))
630 	{
631 		Namval_t *nspace = shp->namespace;
632 		if(*string=='.' && memcmp(string,".sh.",4))
633 			shp->namespace = shp->last_table;
634 		nv_outnode(np,iop,(alt?-1:0),0);
635 		sfputc(iop,')');
636 		shp->namespace = nspace;
637 		return(sftell(iop));
638 	}
639 	else
640 	{
641 		Namval_t *nspace = shp->namespace;
642 		if(alt==1 && nv_isvtree(np))
643 			nv_onattr(np,NV_EXPORT);
644 		if(fmt && memcmp(fmt,"json",4)==0)
645 			nv_onattr(np,NV_JSON);
646 		if(*string=='.')
647 			shp->namespace = 0;
648 		cp = nv_getval(np);
649 		if(*string=='.')
650 			shp->namespace = nspace;
651 		if(alt==1)
652 			nv_offattr(np,NV_EXPORT);
653 		else if(fmt && memcmp(fmt,"json",4)==0)
654 			nv_offattr(np,NV_JSON);
655 		if(!cp)
656 			return(0);
657 		size = strlen(cp);
658 		return(sfwrite(iop,cp,size));
659 	}
660 #else
661 		nv_onattr(np,NV_RAW);
662 	cp = nv_getval(np);
663 	if(nv_isattr(np,NV_BINARY))
664 		nv_offattr(np,NV_RAW);
665 	if((size = nv_size(np))==0)
666 		size = strlen(cp);
667 	if(sz)
668 		*sz = size;
669 	return((void*)cp);
670 #endif
671 }
672 
varname(const char * str,ssize_t n)673 static int varname(const char *str, ssize_t n)
674 {
675 	register int c,dot=1,len=1;
676 	if(n < 0)
677 	{
678 		if(*str=='.')
679 			str++;
680 		n = strlen(str);
681 	}
682 	for(;n > 0; n-=len)
683 	{
684 #ifdef SHOPT_MULTIBYTE
685 		len = mbsize(str);
686 		c = mbchar(str);
687 #else
688 		c = *(unsigned char*)str++;
689 #endif
690 		if(dot && !(isalpha(c)||c=='_'))
691 			break;
692 		else if(dot==0 && !(isalnum(c) || c=='_' || c == '.'))
693 			break;
694 		dot = (c=='.');
695 	}
696 	return(n==0);
697 }
698 
mapformat(Sffmt_t * fe)699 static const char *mapformat(Sffmt_t *fe)
700 {
701 	const struct printmap *pm = Pmap;
702 	while(pm->size>0)
703 	{
704 		if(pm->size==fe->n_str && memcmp(pm->name,fe->t_str,fe->n_str)==0)
705 			return(pm->map);
706 		pm++;
707 	}
708 	return(0);
709 }
710 
extend(Sfio_t * sp,void * v,Sffmt_t * fe)711 static int extend(Sfio_t* sp, void* v, Sffmt_t* fe)
712 {
713 	char*		lastchar = "";
714 	register int	neg = 0;
715 	Sfdouble_t	d;
716 	Sfdouble_t	longmin = LDBL_LLONG_MIN;
717 	Sfdouble_t	longmax = LDBL_LLONG_MAX;
718 	int		format = fe->fmt;
719 	int		n;
720 	int		fold = fe->base;
721 	union types_t*	value = (union types_t*)v;
722 	struct printf*	pp = (struct printf*)fe;
723 	Shell_t		*shp = pp->sh;
724 	register char*	argp = *pp->nextarg;
725 	char		*w,*s;
726 
727 	if(fe->n_str>0 && (format=='T'||format=='Q') && varname(fe->t_str,fe->n_str) && (!argp || varname(argp,-1)))
728 	{
729 		if(argp)
730 			pp->lastarg = argp;
731 		else
732 			argp = pp->lastarg;
733 		if(argp)
734 		{
735 			sfprintf(pp->sh->strbuf,"%s.%.*s%c",argp,fe->n_str,fe->t_str,0);
736 			argp = sfstruse(pp->sh->strbuf);
737 		}
738 	}
739 	else
740 		pp->lastarg = 0;
741 	fe->flags |= SFFMT_VALUE;
742 	if(!argp || format=='Z')
743 	{
744 		switch(format)
745 		{
746 		case 'c':
747 			value->c = 0;
748 			fe->flags &= ~SFFMT_LONG;
749 			break;
750 		case 'q':
751 			format = 's';
752 			/* FALL THROUGH */
753 		case 's':
754 		case 'H':
755 		case 'B':
756 		case 'P':
757 		case 'R':
758 		case 'Z':
759 		case 'b':
760 			fe->fmt = 's';
761 			fe->size = -1;
762 			fe->base = -1;
763 			value->s = "";
764 			fe->flags &= ~SFFMT_LONG;
765 			break;
766 		case 'a':
767 		case 'e':
768 		case 'f':
769 		case 'g':
770 		case 'A':
771 		case 'E':
772 		case 'F':
773 		case 'G':
774                         if(SFFMT_LDOUBLE)
775 				value->ld = 0.;
776 			else
777 				value->d = 0.;
778 			break;
779 		case 'n':
780 			value->ip = &pp->intvar;
781 			break;
782 		case 'Q':
783 			value->ll = 0;
784 			break;
785 		case 'T':
786 			fe->fmt = 'd';
787 			value->ll = tmxgettime();
788 			break;
789 		default:
790 			if(!strchr("DdXxoUu",format))
791 				errormsg(SH_DICT,ERROR_exit(1),e_formspec,format);
792 			fe->fmt = 'd';
793 			value->ll = 0;
794 			break;
795 		}
796 	}
797 	else
798 	{
799 		switch(format)
800 		{
801 		case 'p':
802 			value->p = (char**)strtol(argp,&lastchar,10);
803 			break;
804 		case 'n':
805 		{
806 			Namval_t *np;
807 			np = nv_open(argp,shp->var_tree,NV_VARNAME|NV_NOASSIGN|NV_NOARRAY);
808 			_nv_unset(np,0);
809 			nv_onattr(np,NV_INTEGER);
810 			if (np->nvalue.lp = new_of(int32_t,0))
811 				*np->nvalue.lp = 0;
812 			nv_setsize(np,10);
813 			if(sizeof(int)==sizeof(int32_t))
814 				value->ip = (int*)np->nvalue.lp;
815 			else
816 			{
817 				int32_t sl = 1;
818 				value->ip = (int*)(((char*)np->nvalue.lp) + (*((char*)&sl) ? 0 : sizeof(int)));
819 			}
820 			nv_close(np);
821 			break;
822 		}
823 		case 'q':
824 			if(fe->n_str)
825 			{
826 				const char *fp = mapformat(fe);
827 				if(fp)
828 				{
829 					if (!isalpha(*fp))
830 						switch (*fp++)
831 						{
832 						case '#':
833 							fe->flags |= SFFMT_ALTER;
834 							break;
835 						case '+':
836 							fe->flags |= SFFMT_SIGN;
837 							break;
838 						case '0':
839 							fe->flags |= SFFMT_ZERO;
840 							break;
841 						}
842 					format = *fp;
843 				}
844 			}
845 		case 'b':
846 		case 's':
847 		case 'B':
848 		case 'H':
849 		case 'P':
850 		case 'R':
851 			fe->fmt = 's';
852 			fe->size = -1;
853 			if(format=='s' && fe->base>=0)
854 			{
855 				value->p = pp->nextarg;
856 				pp->nextarg = nullarg;
857 			}
858 			else
859 			{
860 				fe->base = -1;
861 				value->s = argp;
862 			}
863 			fe->flags &= ~SFFMT_LONG;
864 			break;
865 		case 'c':
866 			if(mbwide() && (n = mbsize(argp)) > 1)
867 			{
868 				fe->fmt = 's';
869 				fe->size = n;
870 				value->s = argp;
871 			}
872 			else if(fe->base >=0)
873 				value->s = argp;
874 			else
875 				value->c = *argp;
876 			fe->flags &= ~SFFMT_LONG;
877 			break;
878 		case 'o':
879 		case 'x':
880 		case 'X':
881 		case 'u':
882 		case 'U':
883 			longmax = LDBL_ULLONG_MAX;
884 		case '.':
885 			if(fe->size==2 && strchr("bcsqHPRQTZ",*fe->form))
886 			{
887 				value->ll = ((unsigned char*)argp)[0];
888 				break;
889 			}
890 		case 'd':
891 		case 'D':
892 		case 'i':
893 			switch(*argp)
894 			{
895 			case '\'':
896 			case '"':
897 				w = argp + 1;
898 				if(mbwide() && mbsize(w) > 1)
899 					value->ll = mbchar(w);
900 				else
901 					value->ll = *(unsigned char*)w++;
902 				if(w[0] && (w[0] != argp[0] || w[1]))
903 				{
904 					errormsg(SH_DICT,ERROR_warn(0),e_charconst,argp);
905 					pp->err = 1;
906 				}
907 				break;
908 			default:
909 				d = sh_strnum(shp,argp,&lastchar,0);
910 				if(d<longmin)
911 				{
912 					errormsg(SH_DICT,ERROR_warn(0),e_overflow,argp);
913 					pp->err = 1;
914 					d = longmin;
915 				}
916 				else if(d>longmax)
917 				{
918 					errormsg(SH_DICT,ERROR_warn(0),e_overflow,argp);
919 					pp->err = 1;
920 					d = longmax;
921 				}
922 				value->ll = (Sflong_t)d;
923 				if(lastchar == *pp->nextarg)
924 				{
925 					value->ll = *argp;
926 					lastchar = "";
927 				}
928 				break;
929 			}
930 			if(neg)
931 				value->ll = -value->ll;
932 			fe->size = sizeof(value->ll);
933 			break;
934 		case 'a':
935 		case 'e':
936 		case 'f':
937 		case 'g':
938 		case 'A':
939 		case 'E':
940 		case 'F':
941 		case 'G':
942 			d = sh_strnum(shp,*pp->nextarg,&lastchar,0);
943 			switch(*argp)
944 			{
945 			    case '\'':
946 			    case '"':
947 				d = ((unsigned char*)argp)[1];
948 				if(argp[2] && (argp[2] != argp[0] || argp[3]))
949 				{
950 					errormsg(SH_DICT,ERROR_warn(0),e_charconst,argp);
951 					pp->err = 1;
952 				}
953 				break;
954 			    default:
955 				d = sh_strnum(shp,*pp->nextarg,&lastchar,0);
956 				break;
957 			}
958                         if(SFFMT_LDOUBLE)
959 			{
960 				value->ld = d;
961 				fe->size = sizeof(value->ld);
962 			}
963 			else
964 			{
965 				value->d = d;
966 				fe->size = sizeof(value->d);
967 			}
968 			break;
969 		case 'Q':
970 			value->ll = (Sflong_t)strelapsed(*pp->nextarg,&lastchar,1);
971 			break;
972 		case 'T':
973 			value->ll = (Sflong_t)tmxdate(*pp->nextarg,&lastchar,TMX_NOW);
974 			break;
975 		default:
976 			value->ll = 0;
977 			fe->fmt = 'd';
978 			fe->size = sizeof(value->ll);
979 			errormsg(SH_DICT,ERROR_exit(1),e_formspec,format);
980 			break;
981 		}
982 		if (format == '.')
983 			value->i = value->ll;
984 		if(*lastchar)
985 		{
986 			errormsg(SH_DICT,ERROR_warn(0),e_argtype,format);
987 			pp->err = 1;
988 		}
989 		pp->nextarg++;
990 	}
991 	switch(format)
992 	{
993 	case 'Z':
994 		fe->fmt = 'c';
995 		fe->base = -1;
996 		value->c = 0;
997 		break;
998 	case 'b':
999 		if((n=fmtvecho(shp,value->s,pp))>=0)
1000 		{
1001 			if(pp->nextarg == nullarg)
1002 			{
1003 				pp->argsize = n;
1004 				return -1;
1005 			}
1006 			value->s = stkptr(shp->stk,stktell(shp->stk));
1007 			fe->size = n;
1008 		}
1009 		break;
1010 	case 'B':
1011 		if(!shp->strbuf2)
1012 			shp->strbuf2 = sfstropen();
1013 		fe->size = fmtbase64(shp,shp->strbuf2,value->s, fe->n_str?fe->t_str:0, (fe->flags&SFFMT_ALTER)!=0);
1014 		value->s = sfstruse(shp->strbuf2);
1015 		fe->flags |= SFFMT_SHORT;
1016 		break;
1017 	case 'H':
1018 		value->s = fmthtml(shp,value->s, fe->flags);
1019 		break;
1020 	case 'q':
1021 		value->s = sh_fmtqf(value->s, fe->flags, fold);
1022 		break;
1023 	case 'P':
1024 		s = fmtmatch(value->s);
1025 		if(!s || *s==0)
1026 			errormsg(SH_DICT,ERROR_exit(1),e_badregexp,value->s);
1027 		value->s = s;
1028 		break;
1029 	case 'R':
1030 		s = fmtre(value->s);
1031 		if(!s || *s==0)
1032 			errormsg(SH_DICT,ERROR_exit(1),e_badregexp,value->s);
1033 		value->s = s;
1034 		break;
1035 	case 'Q':
1036 		if (fe->n_str>0)
1037 		{
1038 			fe->fmt = 'd';
1039 			fe->size = sizeof(value->ll);
1040 		}
1041 		else
1042 		{
1043 			value->s = fmtelapsed(value->ll, 1);
1044 			fe->fmt = 's';
1045 			fe->size = -1;
1046 		}
1047 		break;
1048 	case 'T':
1049 		if(fe->n_str>0)
1050 		{
1051 			n = fe->t_str[fe->n_str];
1052 			fe->t_str[fe->n_str] = 0;
1053 			value->s = fmttmx(fe->t_str, value->ll);
1054 			fe->t_str[fe->n_str] = n;
1055 		}
1056 		else value->s = fmttmx(NIL(char*), value->ll);
1057 		fe->fmt = 's';
1058 		fe->size = -1;
1059 		break;
1060 	}
1061 	return 0;
1062 }
1063 
1064 /*
1065  * construct System V echo string out of <cp>
1066  * If there are not escape sequences, returns -1
1067  * Otherwise, puts null terminated result on stack, but doesn't freeze it
1068  * returns length of output.
1069  */
1070 
fmtvecho(Shell_t * shp,const char * string,struct printf * pp)1071 static int fmtvecho(Shell_t *shp,const char *string, struct printf *pp)
1072 {
1073 	register const char *cp = string, *cpmax;
1074 	register int c;
1075 	register int offset = stktell(shp->stk);
1076 #if SHOPT_MULTIBYTE
1077 	int chlen;
1078 	if(mbwide())
1079 	{
1080 		while(1)
1081 		{
1082 			if ((chlen = mbsize(cp)) > 1)
1083 				/* Skip over multibyte characters */
1084 				cp += chlen;
1085 			else if((c= *cp++)==0 || c == '\\')
1086 				break;
1087 		}
1088 	}
1089 	else
1090 #endif /* SHOPT_MULTIBYTE */
1091 	while((c= *cp++) && (c!='\\'));
1092 	if(c==0)
1093 		return(-1);
1094 	c = --cp - string;
1095 	if(c>0)
1096 		sfwrite(shp->stk,(void*)string,c);
1097 	for(; c= *cp; cp++)
1098 	{
1099 #if SHOPT_MULTIBYTE
1100 		if (mbwide() && ((chlen = mbsize(cp)) > 1))
1101 		{
1102 			sfwrite(shp->stk,cp,chlen);
1103 			cp +=  (chlen-1);
1104 			continue;
1105 		}
1106 #endif /* SHOPT_MULTIBYTE */
1107 		if( c=='\\') switch(*++cp)
1108 		{
1109 			case 'E':
1110 				c = ('a'==97?'\033':39); /* ASCII/EBCDIC */
1111 				break;
1112 			case 'a':
1113 				c = '\a';
1114 				break;
1115 			case 'b':
1116 				c = '\b';
1117 				break;
1118 			case 'c':
1119 				pp->cescape++;
1120 				pp->nextarg = nullarg;
1121 				goto done;
1122 			case 'f':
1123 				c = '\f';
1124 				break;
1125 			case 'n':
1126 				c = '\n';
1127 				break;
1128 			case 'r':
1129 				c = '\r';
1130 				break;
1131 			case 'v':
1132 				c = '\v';
1133 				break;
1134 			case 't':
1135 				c = '\t';
1136 				break;
1137 			case '\\':
1138 				c = '\\';
1139 				break;
1140 			case '0':
1141 				c = 0;
1142 				cpmax = cp + 4;
1143 				while(++cp<cpmax && *cp>='0' && *cp<='7')
1144 				{
1145 					c <<= 3;
1146 					c |= (*cp-'0');
1147 				}
1148 			default:
1149 				cp--;
1150 		}
1151 		sfputc(shp->stk,c);
1152 	}
1153 done:
1154 	c = stktell(shp->stk)-offset;
1155 	sfputc(shp->stk,0);
1156 	stkseek(shp->stk,offset);
1157 	return(c);
1158 }
1159