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