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