Home | History | Annotate | Download | only in bltins
      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 (at) 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