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