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