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