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