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