1da2e3ebdSchin /***********************************************************************
2da2e3ebdSchin *                                                                      *
3da2e3ebdSchin *               This software is part of the ast package               *
4b30d1939SAndy Fiddaman *          Copyright (c) 1982-2012 AT&T Intellectual Property          *
5da2e3ebdSchin *                      and is licensed under the                       *
6b30d1939SAndy Fiddaman *                 Eclipse Public License, Version 1.0                  *
77c2fbfb3SApril Chin *                    by AT&T Intellectual Property                     *
8da2e3ebdSchin *                                                                      *
9da2e3ebdSchin *                A copy of the License is available at                 *
10b30d1939SAndy Fiddaman *          http://www.eclipse.org/org/documents/epl-v10.html           *
11b30d1939SAndy Fiddaman *         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
12da2e3ebdSchin *                                                                      *
13da2e3ebdSchin *              Information and Software Systems Research               *
14da2e3ebdSchin *                            AT&T Research                             *
15da2e3ebdSchin *                           Florham Park NJ                            *
16da2e3ebdSchin *                                                                      *
17da2e3ebdSchin *                  David Korn <dgk@research.att.com>                   *
18da2e3ebdSchin *                                                                      *
19da2e3ebdSchin ***********************************************************************/
20da2e3ebdSchin #pragma prototyped
21da2e3ebdSchin /*
22da2e3ebdSchin  * echo [arg...]
23da2e3ebdSchin  * print [-nrps] [-f format] [-u filenum] [arg...]
24da2e3ebdSchin  * printf  format [arg...]
25da2e3ebdSchin  *
26da2e3ebdSchin  *   David Korn
27da2e3ebdSchin  *   AT&T Labs
28da2e3ebdSchin  */
29da2e3ebdSchin 
30da2e3ebdSchin #include	"defs.h"
31da2e3ebdSchin #include	<error.h>
32da2e3ebdSchin #include	<stak.h>
33da2e3ebdSchin #include	"io.h"
34da2e3ebdSchin #include	"name.h"
35da2e3ebdSchin #include	"history.h"
36da2e3ebdSchin #include	"builtins.h"
37da2e3ebdSchin #include	"streval.h"
38da2e3ebdSchin #include	<tmx.h>
39da2e3ebdSchin #include	<ccode.h>
40da2e3ebdSchin 
41da2e3ebdSchin union types_t
42da2e3ebdSchin {
43da2e3ebdSchin 	unsigned char	c;
44da2e3ebdSchin 	short		h;
45da2e3ebdSchin 	int		i;
46da2e3ebdSchin 	long		l;
47da2e3ebdSchin 	Sflong_t	ll;
48da2e3ebdSchin 	Sfdouble_t	ld;
49da2e3ebdSchin 	double		d;
50da2e3ebdSchin 	float		f;
51da2e3ebdSchin 	char		*s;
52da2e3ebdSchin 	int		*ip;
53da2e3ebdSchin 	char		**p;
54da2e3ebdSchin };
55da2e3ebdSchin 
56da2e3ebdSchin struct printf
57da2e3ebdSchin {
58da2e3ebdSchin 	Sffmt_t		hdr;
59da2e3ebdSchin 	int		argsize;
60da2e3ebdSchin 	int		intvar;
61da2e3ebdSchin 	char		**nextarg;
627c2fbfb3SApril Chin 	char		*lastarg;
63da2e3ebdSchin 	char		cescape;
64da2e3ebdSchin 	char		err;
65da2e3ebdSchin 	Shell_t		*sh;
66da2e3ebdSchin };
67da2e3ebdSchin 
68b30d1939SAndy Fiddaman struct printmap
69b30d1939SAndy Fiddaman {
70b30d1939SAndy Fiddaman 	size_t		size;
71b30d1939SAndy Fiddaman 	char		*name;
72b30d1939SAndy Fiddaman 	char		map[3];
73b30d1939SAndy Fiddaman 	const char	*description;
74b30d1939SAndy Fiddaman };
75b30d1939SAndy Fiddaman 
76b30d1939SAndy Fiddaman const struct printmap  Pmap[] =
77b30d1939SAndy Fiddaman {
78b30d1939SAndy Fiddaman 	3,	"csv",	"q+",	"Equivalent to %#q",
79b30d1939SAndy Fiddaman 	4,	"html",	"H",	"Equivalent to %H",
80b30d1939SAndy Fiddaman 	3,	"ere",	"R",	"Equivalent to %R",
81b30d1939SAndy Fiddaman 	7,	"pattern","P",	"Equivalent to %#P",
82b30d1939SAndy Fiddaman 	3,	"url",	"H+",	"Equivalent to %#H",
83b30d1939SAndy Fiddaman 	0,	0,	0,
84b30d1939SAndy Fiddaman };
85b30d1939SAndy Fiddaman 
86b30d1939SAndy Fiddaman 
87da2e3ebdSchin static int		extend(Sfio_t*,void*, Sffmt_t*);
88da2e3ebdSchin static const char   	preformat[] = "";
89da2e3ebdSchin static char		*genformat(char*);
90da2e3ebdSchin static int		fmtvecho(const char*, struct printf*);
917c2fbfb3SApril Chin static ssize_t		fmtbase64(Sfio_t*, char*, int);
92da2e3ebdSchin 
93da2e3ebdSchin struct print
94da2e3ebdSchin {
95da2e3ebdSchin 	Shell_t         *sh;
96da2e3ebdSchin 	const char	*options;
97da2e3ebdSchin 	char		raw;
98da2e3ebdSchin 	char		echon;
99da2e3ebdSchin };
100da2e3ebdSchin 
101da2e3ebdSchin static char* 	nullarg[] = { 0, 0 };
102da2e3ebdSchin 
103da2e3ebdSchin #if !SHOPT_ECHOPRINT
104b30d1939SAndy Fiddaman    int    B_echo(int argc, char *argv[],Shbltin_t *context)
105da2e3ebdSchin    {
106da2e3ebdSchin 	static char bsd_univ;
107da2e3ebdSchin 	struct print prdata;
108da2e3ebdSchin 	prdata.options = sh_optecho+5;
109da2e3ebdSchin 	prdata.raw = prdata.echon = 0;
110b30d1939SAndy Fiddaman 	prdata.sh = context->shp;
111da2e3ebdSchin 	NOT_USED(argc);
112da2e3ebdSchin 	/* This mess is because /bin/echo on BSD is different */
113da2e3ebdSchin 	if(!prdata.sh->universe)
114da2e3ebdSchin 	{
115da2e3ebdSchin 		register char *universe;
116da2e3ebdSchin 		if(universe=astconf("UNIVERSE",0,0))
117da2e3ebdSchin 			bsd_univ = (strcmp(universe,"ucb")==0);
118da2e3ebdSchin 		prdata.sh->universe = 1;
119da2e3ebdSchin 	}
120da2e3ebdSchin 	if(!bsd_univ)
121b30d1939SAndy Fiddaman 		return(b_print(0,argv,(Shbltin_t*)&prdata));
122da2e3ebdSchin 	prdata.options = sh_optecho;
123da2e3ebdSchin 	prdata.raw = 1;
124da2e3ebdSchin 	while(argv[1] && *argv[1]=='-')
125da2e3ebdSchin 	{
126da2e3ebdSchin 		if(strcmp(argv[1],"-n")==0)
127da2e3ebdSchin 			prdata.echon = 1;
128da2e3ebdSchin #if !SHOPT_ECHOE
129da2e3ebdSchin 		else if(strcmp(argv[1],"-e")==0)
130da2e3ebdSchin 			prdata.raw = 0;
131da2e3ebdSchin 		else if(strcmp(argv[1],"-ne")==0 || strcmp(argv[1],"-en")==0)
132da2e3ebdSchin 		{
133da2e3ebdSchin 			prdata.raw = 0;
134da2e3ebdSchin 			prdata.echon = 1;
135da2e3ebdSchin 		}
136da2e3ebdSchin #endif /* SHOPT_ECHOE */
137da2e3ebdSchin 		else
138da2e3ebdSchin 			break;
139da2e3ebdSchin 		argv++;
140da2e3ebdSchin 	}
141b30d1939SAndy Fiddaman 	return(b_print(0,argv,(Shbltin_t*)&prdata));
142da2e3ebdSchin    }
143da2e3ebdSchin #endif /* SHOPT_ECHOPRINT */
144da2e3ebdSchin 
145b30d1939SAndy Fiddaman int    b_printf(int argc, char *argv[],Shbltin_t *context)
146da2e3ebdSchin {
147da2e3ebdSchin 	struct print prdata;
148da2e3ebdSchin 	NOT_USED(argc);
149da2e3ebdSchin 	memset(&prdata,0,sizeof(prdata));
150b30d1939SAndy Fiddaman 	prdata.sh = context->shp;
151da2e3ebdSchin 	prdata.options = sh_optprintf;
152b30d1939SAndy Fiddaman 	return(b_print(-1,argv,(Shbltin_t*)&prdata));
153b30d1939SAndy Fiddaman }
154b30d1939SAndy Fiddaman 
155b30d1939SAndy Fiddaman static int infof(Opt_t* op, Sfio_t* sp, const char* s, Optdisc_t* dp)
156b30d1939SAndy Fiddaman {
157b30d1939SAndy Fiddaman 	const struct printmap *pm;
158b30d1939SAndy Fiddaman 	char c='%';
159b30d1939SAndy Fiddaman 	for(pm=Pmap;pm->size>0;pm++)
160b30d1939SAndy Fiddaman 	{
161b30d1939SAndy Fiddaman 		sfprintf(sp, "[+%c(%s)q?%s.]",c,pm->name,pm->description);
162b30d1939SAndy Fiddaman 	}
163b30d1939SAndy Fiddaman 	return(1);
164da2e3ebdSchin }
165da2e3ebdSchin 
166da2e3ebdSchin /*
167da2e3ebdSchin  * argc==0 when called from echo
168da2e3ebdSchin  * argc==-1 when called from printf
169da2e3ebdSchin  */
170da2e3ebdSchin 
171b30d1939SAndy Fiddaman int    b_print(int argc, char *argv[], Shbltin_t *context)
172da2e3ebdSchin {
173da2e3ebdSchin 	register Sfio_t *outfile;
174da2e3ebdSchin 	register int exitval=0,n, fd = 1;
175b30d1939SAndy Fiddaman 	register Shell_t *shp = context->shp;
176da2e3ebdSchin 	const char *options, *msg = e_file+4;
177da2e3ebdSchin 	char *format = 0;
1787c2fbfb3SApril Chin 	int sflag = 0, nflag=0, rflag=0, vflag=0;
179b30d1939SAndy Fiddaman 	Optdisc_t disc;
180b30d1939SAndy Fiddaman 	disc.version = OPT_VERSION;
181b30d1939SAndy Fiddaman 	disc.infof = infof;
182b30d1939SAndy Fiddaman 	opt_info.disc = &disc;
183da2e3ebdSchin 	if(argc>0)
184da2e3ebdSchin 	{
185da2e3ebdSchin 		options = sh_optprint;
186da2e3ebdSchin 		nflag = rflag = 0;
187da2e3ebdSchin 		format = 0;
188da2e3ebdSchin 	}
189da2e3ebdSchin 	else
190da2e3ebdSchin 	{
191b30d1939SAndy Fiddaman 		struct print *pp = (struct print*)context;
192da2e3ebdSchin 		shp = pp->sh;
193da2e3ebdSchin 		options = pp->options;
194da2e3ebdSchin 		if(argc==0)
195da2e3ebdSchin 		{
196da2e3ebdSchin 			nflag = pp->echon;
197da2e3ebdSchin 			rflag = pp->raw;
198da2e3ebdSchin 			argv++;
199da2e3ebdSchin 			goto skip;
200da2e3ebdSchin 		}
201*f9bbf53bSAndy Fiddaman 		argv++;
202*f9bbf53bSAndy Fiddaman 		goto printf;
203da2e3ebdSchin 	}
204da2e3ebdSchin 	while((n = optget(argv,options))) switch(n)
205da2e3ebdSchin 	{
206da2e3ebdSchin 		case 'n':
207da2e3ebdSchin 			nflag++;
208da2e3ebdSchin 			break;
209da2e3ebdSchin 		case 'p':
210da2e3ebdSchin 			fd = shp->coutpipe;
211da2e3ebdSchin 			msg = e_query;
212da2e3ebdSchin 			break;
213da2e3ebdSchin 		case 'f':
214da2e3ebdSchin 			format = opt_info.arg;
215da2e3ebdSchin 			break;
216da2e3ebdSchin 		case 's':
217da2e3ebdSchin 			/* print to history file */
2187c2fbfb3SApril Chin 			if(!sh_histinit((void*)shp))
219da2e3ebdSchin 				errormsg(SH_DICT,ERROR_system(1),e_history);
220b30d1939SAndy Fiddaman 			fd = sffileno(shp->gd->hist_ptr->histfp);
221da2e3ebdSchin 			sh_onstate(SH_HISTORY);
222da2e3ebdSchin 			sflag++;
223da2e3ebdSchin 			break;
224da2e3ebdSchin 		case 'e':
225da2e3ebdSchin 			rflag = 0;
226da2e3ebdSchin 			break;
227da2e3ebdSchin 		case 'r':
228da2e3ebdSchin 			rflag = 1;
229da2e3ebdSchin 			break;
230da2e3ebdSchin 		case 'u':
231da2e3ebdSchin 			fd = (int)strtol(opt_info.arg,&opt_info.arg,10);
232da2e3ebdSchin 			if(*opt_info.arg)
233da2e3ebdSchin 				fd = -1;
234b30d1939SAndy Fiddaman 			else if(!sh_iovalidfd(shp,fd))
235da2e3ebdSchin 				fd = -1;
236b30d1939SAndy Fiddaman 			else if(!(shp->inuse_bits&(1<<fd)) && (sh_inuse(shp,fd) || (shp->gd->hist_ptr && fd==sffileno(shp->gd->hist_ptr->histfp))))
237da2e3ebdSchin 
238da2e3ebdSchin 				fd = -1;
239da2e3ebdSchin 			break;
2407c2fbfb3SApril Chin 		case 'v':
24134f9b3eeSRoland Mainz 			vflag='v';
24234f9b3eeSRoland Mainz 			break;
24334f9b3eeSRoland Mainz 		case 'C':
24434f9b3eeSRoland Mainz 			vflag='C';
2457c2fbfb3SApril Chin 			break;
246da2e3ebdSchin 		case ':':
247da2e3ebdSchin 			/* The following is for backward compatibility */
248da2e3ebdSchin #if OPT_VERSION >= 19990123
249da2e3ebdSchin 			if(strcmp(opt_info.name,"-R")==0)
250da2e3ebdSchin #else
251da2e3ebdSchin 			if(strcmp(opt_info.option,"-R")==0)
252da2e3ebdSchin #endif
253da2e3ebdSchin 			{
254da2e3ebdSchin 				rflag = 1;
255da2e3ebdSchin 				if(error_info.errors==0)
256da2e3ebdSchin 				{
257da2e3ebdSchin 					argv += opt_info.index+1;
258da2e3ebdSchin 					/* special case test for -Rn */
259da2e3ebdSchin 					if(strchr(argv[-1],'n'))
260da2e3ebdSchin 						nflag++;
261da2e3ebdSchin 					if(*argv && strcmp(*argv,"-n")==0)
262da2e3ebdSchin 					{
263da2e3ebdSchin 
264da2e3ebdSchin 						nflag++;
265da2e3ebdSchin 						argv++;
266da2e3ebdSchin 					}
267da2e3ebdSchin 					goto skip2;
268da2e3ebdSchin 				}
269da2e3ebdSchin 			}
270da2e3ebdSchin 			else
271da2e3ebdSchin 				errormsg(SH_DICT,2, "%s", opt_info.arg);
272da2e3ebdSchin 			break;
273da2e3ebdSchin 		case '?':
274da2e3ebdSchin 			errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg);
275da2e3ebdSchin 			break;
276da2e3ebdSchin 	}
277da2e3ebdSchin 	argv += opt_info.index;
278*f9bbf53bSAndy Fiddaman printf:
279da2e3ebdSchin 	if(error_info.errors || (argc<0 && !(format = *argv++)))
280da2e3ebdSchin 		errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0));
2817c2fbfb3SApril Chin 	if(vflag && format)
28234f9b3eeSRoland Mainz 		errormsg(SH_DICT,ERROR_usage(2),"-%c and -f are mutually exclusive",vflag);
283da2e3ebdSchin skip:
284da2e3ebdSchin 	if(format)
285da2e3ebdSchin 		format = genformat(format);
286da2e3ebdSchin 	/* handle special case of '-' operand for print */
287da2e3ebdSchin 	if(argc>0 && *argv && strcmp(*argv,"-")==0 && strcmp(argv[-1],"--"))
288da2e3ebdSchin 		argv++;
289da2e3ebdSchin skip2:
290da2e3ebdSchin 	if(fd < 0)
291da2e3ebdSchin 	{
292da2e3ebdSchin 		errno = EBADF;
293da2e3ebdSchin 		n = 0;
294da2e3ebdSchin 	}
295da2e3ebdSchin 	else if(!(n=shp->fdstatus[fd]))
2967c2fbfb3SApril Chin 		n = sh_iocheckfd(shp,fd);
297da2e3ebdSchin 	if(!(n&IOWRITE))
298da2e3ebdSchin 	{
299da2e3ebdSchin 		/* don't print error message for stdout for compatibility */
300da2e3ebdSchin 		if(fd==1)
301da2e3ebdSchin 			return(1);
302da2e3ebdSchin 		errormsg(SH_DICT,ERROR_system(1),msg);
303da2e3ebdSchin 	}
304da2e3ebdSchin 	if(!(outfile=shp->sftable[fd]))
305da2e3ebdSchin 	{
306da2e3ebdSchin 		sh_onstate(SH_NOTRACK);
307da2e3ebdSchin 		n = SF_WRITE|((n&IOREAD)?SF_READ:0);
308da2e3ebdSchin 		shp->sftable[fd] = outfile = sfnew(NIL(Sfio_t*),shp->outbuff,IOBSIZE,fd,n);
309da2e3ebdSchin 		sh_offstate(SH_NOTRACK);
310da2e3ebdSchin 		sfpool(outfile,shp->outpool,SF_WRITE);
311da2e3ebdSchin 	}
312da2e3ebdSchin 	/* turn off share to guarantee atomic writes for printf */
313da2e3ebdSchin 	n = sfset(outfile,SF_SHARE|SF_PUBLIC,0);
314da2e3ebdSchin 	if(format)
315da2e3ebdSchin 	{
316da2e3ebdSchin 		/* printf style print */
317da2e3ebdSchin 		Sfio_t *pool;
318da2e3ebdSchin 		struct printf pdata;
319da2e3ebdSchin 		memset(&pdata, 0, sizeof(pdata));
320da2e3ebdSchin 		pdata.sh = shp;
321da2e3ebdSchin 		pdata.hdr.version = SFIO_VERSION;
322da2e3ebdSchin 		pdata.hdr.extf = extend;
323da2e3ebdSchin 		pdata.nextarg = argv;
324da2e3ebdSchin 		sh_offstate(SH_STOPOK);
325da2e3ebdSchin 		pool=sfpool(sfstderr,NIL(Sfio_t*),SF_WRITE);
326da2e3ebdSchin 		do
327da2e3ebdSchin 		{
328da2e3ebdSchin 			if(shp->trapnote&SH_SIGSET)
329da2e3ebdSchin 				break;
330da2e3ebdSchin 			pdata.hdr.form = format;
331da2e3ebdSchin 			sfprintf(outfile,"%!",&pdata);
332da2e3ebdSchin 		} while(*pdata.nextarg && pdata.nextarg!=argv);
333da2e3ebdSchin 		if(pdata.nextarg == nullarg && pdata.argsize>0)
334da2e3ebdSchin 			sfwrite(outfile,stakptr(staktell()),pdata.argsize);
33534f9b3eeSRoland Mainz 		if(sffileno(outfile)!=sffileno(sfstderr))
33634f9b3eeSRoland Mainz 			sfsync(outfile);
337da2e3ebdSchin 		sfpool(sfstderr,pool,SF_WRITE);
338da2e3ebdSchin 		exitval = pdata.err;
339da2e3ebdSchin 	}
3407c2fbfb3SApril Chin 	else if(vflag)
3417c2fbfb3SApril Chin 	{
3427c2fbfb3SApril Chin 		while(*argv)
34334f9b3eeSRoland Mainz 		{
34434f9b3eeSRoland Mainz 			fmtbase64(outfile,*argv++,vflag=='C');
34534f9b3eeSRoland Mainz 			if(!nflag)
34634f9b3eeSRoland Mainz 				sfputc(outfile,'\n');
34734f9b3eeSRoland Mainz 		}
3487c2fbfb3SApril Chin 	}
349da2e3ebdSchin 	else
350da2e3ebdSchin 	{
351da2e3ebdSchin 		/* echo style print */
3527c2fbfb3SApril Chin 		if(nflag && !argv[0])
3537c2fbfb3SApril Chin 			sfsync((Sfio_t*)0);
354b30d1939SAndy Fiddaman 		else if(sh_echolist(shp,outfile,rflag,argv) && !nflag)
355da2e3ebdSchin 			sfputc(outfile,'\n');
356da2e3ebdSchin 	}
357da2e3ebdSchin 	if(sflag)
358da2e3ebdSchin 	{
359b30d1939SAndy Fiddaman 		hist_flush(shp->gd->hist_ptr);
360da2e3ebdSchin 		sh_offstate(SH_HISTORY);
361da2e3ebdSchin 	}
362da2e3ebdSchin 	else if(n&SF_SHARE)
363da2e3ebdSchin 	{
364da2e3ebdSchin 		sfset(outfile,SF_SHARE|SF_PUBLIC,1);
365da2e3ebdSchin 		sfsync(outfile);
366da2e3ebdSchin 	}
367da2e3ebdSchin 	return(exitval);
368da2e3ebdSchin }
369da2e3ebdSchin 
370da2e3ebdSchin /*
371da2e3ebdSchin  * echo the argument list onto <outfile>
372da2e3ebdSchin  * if <raw> is non-zero then \ is not a special character.
373da2e3ebdSchin  * returns 0 for \c otherwise 1.
374da2e3ebdSchin  */
375da2e3ebdSchin 
376b30d1939SAndy Fiddaman int sh_echolist(Shell_t *shp,Sfio_t *outfile, int raw, char *argv[])
377da2e3ebdSchin {
378da2e3ebdSchin 	register char	*cp;
379da2e3ebdSchin 	register int	n;
380da2e3ebdSchin 	struct printf pdata;
381da2e3ebdSchin 	pdata.cescape = 0;
382da2e3ebdSchin 	pdata.err = 0;
383da2e3ebdSchin 	while(!pdata.cescape && (cp= *argv++))
384da2e3ebdSchin 	{
385da2e3ebdSchin 		if(!raw  && (n=fmtvecho(cp,&pdata))>=0)
386da2e3ebdSchin 		{
387da2e3ebdSchin 			if(n)
388da2e3ebdSchin 				sfwrite(outfile,stakptr(staktell()),n);
389da2e3ebdSchin 		}
390da2e3ebdSchin 		else
391da2e3ebdSchin 			sfputr(outfile,cp,-1);
392da2e3ebdSchin 		if(*argv)
393da2e3ebdSchin 			sfputc(outfile,' ');
394b30d1939SAndy Fiddaman 		sh_sigcheck(shp);
395da2e3ebdSchin 	}
396da2e3ebdSchin 	return(!pdata.cescape);
397da2e3ebdSchin }
398da2e3ebdSchin 
399da2e3ebdSchin /*
400da2e3ebdSchin  * modified version of stresc for generating formats
401da2e3ebdSchin  */
402da2e3ebdSchin static char strformat(char *s)
403da2e3ebdSchin {
404da2e3ebdSchin         register char*  t;
405da2e3ebdSchin         register int    c;
406da2e3ebdSchin         char*           b;
407da2e3ebdSchin         char*           p;
408b30d1939SAndy Fiddaman #if SHOPT_MULTIBYTE && defined(FMT_EXP_WIDE)
409b30d1939SAndy Fiddaman 	int		w;
410b30d1939SAndy Fiddaman #endif
411da2e3ebdSchin 
412da2e3ebdSchin         b = t = s;
413da2e3ebdSchin         for (;;)
414da2e3ebdSchin         {
415da2e3ebdSchin                 switch (c = *s++)
416da2e3ebdSchin                 {
417da2e3ebdSchin                     case '\\':
418da2e3ebdSchin 			if(*s==0)
419da2e3ebdSchin 				break;
420b30d1939SAndy Fiddaman #if SHOPT_MULTIBYTE && defined(FMT_EXP_WIDE)
421b30d1939SAndy Fiddaman                         c = chrexp(s - 1, &p, &w, FMT_EXP_CHAR|FMT_EXP_LINE|FMT_EXP_WIDE);
422b30d1939SAndy Fiddaman #else
423da2e3ebdSchin                         c = chresc(s - 1, &p);
424b30d1939SAndy Fiddaman #endif
425da2e3ebdSchin                         s = p;
426da2e3ebdSchin #if SHOPT_MULTIBYTE
427b30d1939SAndy Fiddaman #if defined(FMT_EXP_WIDE)
428b30d1939SAndy Fiddaman 			if(w)
429b30d1939SAndy Fiddaman 			{
430b30d1939SAndy Fiddaman 				t += mbwide() ? mbconv(t, c) : wc2utf8(t, c);
431b30d1939SAndy Fiddaman 				continue;
432b30d1939SAndy Fiddaman 			}
433b30d1939SAndy Fiddaman #else
434da2e3ebdSchin 			if(c>UCHAR_MAX && mbwide())
435da2e3ebdSchin 			{
436b30d1939SAndy Fiddaman 				t += mbconv(t, c);
437da2e3ebdSchin 				continue;
438da2e3ebdSchin 			}
439b30d1939SAndy Fiddaman #endif /* FMT_EXP_WIDE */
440da2e3ebdSchin #endif /* SHOPT_MULTIBYTE */
441da2e3ebdSchin 			if(c=='%')
442da2e3ebdSchin 				*t++ = '%';
443da2e3ebdSchin 			else if(c==0)
444da2e3ebdSchin 			{
445da2e3ebdSchin 				*t++ = '%';
446da2e3ebdSchin 				c = 'Z';
447da2e3ebdSchin 			}
448da2e3ebdSchin                         break;
449da2e3ebdSchin                     case 0:
450da2e3ebdSchin                         *t = 0;
451da2e3ebdSchin                         return(t - b);
452da2e3ebdSchin                 }
453da2e3ebdSchin                 *t++ = c;
454da2e3ebdSchin         }
455da2e3ebdSchin }
456da2e3ebdSchin 
457da2e3ebdSchin 
458da2e3ebdSchin static char *genformat(char *format)
459da2e3ebdSchin {
460da2e3ebdSchin 	register char *fp;
461da2e3ebdSchin 	stakseek(0);
462da2e3ebdSchin 	stakputs(preformat);
463da2e3ebdSchin 	stakputs(format);
464da2e3ebdSchin 	fp = (char*)stakfreeze(1);
465da2e3ebdSchin 	strformat(fp+sizeof(preformat)-1);
466da2e3ebdSchin 	return(fp);
467da2e3ebdSchin }
468da2e3ebdSchin 
469b30d1939SAndy Fiddaman static char *fmthtml(const char *string, int flags)
470da2e3ebdSchin {
471da2e3ebdSchin 	register const char *cp = string;
472da2e3ebdSchin 	register int c, offset = staktell();
473b30d1939SAndy Fiddaman 	if(!(flags&SFFMT_ALTER))
474da2e3ebdSchin 	{
475b30d1939SAndy Fiddaman 		while(c= *(unsigned char*)cp++)
476b30d1939SAndy Fiddaman 		{
477da2e3ebdSchin #if SHOPT_MULTIBYTE
478b30d1939SAndy Fiddaman 			register int s;
479b30d1939SAndy Fiddaman 			if((s=mbsize(cp-1)) > 1)
480b30d1939SAndy Fiddaman 			{
481b30d1939SAndy Fiddaman 				cp += (s-1);
482b30d1939SAndy Fiddaman 				continue;
483b30d1939SAndy Fiddaman 			}
484b30d1939SAndy Fiddaman #endif /* SHOPT_MULTIBYTE */
485b30d1939SAndy Fiddaman 			if(c=='<')
486b30d1939SAndy Fiddaman 				stakputs("&lt;");
487b30d1939SAndy Fiddaman 			else if(c=='>')
488b30d1939SAndy Fiddaman 				stakputs("&gt;");
489b30d1939SAndy Fiddaman 			else if(c=='&')
490b30d1939SAndy Fiddaman 				stakputs("&amp;");
491b30d1939SAndy Fiddaman 			else if(c=='"')
492b30d1939SAndy Fiddaman 				stakputs("&quot;");
493b30d1939SAndy Fiddaman 			else if(c=='\'')
494b30d1939SAndy Fiddaman 				stakputs("&apos;");
495b30d1939SAndy Fiddaman 			else if(c==' ')
496b30d1939SAndy Fiddaman 				stakputs("&nbsp;");
497b30d1939SAndy Fiddaman 			else if(!isprint(c) && c!='\n' && c!='\r')
498b30d1939SAndy Fiddaman 				sfprintf(stkstd,"&#%X;",CCMAPC(c,CC_NATIVE,CC_ASCII));
499b30d1939SAndy Fiddaman 			else
500b30d1939SAndy Fiddaman 				stakputc(c);
501b30d1939SAndy Fiddaman 		}
502b30d1939SAndy Fiddaman 	}
503b30d1939SAndy Fiddaman 	else
504b30d1939SAndy Fiddaman 	{
505b30d1939SAndy Fiddaman 		while(c= *(unsigned char*)cp++)
506da2e3ebdSchin 		{
507b30d1939SAndy Fiddaman 			if(strchr("!*'();@&+$,#[]<>~.\"{}|\\-`^% ",c) || (!isprint(c) && c!='\n' && c!='\r'))
508b30d1939SAndy Fiddaman 				sfprintf(stkstd,"%%%02X",CCMAPC(c,CC_NATIVE,CC_ASCII));
509b30d1939SAndy Fiddaman 			else
510b30d1939SAndy Fiddaman 				stakputc(c);
511da2e3ebdSchin 		}
512da2e3ebdSchin 	}
513da2e3ebdSchin 	stakputc(0);
514da2e3ebdSchin 	return(stakptr(offset));
515da2e3ebdSchin }
516da2e3ebdSchin 
5177c2fbfb3SApril Chin #if 1
5187c2fbfb3SApril Chin static ssize_t fmtbase64(Sfio_t *iop, char *string, int alt)
5197c2fbfb3SApril Chin #else
5207c2fbfb3SApril Chin static void *fmtbase64(char *string, ssize_t *sz, int alt)
5217c2fbfb3SApril Chin #endif
522da2e3ebdSchin {
523da2e3ebdSchin 	char			*cp;
524da2e3ebdSchin 	Sfdouble_t		d;
5257c2fbfb3SApril Chin 	ssize_t			size;
526da2e3ebdSchin 	Namval_t		*np = nv_open(string, NiL, NV_VARNAME|NV_NOASSIGN|NV_NOADD);
527b30d1939SAndy Fiddaman 	Namarr_t		*ap;
528da2e3ebdSchin 	static union types_t	number;
5297c2fbfb3SApril Chin 	if(!np || nv_isnull(np))
5307c2fbfb3SApril Chin 	{
5317c2fbfb3SApril Chin 		if(sh_isoption(SH_NOUNSET))
5327c2fbfb3SApril Chin 			errormsg(SH_DICT,ERROR_exit(1),e_notset,string);
5337c2fbfb3SApril Chin 		return(0);
5347c2fbfb3SApril Chin 	}
535da2e3ebdSchin 	if(nv_isattr(np,NV_INTEGER))
536da2e3ebdSchin 	{
537da2e3ebdSchin 		d = nv_getnum(np);
538da2e3ebdSchin 		if(nv_isattr(np,NV_DOUBLE))
539da2e3ebdSchin 		{
540da2e3ebdSchin 			if(nv_isattr(np,NV_LONG))
541da2e3ebdSchin 			{
542da2e3ebdSchin 				size = sizeof(Sfdouble_t);
543da2e3ebdSchin 				number.ld = d;
544da2e3ebdSchin 			}
545da2e3ebdSchin 			else if(nv_isattr(np,NV_SHORT))
546da2e3ebdSchin 			{
547da2e3ebdSchin 				size = sizeof(float);
548da2e3ebdSchin 				number.f = (float)d;
549da2e3ebdSchin 			}
550da2e3ebdSchin 			else
551da2e3ebdSchin 			{
552da2e3ebdSchin 				size = sizeof(double);
553da2e3ebdSchin 				number.d = (double)d;
554da2e3ebdSchin 			}
555da2e3ebdSchin 		}
556da2e3ebdSchin 		else
557da2e3ebdSchin 		{
558da2e3ebdSchin 			if(nv_isattr(np,NV_LONG))
559da2e3ebdSchin 			{
560da2e3ebdSchin 				size =  sizeof(Sflong_t);
561da2e3ebdSchin 				number.ll = (Sflong_t)d;
562da2e3ebdSchin 			}
563da2e3ebdSchin 			else if(nv_isattr(np,NV_SHORT))
564da2e3ebdSchin 			{
565da2e3ebdSchin 				size =  sizeof(short);
566da2e3ebdSchin 				number.h = (short)d;
567da2e3ebdSchin 			}
568da2e3ebdSchin 			else
569da2e3ebdSchin 			{
570da2e3ebdSchin 				size =  sizeof(short);
571da2e3ebdSchin 				number.i = (int)d;
572da2e3ebdSchin 			}
573da2e3ebdSchin 		}
5747c2fbfb3SApril Chin #if 1
5757c2fbfb3SApril Chin 		return(sfwrite(iop, (void*)&number, size));
5767c2fbfb3SApril Chin #else
577da2e3ebdSchin 		if(sz)
578da2e3ebdSchin 			*sz = size;
579da2e3ebdSchin 		return((void*)&number);
5807c2fbfb3SApril Chin #endif
581da2e3ebdSchin 	}
582da2e3ebdSchin 	if(nv_isattr(np,NV_BINARY))
5837c2fbfb3SApril Chin #if 1
5847c2fbfb3SApril Chin 	{
5857c2fbfb3SApril Chin 		Namfun_t *fp;
5867c2fbfb3SApril Chin 		for(fp=np->nvfun; fp;fp=fp->next)
5877c2fbfb3SApril Chin 		{
5887c2fbfb3SApril Chin 			if(fp->disc && fp->disc->writef)
5897c2fbfb3SApril Chin 				break;
5907c2fbfb3SApril Chin 		}
5917c2fbfb3SApril Chin 		if(fp)
5927c2fbfb3SApril Chin 			return (*fp->disc->writef)(np, iop, 0, fp);
5937c2fbfb3SApril Chin 		else
5947c2fbfb3SApril Chin 		{
5957c2fbfb3SApril Chin 			int n = nv_size(np);
59634f9b3eeSRoland Mainz 			if(nv_isarray(np))
59734f9b3eeSRoland Mainz 			{
59834f9b3eeSRoland Mainz 				nv_onattr(np,NV_RAW);
59934f9b3eeSRoland Mainz 				cp = nv_getval(np);
60034f9b3eeSRoland Mainz 				nv_offattr(np,NV_RAW);
60134f9b3eeSRoland Mainz 			}
60234f9b3eeSRoland Mainz 			else
60334f9b3eeSRoland Mainz 				cp = (char*)np->nvalue.cp;
6047c2fbfb3SApril Chin 			if((size = n)==0)
6057c2fbfb3SApril Chin 				size = strlen(cp);
6067c2fbfb3SApril Chin 			size = sfwrite(iop, cp, size);
6077c2fbfb3SApril Chin 			return(n?n:size);
6087c2fbfb3SApril Chin 		}
6097c2fbfb3SApril Chin 	}
610b30d1939SAndy Fiddaman 	else if(nv_isarray(np) && (ap=nv_arrayptr(np)) && array_elem(ap) && (ap->nelem&(ARRAY_UNDEF|ARRAY_SCAN)))
6117c2fbfb3SApril Chin 	{
6127c2fbfb3SApril Chin 		nv_outnode(np,iop,(alt?-1:0),0);
6137c2fbfb3SApril Chin 		sfputc(iop,')');
6147c2fbfb3SApril Chin 		return(sftell(iop));
6157c2fbfb3SApril Chin 	}
6167c2fbfb3SApril Chin 	else
6177c2fbfb3SApril Chin 	{
6187c2fbfb3SApril Chin 		if(alt && nv_isvtree(np))
6197c2fbfb3SApril Chin 			nv_onattr(np,NV_EXPORT);
620b30d1939SAndy Fiddaman 		else
621b30d1939SAndy Fiddaman 			alt = 0;
622b30d1939SAndy Fiddaman 		cp = nv_getval(np);
623b30d1939SAndy Fiddaman 		if(alt)
624b30d1939SAndy Fiddaman 			nv_offattr(np,NV_EXPORT);
625b30d1939SAndy Fiddaman 		if(!cp)
6267c2fbfb3SApril Chin 			return(0);
6277c2fbfb3SApril Chin 		size = strlen(cp);
6287c2fbfb3SApril Chin 		return(sfwrite(iop,cp,size));
6297c2fbfb3SApril Chin 	}
6307c2fbfb3SApril Chin #else
631da2e3ebdSchin 		nv_onattr(np,NV_RAW);
632da2e3ebdSchin 	cp = nv_getval(np);
633da2e3ebdSchin 	if(nv_isattr(np,NV_BINARY))
634da2e3ebdSchin 		nv_offattr(np,NV_RAW);
635da2e3ebdSchin 	if((size = nv_size(np))==0)
636da2e3ebdSchin 		size = strlen(cp);
637da2e3ebdSchin 	if(sz)
638da2e3ebdSchin 		*sz = size;
639da2e3ebdSchin 	return((void*)cp);
6407c2fbfb3SApril Chin #endif
6417c2fbfb3SApril Chin }
6427c2fbfb3SApril Chin 
6437c2fbfb3SApril Chin static int varname(const char *str, int n)
6447c2fbfb3SApril Chin {
6457c2fbfb3SApril Chin 	register int c,dot=1,len=1;
6467c2fbfb3SApril Chin 	if(n < 0)
6477c2fbfb3SApril Chin 	{
6487c2fbfb3SApril Chin 		if(*str=='.')
6497c2fbfb3SApril Chin 			str++;
6507c2fbfb3SApril Chin 		n = strlen(str);
6517c2fbfb3SApril Chin 	}
6527c2fbfb3SApril Chin 	for(;n > 0; n-=len)
6537c2fbfb3SApril Chin 	{
6547c2fbfb3SApril Chin #ifdef SHOPT_MULTIBYTE
6557c2fbfb3SApril Chin 		len = mbsize(str);
6567c2fbfb3SApril Chin 		c = mbchar(str);
6577c2fbfb3SApril Chin #else
6587c2fbfb3SApril Chin 		c = *(unsigned char*)str++;
6597c2fbfb3SApril Chin #endif
6607c2fbfb3SApril Chin 		if(dot && !(isalpha(c)||c=='_'))
6617c2fbfb3SApril Chin 			break;
6627c2fbfb3SApril Chin 		else if(dot==0 && !(isalnum(c) || c=='_' || c == '.'))
6637c2fbfb3SApril Chin 			break;
6647c2fbfb3SApril Chin 		dot = (c=='.');
6657c2fbfb3SApril Chin 	}
6667c2fbfb3SApril Chin 	return(n==0);
667da2e3ebdSchin }
668da2e3ebdSchin 
669b30d1939SAndy Fiddaman static const char *mapformat(Sffmt_t *fe)
670b30d1939SAndy Fiddaman {
671b30d1939SAndy Fiddaman 	const struct printmap *pm = Pmap;
672b30d1939SAndy Fiddaman 	while(pm->size>0)
673b30d1939SAndy Fiddaman 	{
674b30d1939SAndy Fiddaman 		if(pm->size==fe->n_str && memcmp(pm->name,fe->t_str,fe->n_str)==0)
675b30d1939SAndy Fiddaman 			return(pm->map);
676b30d1939SAndy Fiddaman 		pm++;
677b30d1939SAndy Fiddaman 	}
678b30d1939SAndy Fiddaman 	return(0);
679b30d1939SAndy Fiddaman }
680b30d1939SAndy Fiddaman 
681da2e3ebdSchin static int extend(Sfio_t* sp, void* v, Sffmt_t* fe)
682da2e3ebdSchin {
683da2e3ebdSchin 	char*		lastchar = "";
684da2e3ebdSchin 	register int	neg = 0;
685da2e3ebdSchin 	Sfdouble_t	d;
686da2e3ebdSchin 	Sfdouble_t	longmin = LDBL_LLONG_MIN;
687da2e3ebdSchin 	Sfdouble_t	longmax = LDBL_LLONG_MAX;
688da2e3ebdSchin 	int		format = fe->fmt;
689da2e3ebdSchin 	int		n;
690da2e3ebdSchin 	int		fold = fe->base;
691da2e3ebdSchin 	union types_t*	value = (union types_t*)v;
692da2e3ebdSchin 	struct printf*	pp = (struct printf*)fe;
693b30d1939SAndy Fiddaman 	Shell_t		*shp = pp->sh;
694da2e3ebdSchin 	register char*	argp = *pp->nextarg;
695b30d1939SAndy Fiddaman 	char		*w,*s;
696da2e3ebdSchin 
697b30d1939SAndy Fiddaman 	if(fe->n_str>0 && (format=='T'||format=='Q') && varname(fe->t_str,fe->n_str) && (!argp || varname(argp,-1)))
6987c2fbfb3SApril Chin 	{
6997c2fbfb3SApril Chin 		if(argp)
7007c2fbfb3SApril Chin 			pp->lastarg = argp;
7017c2fbfb3SApril Chin 		else
7027c2fbfb3SApril Chin 			argp = pp->lastarg;
7037c2fbfb3SApril Chin 		if(argp)
7047c2fbfb3SApril Chin 		{
7057c2fbfb3SApril Chin 			sfprintf(pp->sh->strbuf,"%s.%.*s%c",argp,fe->n_str,fe->t_str,0);
7067c2fbfb3SApril Chin 			argp = sfstruse(pp->sh->strbuf);
7077c2fbfb3SApril Chin 		}
7087c2fbfb3SApril Chin 	}
7097c2fbfb3SApril Chin 	else
7107c2fbfb3SApril Chin 		pp->lastarg = 0;
711da2e3ebdSchin 	fe->flags |= SFFMT_VALUE;
712da2e3ebdSchin 	if(!argp || format=='Z')
713da2e3ebdSchin 	{
714da2e3ebdSchin 		switch(format)
715da2e3ebdSchin 		{
716da2e3ebdSchin 		case 'c':
717da2e3ebdSchin 			value->c = 0;
718da2e3ebdSchin 			fe->flags &= ~SFFMT_LONG;
719da2e3ebdSchin 			break;
720da2e3ebdSchin 		case 'q':
721da2e3ebdSchin 			format = 's';
722da2e3ebdSchin 			/* FALL THROUGH */
723da2e3ebdSchin 		case 's':
724da2e3ebdSchin 		case 'H':
725da2e3ebdSchin 		case 'B':
726da2e3ebdSchin 		case 'P':
727da2e3ebdSchin 		case 'R':
728da2e3ebdSchin 		case 'Z':
729da2e3ebdSchin 		case 'b':
730da2e3ebdSchin 			fe->fmt = 's';
731da2e3ebdSchin 			fe->size = -1;
732da2e3ebdSchin 			fe->base = -1;
733da2e3ebdSchin 			value->s = "";
734da2e3ebdSchin 			fe->flags &= ~SFFMT_LONG;
735da2e3ebdSchin 			break;
736da2e3ebdSchin 		case 'a':
737da2e3ebdSchin 		case 'e':
738da2e3ebdSchin 		case 'f':
739da2e3ebdSchin 		case 'g':
740da2e3ebdSchin 		case 'A':
741da2e3ebdSchin 		case 'E':
742da2e3ebdSchin 		case 'F':
743da2e3ebdSchin 		case 'G':
744da2e3ebdSchin                         if(SFFMT_LDOUBLE)
745da2e3ebdSchin 				value->ld = 0.;
746da2e3ebdSchin 			else
747da2e3ebdSchin 				value->d = 0.;
748da2e3ebdSchin 			break;
749da2e3ebdSchin 		case 'n':
750da2e3ebdSchin 			value->ip = &pp->intvar;
751da2e3ebdSchin 			break;
752da2e3ebdSchin 		case 'Q':
753da2e3ebdSchin 			value->ll = 0;
754da2e3ebdSchin 			break;
755da2e3ebdSchin 		case 'T':
756da2e3ebdSchin 			fe->fmt = 'd';
757da2e3ebdSchin 			value->ll = tmxgettime();
758da2e3ebdSchin 			break;
759da2e3ebdSchin 		default:
760da2e3ebdSchin 			if(!strchr("DdXxoUu",format))
761da2e3ebdSchin 				errormsg(SH_DICT,ERROR_exit(1),e_formspec,format);
762da2e3ebdSchin 			fe->fmt = 'd';
763da2e3ebdSchin 			value->ll = 0;
764da2e3ebdSchin 			break;
765da2e3ebdSchin 		}
766da2e3ebdSchin 	}
767da2e3ebdSchin 	else
768da2e3ebdSchin 	{
769da2e3ebdSchin 		switch(format)
770da2e3ebdSchin 		{
771da2e3ebdSchin 		case 'p':
772da2e3ebdSchin 			value->p = (char**)strtol(argp,&lastchar,10);
773da2e3ebdSchin 			break;
774da2e3ebdSchin 		case 'n':
775da2e3ebdSchin 		{
776da2e3ebdSchin 			Namval_t *np;
777b30d1939SAndy Fiddaman 			np = nv_open(argp,shp->var_tree,NV_VARNAME|NV_NOASSIGN|NV_NOARRAY);
778b30d1939SAndy Fiddaman 			_nv_unset(np,0);
779da2e3ebdSchin 			nv_onattr(np,NV_INTEGER);
780da2e3ebdSchin 			if (np->nvalue.lp = new_of(int32_t,0))
781da2e3ebdSchin 				*np->nvalue.lp = 0;
782da2e3ebdSchin 			nv_setsize(np,10);
783da2e3ebdSchin 			if(sizeof(int)==sizeof(int32_t))
784da2e3ebdSchin 				value->ip = (int*)np->nvalue.lp;
785da2e3ebdSchin 			else
786da2e3ebdSchin 			{
787da2e3ebdSchin 				int32_t sl = 1;
788da2e3ebdSchin 				value->ip = (int*)(((char*)np->nvalue.lp) + (*((char*)&sl) ? 0 : sizeof(int)));
789da2e3ebdSchin 			}
790da2e3ebdSchin 			nv_close(np);
791da2e3ebdSchin 			break;
792da2e3ebdSchin 		}
793da2e3ebdSchin 		case 'q':
794b30d1939SAndy Fiddaman 			if(fe->n_str)
795b30d1939SAndy Fiddaman 			{
796b30d1939SAndy Fiddaman 				const char *fp = mapformat(fe);
797b30d1939SAndy Fiddaman 				if(fp)
798b30d1939SAndy Fiddaman 				{
799b30d1939SAndy Fiddaman 					format = *fp;
800b30d1939SAndy Fiddaman 					if(fp[1])
801b30d1939SAndy Fiddaman 						fe->flags |=SFFMT_ALTER;
802b30d1939SAndy Fiddaman 				}
803b30d1939SAndy Fiddaman 			}
804b30d1939SAndy Fiddaman 			/* FALLTHROUGH */
805da2e3ebdSchin 		case 'b':
806da2e3ebdSchin 		case 's':
807da2e3ebdSchin 		case 'B':
808da2e3ebdSchin 		case 'H':
809da2e3ebdSchin 		case 'P':
810da2e3ebdSchin 		case 'R':
811da2e3ebdSchin 			fe->fmt = 's';
812da2e3ebdSchin 			fe->size = -1;
813da2e3ebdSchin 			if(format=='s' && fe->base>=0)
814da2e3ebdSchin 			{
815da2e3ebdSchin 				value->p = pp->nextarg;
816da2e3ebdSchin 				pp->nextarg = nullarg;
817da2e3ebdSchin 			}
818da2e3ebdSchin 			else
819da2e3ebdSchin 			{
820da2e3ebdSchin 				fe->base = -1;
821da2e3ebdSchin 				value->s = argp;
822da2e3ebdSchin 			}
823da2e3ebdSchin 			fe->flags &= ~SFFMT_LONG;
824da2e3ebdSchin 			break;
825da2e3ebdSchin 		case 'c':
82634f9b3eeSRoland Mainz 			if(mbwide() && (n = mbsize(argp)) > 1)
82734f9b3eeSRoland Mainz 			{
82834f9b3eeSRoland Mainz 				fe->fmt = 's';
82934f9b3eeSRoland Mainz 				fe->size = n;
83034f9b3eeSRoland Mainz 				value->s = argp;
83134f9b3eeSRoland Mainz 			}
83234f9b3eeSRoland Mainz 			else if(fe->base >=0)
833da2e3ebdSchin 				value->s = argp;
834da2e3ebdSchin 			else
835da2e3ebdSchin 				value->c = *argp;
836da2e3ebdSchin 			fe->flags &= ~SFFMT_LONG;
837da2e3ebdSchin 			break;
838da2e3ebdSchin 		case 'o':
839da2e3ebdSchin 		case 'x':
840da2e3ebdSchin 		case 'X':
841da2e3ebdSchin 		case 'u':
842da2e3ebdSchin 		case 'U':
843da2e3ebdSchin 			longmax = LDBL_ULLONG_MAX;
8445ae8bd53SToomas Soome 			/* FALLTHROUGH */
845da2e3ebdSchin 		case '.':
846da2e3ebdSchin 			if(fe->size==2 && strchr("bcsqHPRQTZ",*fe->form))
847da2e3ebdSchin 			{
848da2e3ebdSchin 				value->ll = ((unsigned char*)argp)[0];
849da2e3ebdSchin 				break;
850da2e3ebdSchin 			}
8515ae8bd53SToomas Soome 			/* FALLTHROUGH */
852da2e3ebdSchin 		case 'd':
853da2e3ebdSchin 		case 'D':
854da2e3ebdSchin 		case 'i':
855da2e3ebdSchin 			switch(*argp)
856da2e3ebdSchin 			{
857da2e3ebdSchin 			case '\'':
858da2e3ebdSchin 			case '"':
85934f9b3eeSRoland Mainz 				w = argp + 1;
86034f9b3eeSRoland Mainz 				if(mbwide() && mbsize(w) > 1)
86134f9b3eeSRoland Mainz 					value->ll = mbchar(w);
86234f9b3eeSRoland Mainz 				else
86334f9b3eeSRoland Mainz 					value->ll = *(unsigned char*)w++;
86434f9b3eeSRoland Mainz 				if(w[0] && (w[0] != argp[0] || w[1]))
8657c2fbfb3SApril Chin 				{
8667c2fbfb3SApril Chin 					errormsg(SH_DICT,ERROR_warn(0),e_charconst,argp);
8677c2fbfb3SApril Chin 					pp->err = 1;
8687c2fbfb3SApril Chin 				}
869da2e3ebdSchin 				break;
870da2e3ebdSchin 			default:
871da2e3ebdSchin 				d = sh_strnum(argp,&lastchar,0);
872da2e3ebdSchin 				if(d<longmin)
873da2e3ebdSchin 				{
874da2e3ebdSchin 					errormsg(SH_DICT,ERROR_warn(0),e_overflow,argp);
875da2e3ebdSchin 					pp->err = 1;
876da2e3ebdSchin 					d = longmin;
877da2e3ebdSchin 				}
878da2e3ebdSchin 				else if(d>longmax)
879da2e3ebdSchin 				{
880da2e3ebdSchin 					errormsg(SH_DICT,ERROR_warn(0),e_overflow,argp);
881da2e3ebdSchin 					pp->err = 1;
882da2e3ebdSchin 					d = longmax;
883da2e3ebdSchin 				}
884da2e3ebdSchin 				value->ll = (Sflong_t)d;
885da2e3ebdSchin 				if(lastchar == *pp->nextarg)
886da2e3ebdSchin 				{
887da2e3ebdSchin 					value->ll = *argp;
888da2e3ebdSchin 					lastchar = "";
889da2e3ebdSchin 				}
890da2e3ebdSchin 				break;
891da2e3ebdSchin 			}
892da2e3ebdSchin 			if(neg)
893da2e3ebdSchin 				value->ll = -value->ll;
894da2e3ebdSchin 			fe->size = sizeof(value->ll);
895da2e3ebdSchin 			break;
896da2e3ebdSchin 		case 'a':
897da2e3ebdSchin 		case 'e':
898da2e3ebdSchin 		case 'f':
899da2e3ebdSchin 		case 'g':
900da2e3ebdSchin 		case 'A':
901da2e3ebdSchin 		case 'E':
902da2e3ebdSchin 		case 'F':
903da2e3ebdSchin 		case 'G':
904da2e3ebdSchin 			d = sh_strnum(*pp->nextarg,&lastchar,0);
9057c2fbfb3SApril Chin 			switch(*argp)
9067c2fbfb3SApril Chin 			{
9077c2fbfb3SApril Chin 			    case '\'':
9087c2fbfb3SApril Chin 			    case '"':
9097c2fbfb3SApril Chin 				d = ((unsigned char*)argp)[1];
9107c2fbfb3SApril Chin 				if(argp[2] && (argp[2] != argp[0] || argp[3]))
9117c2fbfb3SApril Chin 				{
9127c2fbfb3SApril Chin 					errormsg(SH_DICT,ERROR_warn(0),e_charconst,argp);
9137c2fbfb3SApril Chin 					pp->err = 1;
9147c2fbfb3SApril Chin 				}
9157c2fbfb3SApril Chin 				break;
9167c2fbfb3SApril Chin 			    default:
9177c2fbfb3SApril Chin 				d = sh_strnum(*pp->nextarg,&lastchar,0);
9187c2fbfb3SApril Chin 				break;
9197c2fbfb3SApril Chin 			}
920da2e3ebdSchin                         if(SFFMT_LDOUBLE)
921da2e3ebdSchin 			{
922da2e3ebdSchin 				value->ld = d;
923da2e3ebdSchin 				fe->size = sizeof(value->ld);
924da2e3ebdSchin 			}
925da2e3ebdSchin 			else
926da2e3ebdSchin 			{
927da2e3ebdSchin 				value->d = d;
928da2e3ebdSchin 				fe->size = sizeof(value->d);
929da2e3ebdSchin 			}
930da2e3ebdSchin 			break;
931da2e3ebdSchin 		case 'Q':
932da2e3ebdSchin 			value->ll = (Sflong_t)strelapsed(*pp->nextarg,&lastchar,1);
933da2e3ebdSchin 			break;
934da2e3ebdSchin 		case 'T':
935da2e3ebdSchin 			value->ll = (Sflong_t)tmxdate(*pp->nextarg,&lastchar,TMX_NOW);
936da2e3ebdSchin 			break;
937da2e3ebdSchin 		default:
938da2e3ebdSchin 			value->ll = 0;
939da2e3ebdSchin 			fe->fmt = 'd';
940da2e3ebdSchin 			fe->size = sizeof(value->ll);
941da2e3ebdSchin 			errormsg(SH_DICT,ERROR_exit(1),e_formspec,format);
942da2e3ebdSchin 			break;
943da2e3ebdSchin 		}
944da2e3ebdSchin 		if (format == '.')
945da2e3ebdSchin 			value->i = value->ll;
946da2e3ebdSchin 		if(*lastchar)
947da2e3ebdSchin 		{
948da2e3ebdSchin 			errormsg(SH_DICT,ERROR_warn(0),e_argtype,format);
949da2e3ebdSchin 			pp->err = 1;
950da2e3ebdSchin 		}
951da2e3ebdSchin 		pp->nextarg++;
952da2e3ebdSchin 	}
953da2e3ebdSchin 	switch(format)
954da2e3ebdSchin 	{
955da2e3ebdSchin 	case 'Z':
956da2e3ebdSchin 		fe->fmt = 'c';
957da2e3ebdSchin 		fe->base = -1;
958da2e3ebdSchin 		value->c = 0;
959da2e3ebdSchin 		break;
960da2e3ebdSchin 	case 'b':
961da2e3ebdSchin 		if((n=fmtvecho(value->s,pp))>=0)
962da2e3ebdSchin 		{
963da2e3ebdSchin 			if(pp->nextarg == nullarg)
964da2e3ebdSchin 			{
965da2e3ebdSchin 				pp->argsize = n;
966da2e3ebdSchin 				return -1;
967da2e3ebdSchin 			}
968da2e3ebdSchin 			value->s = stakptr(staktell());
969b30d1939SAndy Fiddaman 			fe->size = n;
970da2e3ebdSchin 		}
971da2e3ebdSchin 		break;
972da2e3ebdSchin 	case 'B':
973b30d1939SAndy Fiddaman 		if(!shp->strbuf2)
974b30d1939SAndy Fiddaman 			shp->strbuf2 = sfstropen();
975b30d1939SAndy Fiddaman 		fe->size = fmtbase64(shp->strbuf2,value->s, fe->flags&SFFMT_ALTER);
976b30d1939SAndy Fiddaman 		value->s = sfstruse(shp->strbuf2);
9777c2fbfb3SApril Chin 		fe->flags |= SFFMT_SHORT;
978da2e3ebdSchin 		break;
979da2e3ebdSchin 	case 'H':
980b30d1939SAndy Fiddaman 		value->s = fmthtml(value->s, fe->flags);
981da2e3ebdSchin 		break;
982da2e3ebdSchin 	case 'q':
983da2e3ebdSchin 		value->s = sh_fmtqf(value->s, !!(fe->flags & SFFMT_ALTER), fold);
984da2e3ebdSchin 		break;
985da2e3ebdSchin 	case 'P':
986b30d1939SAndy Fiddaman 		s = fmtmatch(value->s);
987da2e3ebdSchin 		if(!s || *s==0)
988da2e3ebdSchin 			errormsg(SH_DICT,ERROR_exit(1),e_badregexp,value->s);
989da2e3ebdSchin 		value->s = s;
990da2e3ebdSchin 		break;
991da2e3ebdSchin 	case 'R':
992b30d1939SAndy Fiddaman 		s = fmtre(value->s);
993b30d1939SAndy Fiddaman 		if(!s || *s==0)
994da2e3ebdSchin 			errormsg(SH_DICT,ERROR_exit(1),e_badregexp,value->s);
995b30d1939SAndy Fiddaman 		value->s = s;
996da2e3ebdSchin 		break;
997da2e3ebdSchin 	case 'Q':
998da2e3ebdSchin 		if (fe->n_str>0)
999da2e3ebdSchin 		{
1000da2e3ebdSchin 			fe->fmt = 'd';
1001da2e3ebdSchin 			fe->size = sizeof(value->ll);
1002da2e3ebdSchin 		}
1003da2e3ebdSchin 		else
1004da2e3ebdSchin 		{
1005da2e3ebdSchin 			value->s = fmtelapsed(value->ll, 1);
1006da2e3ebdSchin 			fe->fmt = 's';
1007da2e3ebdSchin 			fe->size = -1;
1008da2e3ebdSchin 		}
1009da2e3ebdSchin 		break;
1010da2e3ebdSchin 	case 'T':
1011da2e3ebdSchin 		if(fe->n_str>0)
1012da2e3ebdSchin 		{
1013da2e3ebdSchin 			n = fe->t_str[fe->n_str];
1014da2e3ebdSchin 			fe->t_str[fe->n_str] = 0;
1015da2e3ebdSchin 			value->s = fmttmx(fe->t_str, value->ll);
1016da2e3ebdSchin 			fe->t_str[fe->n_str] = n;
1017da2e3ebdSchin 		}
1018da2e3ebdSchin 		else value->s = fmttmx(NIL(char*), value->ll);
1019da2e3ebdSchin 		fe->fmt = 's';
1020da2e3ebdSchin 		fe->size = -1;
1021da2e3ebdSchin 		break;
1022da2e3ebdSchin 	}
1023da2e3ebdSchin 	return 0;
1024da2e3ebdSchin }
1025da2e3ebdSchin 
1026da2e3ebdSchin /*
1027da2e3ebdSchin  * construct System V echo string out of <cp>
1028da2e3ebdSchin  * If there are not escape sequences, returns -1
1029da2e3ebdSchin  * Otherwise, puts null terminated result on stack, but doesn't freeze it
1030da2e3ebdSchin  * returns length of output.
1031da2e3ebdSchin  */
1032da2e3ebdSchin 
1033da2e3ebdSchin static int fmtvecho(const char *string, struct printf *pp)
1034da2e3ebdSchin {
1035da2e3ebdSchin 	register const char *cp = string, *cpmax;
1036da2e3ebdSchin 	register int c;
1037da2e3ebdSchin 	register int offset = staktell();
1038da2e3ebdSchin #if SHOPT_MULTIBYTE
1039da2e3ebdSchin 	int chlen;
1040da2e3ebdSchin 	if(mbwide())
1041da2e3ebdSchin 	{
1042da2e3ebdSchin 		while(1)
1043da2e3ebdSchin 		{
1044da2e3ebdSchin 			if ((chlen = mbsize(cp)) > 1)
1045da2e3ebdSchin 				/* Skip over multibyte characters */
1046da2e3ebdSchin 				cp += chlen;
1047da2e3ebdSchin 			else if((c= *cp++)==0 || c == '\\')
1048da2e3ebdSchin 				break;
1049da2e3ebdSchin 		}
1050da2e3ebdSchin 	}
1051da2e3ebdSchin 	else
1052da2e3ebdSchin #endif /* SHOPT_MULTIBYTE */
1053da2e3ebdSchin 	while((c= *cp++) && (c!='\\'));
1054da2e3ebdSchin 	if(c==0)
1055da2e3ebdSchin 		return(-1);
1056da2e3ebdSchin 	c = --cp - string;
1057da2e3ebdSchin 	if(c>0)
1058da2e3ebdSchin 		stakwrite((void*)string,c);
1059da2e3ebdSchin 	for(; c= *cp; cp++)
1060da2e3ebdSchin 	{
1061da2e3ebdSchin #if SHOPT_MULTIBYTE
1062da2e3ebdSchin 		if (mbwide() && ((chlen = mbsize(cp)) > 1))
1063da2e3ebdSchin 		{
1064da2e3ebdSchin 			stakwrite(cp,chlen);
1065da2e3ebdSchin 			cp +=  (chlen-1);
1066da2e3ebdSchin 			continue;
1067da2e3ebdSchin 		}
1068da2e3ebdSchin #endif /* SHOPT_MULTIBYTE */
1069da2e3ebdSchin 		if( c=='\\') switch(*++cp)
1070da2e3ebdSchin 		{
1071da2e3ebdSchin 			case 'E':
1072da2e3ebdSchin 				c = ('a'==97?'\033':39); /* ASCII/EBCDIC */
1073da2e3ebdSchin 				break;
1074da2e3ebdSchin 			case 'a':
1075da2e3ebdSchin 				c = '\a';
1076da2e3ebdSchin 				break;
1077da2e3ebdSchin 			case 'b':
1078da2e3ebdSchin 				c = '\b';
1079da2e3ebdSchin 				break;
1080da2e3ebdSchin 			case 'c':
1081da2e3ebdSchin 				pp->cescape++;
1082da2e3ebdSchin 				pp->nextarg = nullarg;
1083da2e3ebdSchin 				goto done;
1084da2e3ebdSchin 			case 'f':
1085da2e3ebdSchin 				c = '\f';
1086da2e3ebdSchin 				break;
1087da2e3ebdSchin 			case 'n':
1088da2e3ebdSchin 				c = '\n';
1089da2e3ebdSchin 				break;
1090da2e3ebdSchin 			case 'r':
1091da2e3ebdSchin 				c = '\r';
1092da2e3ebdSchin 				break;
1093da2e3ebdSchin 			case 'v':
1094da2e3ebdSchin 				c = '\v';
1095da2e3ebdSchin 				break;
1096da2e3ebdSchin 			case 't':
1097da2e3ebdSchin 				c = '\t';
1098da2e3ebdSchin 				break;
1099da2e3ebdSchin 			case '\\':
1100da2e3ebdSchin 				c = '\\';
1101da2e3ebdSchin 				break;
1102da2e3ebdSchin 			case '0':
1103da2e3ebdSchin 				c = 0;
1104da2e3ebdSchin 				cpmax = cp + 4;
1105da2e3ebdSchin 				while(++cp<cpmax && *cp>='0' && *cp<='7')
1106da2e3ebdSchin 				{
1107da2e3ebdSchin 					c <<= 3;
1108da2e3ebdSchin 					c |= (*cp-'0');
1109da2e3ebdSchin 				}
11105ae8bd53SToomas Soome 				/* FALLTHROUGH */
1111da2e3ebdSchin 			default:
1112da2e3ebdSchin 				cp--;
1113da2e3ebdSchin 		}
1114da2e3ebdSchin 		stakputc(c);
1115da2e3ebdSchin 	}
1116da2e3ebdSchin done:
1117da2e3ebdSchin 	c = staktell()-offset;
1118da2e3ebdSchin 	stakputc(0);
1119da2e3ebdSchin 	stakseek(offset);
1120da2e3ebdSchin 	return(c);
1121da2e3ebdSchin }
1122