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