summaryrefslogtreecommitdiff
path: root/src/cmd/ksh93/bltins/print.c
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2012-06-24 22:28:35 +0000
committerIgor Pashev <pashev.igor@gmail.com>2012-06-24 22:28:35 +0000
commit3950ffe2a485479f6561c27364d3d7df5a21d124 (patch)
tree468c6e14449d1b1e279222ec32f676b0311917d2 /src/cmd/ksh93/bltins/print.c
downloadksh-upstream.tar.gz
Imported Upstream version 93u+upstream
Diffstat (limited to 'src/cmd/ksh93/bltins/print.c')
-rw-r--r--src/cmd/ksh93/bltins/print.c1058
1 files changed, 1058 insertions, 0 deletions
diff --git a/src/cmd/ksh93/bltins/print.c b/src/cmd/ksh93/bltins/print.c
new file mode 100644
index 0000000..011bccd
--- /dev/null
+++ b/src/cmd/ksh93/bltins/print.c
@@ -0,0 +1,1058 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1982-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/*
+ * echo [arg...]
+ * print [-nrps] [-f format] [-u filenum] [arg...]
+ * printf format [arg...]
+ *
+ * David Korn
+ * AT&T Labs
+ */
+
+#include "defs.h"
+#include <error.h>
+#include <stak.h>
+#include "io.h"
+#include "name.h"
+#include "history.h"
+#include "builtins.h"
+#include "streval.h"
+#include <tmx.h>
+#include <ccode.h>
+
+union types_t
+{
+ unsigned char c;
+ short h;
+ int i;
+ long l;
+ Sflong_t ll;
+ Sfdouble_t ld;
+ double d;
+ float f;
+ char *s;
+ int *ip;
+ char **p;
+};
+
+struct printf
+{
+ Sffmt_t hdr;
+ int argsize;
+ int intvar;
+ char **nextarg;
+ char *lastarg;
+ char cescape;
+ char err;
+ Shell_t *sh;
+};
+
+static int extend(Sfio_t*,void*, Sffmt_t*);
+static const char preformat[] = "";
+static char *genformat(char*);
+static int fmtvecho(const char*, struct printf*);
+static ssize_t fmtbase64(Sfio_t*, char*, int);
+
+struct print
+{
+ Shell_t *sh;
+ const char *options;
+ char raw;
+ char echon;
+};
+
+static char* nullarg[] = { 0, 0 };
+
+#if !SHOPT_ECHOPRINT
+ int B_echo(int argc, char *argv[],Shbltin_t *context)
+ {
+ static char bsd_univ;
+ struct print prdata;
+ prdata.options = sh_optecho+5;
+ prdata.raw = prdata.echon = 0;
+ prdata.sh = context->shp;
+ NOT_USED(argc);
+ /* This mess is because /bin/echo on BSD is different */
+ if(!prdata.sh->universe)
+ {
+ register char *universe;
+ if(universe=astconf("UNIVERSE",0,0))
+ bsd_univ = (strcmp(universe,"ucb")==0);
+ prdata.sh->universe = 1;
+ }
+ if(!bsd_univ)
+ return(b_print(0,argv,(Shbltin_t*)&prdata));
+ prdata.options = sh_optecho;
+ prdata.raw = 1;
+ while(argv[1] && *argv[1]=='-')
+ {
+ if(strcmp(argv[1],"-n")==0)
+ prdata.echon = 1;
+#if !SHOPT_ECHOE
+ else if(strcmp(argv[1],"-e")==0)
+ prdata.raw = 0;
+ else if(strcmp(argv[1],"-ne")==0 || strcmp(argv[1],"-en")==0)
+ {
+ prdata.raw = 0;
+ prdata.echon = 1;
+ }
+#endif /* SHOPT_ECHOE */
+ else
+ break;
+ argv++;
+ }
+ return(b_print(0,argv,(Shbltin_t*)&prdata));
+ }
+#endif /* SHOPT_ECHOPRINT */
+
+int b_printf(int argc, char *argv[],Shbltin_t *context)
+{
+ struct print prdata;
+ NOT_USED(argc);
+ memset(&prdata,0,sizeof(prdata));
+ prdata.sh = context->shp;
+ prdata.options = sh_optprintf;
+ return(b_print(-1,argv,(Shbltin_t*)&prdata));
+}
+
+/*
+ * argc==0 when called from echo
+ * argc==-1 when called from printf
+ */
+
+int b_print(int argc, char *argv[], Shbltin_t *context)
+{
+ register Sfio_t *outfile;
+ register int exitval=0,n, fd = 1;
+ register Shell_t *shp = context->shp;
+ const char *options, *msg = e_file+4;
+ char *format = 0;
+ int sflag = 0, nflag=0, rflag=0, vflag=0;
+ if(argc>0)
+ {
+ options = sh_optprint;
+ nflag = rflag = 0;
+ format = 0;
+ }
+ else
+ {
+ struct print *pp = (struct print*)context;
+ shp = pp->sh;
+ options = pp->options;
+ if(argc==0)
+ {
+ nflag = pp->echon;
+ rflag = pp->raw;
+ argv++;
+ goto skip;
+ }
+ }
+ while((n = optget(argv,options))) switch(n)
+ {
+ case 'n':
+ nflag++;
+ break;
+ case 'p':
+ fd = shp->coutpipe;
+ msg = e_query;
+ break;
+ case 'f':
+ format = opt_info.arg;
+ break;
+ case 's':
+ /* print to history file */
+ if(!sh_histinit((void*)shp))
+ errormsg(SH_DICT,ERROR_system(1),e_history);
+ fd = sffileno(shp->gd->hist_ptr->histfp);
+ sh_onstate(SH_HISTORY);
+ sflag++;
+ break;
+ case 'e':
+ rflag = 0;
+ break;
+ case 'r':
+ rflag = 1;
+ break;
+ case 'u':
+ fd = (int)strtol(opt_info.arg,&opt_info.arg,10);
+ if(*opt_info.arg)
+ fd = -1;
+ else if(!sh_iovalidfd(shp,fd))
+ fd = -1;
+ else if(!(shp->inuse_bits&(1<<fd)) && (sh_inuse(shp,fd) || (shp->gd->hist_ptr && fd==sffileno(shp->gd->hist_ptr->histfp))))
+
+ fd = -1;
+ break;
+ case 'v':
+ vflag='v';
+ break;
+ case 'C':
+ vflag='C';
+ break;
+ case ':':
+ /* The following is for backward compatibility */
+#if OPT_VERSION >= 19990123
+ if(strcmp(opt_info.name,"-R")==0)
+#else
+ if(strcmp(opt_info.option,"-R")==0)
+#endif
+ {
+ rflag = 1;
+ if(error_info.errors==0)
+ {
+ argv += opt_info.index+1;
+ /* special case test for -Rn */
+ if(strchr(argv[-1],'n'))
+ nflag++;
+ if(*argv && strcmp(*argv,"-n")==0)
+ {
+
+ nflag++;
+ argv++;
+ }
+ goto skip2;
+ }
+ }
+ else
+ errormsg(SH_DICT,2, "%s", opt_info.arg);
+ break;
+ case '?':
+ errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg);
+ break;
+ }
+ argv += opt_info.index;
+ if(error_info.errors || (argc<0 && !(format = *argv++)))
+ errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0));
+ if(vflag && format)
+ errormsg(SH_DICT,ERROR_usage(2),"-%c and -f are mutually exclusive",vflag);
+skip:
+ if(format)
+ format = genformat(format);
+ /* handle special case of '-' operand for print */
+ if(argc>0 && *argv && strcmp(*argv,"-")==0 && strcmp(argv[-1],"--"))
+ argv++;
+skip2:
+ if(fd < 0)
+ {
+ errno = EBADF;
+ n = 0;
+ }
+ else if(!(n=shp->fdstatus[fd]))
+ n = sh_iocheckfd(shp,fd);
+ if(!(n&IOWRITE))
+ {
+ /* don't print error message for stdout for compatibility */
+ if(fd==1)
+ return(1);
+ errormsg(SH_DICT,ERROR_system(1),msg);
+ }
+ if(!(outfile=shp->sftable[fd]))
+ {
+ sh_onstate(SH_NOTRACK);
+ n = SF_WRITE|((n&IOREAD)?SF_READ:0);
+ shp->sftable[fd] = outfile = sfnew(NIL(Sfio_t*),shp->outbuff,IOBSIZE,fd,n);
+ sh_offstate(SH_NOTRACK);
+ sfpool(outfile,shp->outpool,SF_WRITE);
+ }
+ /* turn off share to guarantee atomic writes for printf */
+ n = sfset(outfile,SF_SHARE|SF_PUBLIC,0);
+ if(format)
+ {
+ /* printf style print */
+ Sfio_t *pool;
+ struct printf pdata;
+ memset(&pdata, 0, sizeof(pdata));
+ pdata.sh = shp;
+ pdata.hdr.version = SFIO_VERSION;
+ pdata.hdr.extf = extend;
+ pdata.nextarg = argv;
+ sh_offstate(SH_STOPOK);
+ pool=sfpool(sfstderr,NIL(Sfio_t*),SF_WRITE);
+ do
+ {
+ if(shp->trapnote&SH_SIGSET)
+ break;
+ pdata.hdr.form = format;
+ sfprintf(outfile,"%!",&pdata);
+ } while(*pdata.nextarg && pdata.nextarg!=argv);
+ if(pdata.nextarg == nullarg && pdata.argsize>0)
+ sfwrite(outfile,stakptr(staktell()),pdata.argsize);
+ if(sffileno(outfile)!=sffileno(sfstderr))
+ sfsync(outfile);
+ sfpool(sfstderr,pool,SF_WRITE);
+ exitval = pdata.err;
+ }
+ else if(vflag)
+ {
+ while(*argv)
+ {
+ fmtbase64(outfile,*argv++,vflag=='C');
+ if(!nflag)
+ sfputc(outfile,'\n');
+ }
+ }
+ else
+ {
+ /* echo style print */
+ if(nflag && !argv[0])
+ sfsync((Sfio_t*)0);
+ else if(sh_echolist(shp,outfile,rflag,argv) && !nflag)
+ sfputc(outfile,'\n');
+ }
+ if(sflag)
+ {
+ hist_flush(shp->gd->hist_ptr);
+ sh_offstate(SH_HISTORY);
+ }
+ else if(n&SF_SHARE)
+ {
+ sfset(outfile,SF_SHARE|SF_PUBLIC,1);
+ sfsync(outfile);
+ }
+ return(exitval);
+}
+
+/*
+ * echo the argument list onto <outfile>
+ * if <raw> is non-zero then \ is not a special character.
+ * returns 0 for \c otherwise 1.
+ */
+
+int sh_echolist(Shell_t *shp,Sfio_t *outfile, int raw, char *argv[])
+{
+ register char *cp;
+ register int n;
+ struct printf pdata;
+ pdata.cescape = 0;
+ pdata.err = 0;
+ while(!pdata.cescape && (cp= *argv++))
+ {
+ if(!raw && (n=fmtvecho(cp,&pdata))>=0)
+ {
+ if(n)
+ sfwrite(outfile,stakptr(staktell()),n);
+ }
+ else
+ sfputr(outfile,cp,-1);
+ if(*argv)
+ sfputc(outfile,' ');
+ sh_sigcheck(shp);
+ }
+ return(!pdata.cescape);
+}
+
+/*
+ * modified version of stresc for generating formats
+ */
+static char strformat(char *s)
+{
+ register char* t;
+ register int c;
+ char* b;
+ char* p;
+#if SHOPT_MULTIBYTE && defined(FMT_EXP_WIDE)
+ int w;
+#endif
+
+ b = t = s;
+ for (;;)
+ {
+ switch (c = *s++)
+ {
+ case '\\':
+ if(*s==0)
+ break;
+#if SHOPT_MULTIBYTE && defined(FMT_EXP_WIDE)
+ c = chrexp(s - 1, &p, &w, FMT_EXP_CHAR|FMT_EXP_LINE|FMT_EXP_WIDE);
+#else
+ c = chresc(s - 1, &p);
+#endif
+ s = p;
+#if SHOPT_MULTIBYTE
+#if defined(FMT_EXP_WIDE)
+ if(w)
+ {
+ t += mbwide() ? mbconv(t, c) : wc2utf8(t, c);
+ continue;
+ }
+#else
+ if(c>UCHAR_MAX && mbwide())
+ {
+ t += mbconv(t, c);
+ continue;
+ }
+#endif /* FMT_EXP_WIDE */
+#endif /* SHOPT_MULTIBYTE */
+ if(c=='%')
+ *t++ = '%';
+ else if(c==0)
+ {
+ *t++ = '%';
+ c = 'Z';
+ }
+ break;
+ case 0:
+ *t = 0;
+ return(t - b);
+ }
+ *t++ = c;
+ }
+}
+
+
+static char *genformat(char *format)
+{
+ register char *fp;
+ stakseek(0);
+ stakputs(preformat);
+ stakputs(format);
+ fp = (char*)stakfreeze(1);
+ strformat(fp+sizeof(preformat)-1);
+ return(fp);
+}
+
+static char *fmthtml(const char *string, int flags)
+{
+ register const char *cp = string;
+ register int c, offset = staktell();
+ if(!(flags&SFFMT_ALTER))
+ {
+ while(c= *(unsigned char*)cp++)
+ {
+#if SHOPT_MULTIBYTE
+ register int s;
+ if((s=mbsize(cp-1)) > 1)
+ {
+ cp += (s-1);
+ continue;
+ }
+#endif /* SHOPT_MULTIBYTE */
+ if(c=='<')
+ stakputs("&lt;");
+ else if(c=='>')
+ stakputs("&gt;");
+ else if(c=='&')
+ stakputs("&amp;");
+ else if(c=='"')
+ stakputs("&quot;");
+ else if(c=='\'')
+ stakputs("&apos;");
+ else if(c==' ')
+ stakputs("&nbsp;");
+ else if(!isprint(c) && c!='\n' && c!='\r')
+ sfprintf(stkstd,"&#%X;",CCMAPC(c,CC_NATIVE,CC_ASCII));
+ else
+ stakputc(c);
+ }
+ }
+ else
+ {
+ while(c= *(unsigned char*)cp++)
+ {
+ if(strchr("!*'();@&+$,#[]<>~.\"{}|\\-`^% ",c) || (!isprint(c) && c!='\n' && c!='\r'))
+ sfprintf(stkstd,"%%%02X",CCMAPC(c,CC_NATIVE,CC_ASCII));
+ else
+ stakputc(c);
+ }
+ }
+ stakputc(0);
+ return(stakptr(offset));
+}
+
+#if 1
+static ssize_t fmtbase64(Sfio_t *iop, char *string, int alt)
+#else
+static void *fmtbase64(char *string, ssize_t *sz, int alt)
+#endif
+{
+ char *cp;
+ Sfdouble_t d;
+ ssize_t size;
+ Namval_t *np = nv_open(string, NiL, NV_VARNAME|NV_NOASSIGN|NV_NOADD);
+ Namarr_t *ap;
+ static union types_t number;
+ if(!np || nv_isnull(np))
+ {
+ if(sh_isoption(SH_NOUNSET))
+ errormsg(SH_DICT,ERROR_exit(1),e_notset,string);
+ return(0);
+ }
+ if(nv_isattr(np,NV_INTEGER))
+ {
+ d = nv_getnum(np);
+ if(nv_isattr(np,NV_DOUBLE))
+ {
+ if(nv_isattr(np,NV_LONG))
+ {
+ size = sizeof(Sfdouble_t);
+ number.ld = d;
+ }
+ else if(nv_isattr(np,NV_SHORT))
+ {
+ size = sizeof(float);
+ number.f = (float)d;
+ }
+ else
+ {
+ size = sizeof(double);
+ number.d = (double)d;
+ }
+ }
+ else
+ {
+ if(nv_isattr(np,NV_LONG))
+ {
+ size = sizeof(Sflong_t);
+ number.ll = (Sflong_t)d;
+ }
+ else if(nv_isattr(np,NV_SHORT))
+ {
+ size = sizeof(short);
+ number.h = (short)d;
+ }
+ else
+ {
+ size = sizeof(short);
+ number.i = (int)d;
+ }
+ }
+#if 1
+ return(sfwrite(iop, (void*)&number, size));
+#else
+ if(sz)
+ *sz = size;
+ return((void*)&number);
+#endif
+ }
+ if(nv_isattr(np,NV_BINARY))
+#if 1
+ {
+ Namfun_t *fp;
+ for(fp=np->nvfun; fp;fp=fp->next)
+ {
+ if(fp->disc && fp->disc->writef)
+ break;
+ }
+ if(fp)
+ return (*fp->disc->writef)(np, iop, 0, fp);
+ else
+ {
+ int n = nv_size(np);
+ if(nv_isarray(np))
+ {
+ nv_onattr(np,NV_RAW);
+ cp = nv_getval(np);
+ nv_offattr(np,NV_RAW);
+ }
+ else
+ cp = (char*)np->nvalue.cp;
+ if((size = n)==0)
+ size = strlen(cp);
+ size = sfwrite(iop, cp, size);
+ return(n?n:size);
+ }
+ }
+ else if(nv_isarray(np) && (ap=nv_arrayptr(np)) && array_elem(ap) && (ap->nelem&(ARRAY_UNDEF|ARRAY_SCAN)))
+ {
+ nv_outnode(np,iop,(alt?-1:0),0);
+ sfputc(iop,')');
+ return(sftell(iop));
+ }
+ else
+ {
+ if(alt && nv_isvtree(np))
+ nv_onattr(np,NV_EXPORT);
+ else
+ alt = 0;
+ cp = nv_getval(np);
+ if(alt)
+ nv_offattr(np,NV_EXPORT);
+ if(!cp)
+ return(0);
+ size = strlen(cp);
+ return(sfwrite(iop,cp,size));
+ }
+#else
+ nv_onattr(np,NV_RAW);
+ cp = nv_getval(np);
+ if(nv_isattr(np,NV_BINARY))
+ nv_offattr(np,NV_RAW);
+ if((size = nv_size(np))==0)
+ size = strlen(cp);
+ if(sz)
+ *sz = size;
+ return((void*)cp);
+#endif
+}
+
+static int varname(const char *str, int n)
+{
+ register int c,dot=1,len=1;
+ if(n < 0)
+ {
+ if(*str=='.')
+ str++;
+ n = strlen(str);
+ }
+ for(;n > 0; n-=len)
+ {
+#ifdef SHOPT_MULTIBYTE
+ len = mbsize(str);
+ c = mbchar(str);
+#else
+ c = *(unsigned char*)str++;
+#endif
+ if(dot && !(isalpha(c)||c=='_'))
+ break;
+ else if(dot==0 && !(isalnum(c) || c=='_' || c == '.'))
+ break;
+ dot = (c=='.');
+ }
+ return(n==0);
+}
+
+static int extend(Sfio_t* sp, void* v, Sffmt_t* fe)
+{
+ char* lastchar = "";
+ register int neg = 0;
+ Sfdouble_t d;
+ Sfdouble_t longmin = LDBL_LLONG_MIN;
+ Sfdouble_t longmax = LDBL_LLONG_MAX;
+ int format = fe->fmt;
+ int n;
+ int fold = fe->base;
+ union types_t* value = (union types_t*)v;
+ struct printf* pp = (struct printf*)fe;
+ Shell_t *shp = pp->sh;
+ register char* argp = *pp->nextarg;
+ char *w,*s;
+
+ if(fe->n_str>0 && varname(fe->t_str,fe->n_str) && (!argp || varname(argp,-1)))
+ {
+ if(argp)
+ pp->lastarg = argp;
+ else
+ argp = pp->lastarg;
+ if(argp)
+ {
+ sfprintf(pp->sh->strbuf,"%s.%.*s%c",argp,fe->n_str,fe->t_str,0);
+ argp = sfstruse(pp->sh->strbuf);
+ }
+ }
+ else
+ pp->lastarg = 0;
+ fe->flags |= SFFMT_VALUE;
+ if(!argp || format=='Z')
+ {
+ switch(format)
+ {
+ case 'c':
+ value->c = 0;
+ fe->flags &= ~SFFMT_LONG;
+ break;
+ case 'q':
+ format = 's';
+ /* FALL THROUGH */
+ case 's':
+ case 'H':
+ case 'B':
+ case 'P':
+ case 'R':
+ case 'Z':
+ case 'b':
+ fe->fmt = 's';
+ fe->size = -1;
+ fe->base = -1;
+ value->s = "";
+ fe->flags &= ~SFFMT_LONG;
+ break;
+ case 'a':
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'A':
+ case 'E':
+ case 'F':
+ case 'G':
+ if(SFFMT_LDOUBLE)
+ value->ld = 0.;
+ else
+ value->d = 0.;
+ break;
+ case 'n':
+ value->ip = &pp->intvar;
+ break;
+ case 'Q':
+ value->ll = 0;
+ break;
+ case 'T':
+ fe->fmt = 'd';
+ value->ll = tmxgettime();
+ break;
+ default:
+ if(!strchr("DdXxoUu",format))
+ errormsg(SH_DICT,ERROR_exit(1),e_formspec,format);
+ fe->fmt = 'd';
+ value->ll = 0;
+ break;
+ }
+ }
+ else
+ {
+ switch(format)
+ {
+ case 'p':
+ value->p = (char**)strtol(argp,&lastchar,10);
+ break;
+ case 'n':
+ {
+ Namval_t *np;
+ np = nv_open(argp,shp->var_tree,NV_VARNAME|NV_NOASSIGN|NV_NOARRAY);
+ _nv_unset(np,0);
+ nv_onattr(np,NV_INTEGER);
+ if (np->nvalue.lp = new_of(int32_t,0))
+ *np->nvalue.lp = 0;
+ nv_setsize(np,10);
+ if(sizeof(int)==sizeof(int32_t))
+ value->ip = (int*)np->nvalue.lp;
+ else
+ {
+ int32_t sl = 1;
+ value->ip = (int*)(((char*)np->nvalue.lp) + (*((char*)&sl) ? 0 : sizeof(int)));
+ }
+ nv_close(np);
+ break;
+ }
+ case 'q':
+ case 'b':
+ case 's':
+ case 'B':
+ case 'H':
+ case 'P':
+ case 'R':
+ fe->fmt = 's';
+ fe->size = -1;
+ if(format=='s' && fe->base>=0)
+ {
+ value->p = pp->nextarg;
+ pp->nextarg = nullarg;
+ }
+ else
+ {
+ fe->base = -1;
+ value->s = argp;
+ }
+ fe->flags &= ~SFFMT_LONG;
+ break;
+ case 'c':
+ if(mbwide() && (n = mbsize(argp)) > 1)
+ {
+ fe->fmt = 's';
+ fe->size = n;
+ value->s = argp;
+ }
+ else if(fe->base >=0)
+ value->s = argp;
+ else
+ value->c = *argp;
+ fe->flags &= ~SFFMT_LONG;
+ break;
+ case 'o':
+ case 'x':
+ case 'X':
+ case 'u':
+ case 'U':
+ longmax = LDBL_ULLONG_MAX;
+ case '.':
+ if(fe->size==2 && strchr("bcsqHPRQTZ",*fe->form))
+ {
+ value->ll = ((unsigned char*)argp)[0];
+ break;
+ }
+ case 'd':
+ case 'D':
+ case 'i':
+ switch(*argp)
+ {
+ case '\'':
+ case '"':
+ w = argp + 1;
+ if(mbwide() && mbsize(w) > 1)
+ value->ll = mbchar(w);
+ else
+ value->ll = *(unsigned char*)w++;
+ if(w[0] && (w[0] != argp[0] || w[1]))
+ {
+ errormsg(SH_DICT,ERROR_warn(0),e_charconst,argp);
+ pp->err = 1;
+ }
+ break;
+ default:
+ d = sh_strnum(argp,&lastchar,0);
+ if(d<longmin)
+ {
+ errormsg(SH_DICT,ERROR_warn(0),e_overflow,argp);
+ pp->err = 1;
+ d = longmin;
+ }
+ else if(d>longmax)
+ {
+ errormsg(SH_DICT,ERROR_warn(0),e_overflow,argp);
+ pp->err = 1;
+ d = longmax;
+ }
+ value->ll = (Sflong_t)d;
+ if(lastchar == *pp->nextarg)
+ {
+ value->ll = *argp;
+ lastchar = "";
+ }
+ break;
+ }
+ if(neg)
+ value->ll = -value->ll;
+ fe->size = sizeof(value->ll);
+ break;
+ case 'a':
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'A':
+ case 'E':
+ case 'F':
+ case 'G':
+ d = sh_strnum(*pp->nextarg,&lastchar,0);
+ switch(*argp)
+ {
+ case '\'':
+ case '"':
+ d = ((unsigned char*)argp)[1];
+ if(argp[2] && (argp[2] != argp[0] || argp[3]))
+ {
+ errormsg(SH_DICT,ERROR_warn(0),e_charconst,argp);
+ pp->err = 1;
+ }
+ break;
+ default:
+ d = sh_strnum(*pp->nextarg,&lastchar,0);
+ break;
+ }
+ if(SFFMT_LDOUBLE)
+ {
+ value->ld = d;
+ fe->size = sizeof(value->ld);
+ }
+ else
+ {
+ value->d = d;
+ fe->size = sizeof(value->d);
+ }
+ break;
+ case 'Q':
+ value->ll = (Sflong_t)strelapsed(*pp->nextarg,&lastchar,1);
+ break;
+ case 'T':
+ value->ll = (Sflong_t)tmxdate(*pp->nextarg,&lastchar,TMX_NOW);
+ break;
+ default:
+ value->ll = 0;
+ fe->fmt = 'd';
+ fe->size = sizeof(value->ll);
+ errormsg(SH_DICT,ERROR_exit(1),e_formspec,format);
+ break;
+ }
+ if (format == '.')
+ value->i = value->ll;
+ if(*lastchar)
+ {
+ errormsg(SH_DICT,ERROR_warn(0),e_argtype,format);
+ pp->err = 1;
+ }
+ pp->nextarg++;
+ }
+ switch(format)
+ {
+ case 'Z':
+ fe->fmt = 'c';
+ fe->base = -1;
+ value->c = 0;
+ break;
+ case 'b':
+ if((n=fmtvecho(value->s,pp))>=0)
+ {
+ if(pp->nextarg == nullarg)
+ {
+ pp->argsize = n;
+ return -1;
+ }
+ value->s = stakptr(staktell());
+ fe->size = n;
+ }
+ break;
+ case 'B':
+ if(!shp->strbuf2)
+ shp->strbuf2 = sfstropen();
+ fe->size = fmtbase64(shp->strbuf2,value->s, fe->flags&SFFMT_ALTER);
+ value->s = sfstruse(shp->strbuf2);
+ fe->flags |= SFFMT_SHORT;
+ break;
+ case 'H':
+ value->s = fmthtml(value->s, fe->flags);
+ break;
+ case 'q':
+ value->s = sh_fmtqf(value->s, !!(fe->flags & SFFMT_ALTER), fold);
+ break;
+ case 'P':
+ s = fmtmatch(value->s);
+ if(!s || *s==0)
+ errormsg(SH_DICT,ERROR_exit(1),e_badregexp,value->s);
+ value->s = s;
+ break;
+ case 'R':
+ s = fmtre(value->s);
+ if(!s || *s==0)
+ errormsg(SH_DICT,ERROR_exit(1),e_badregexp,value->s);
+ value->s = s;
+ break;
+ case 'Q':
+ if (fe->n_str>0)
+ {
+ fe->fmt = 'd';
+ fe->size = sizeof(value->ll);
+ }
+ else
+ {
+ value->s = fmtelapsed(value->ll, 1);
+ fe->fmt = 's';
+ fe->size = -1;
+ }
+ break;
+ case 'T':
+ if(fe->n_str>0)
+ {
+ n = fe->t_str[fe->n_str];
+ fe->t_str[fe->n_str] = 0;
+ value->s = fmttmx(fe->t_str, value->ll);
+ fe->t_str[fe->n_str] = n;
+ }
+ else value->s = fmttmx(NIL(char*), value->ll);
+ fe->fmt = 's';
+ fe->size = -1;
+ break;
+ }
+ return 0;
+}
+
+/*
+ * construct System V echo string out of <cp>
+ * If there are not escape sequences, returns -1
+ * Otherwise, puts null terminated result on stack, but doesn't freeze it
+ * returns length of output.
+ */
+
+static int fmtvecho(const char *string, struct printf *pp)
+{
+ register const char *cp = string, *cpmax;
+ register int c;
+ register int offset = staktell();
+#if SHOPT_MULTIBYTE
+ int chlen;
+ if(mbwide())
+ {
+ while(1)
+ {
+ if ((chlen = mbsize(cp)) > 1)
+ /* Skip over multibyte characters */
+ cp += chlen;
+ else if((c= *cp++)==0 || c == '\\')
+ break;
+ }
+ }
+ else
+#endif /* SHOPT_MULTIBYTE */
+ while((c= *cp++) && (c!='\\'));
+ if(c==0)
+ return(-1);
+ c = --cp - string;
+ if(c>0)
+ stakwrite((void*)string,c);
+ for(; c= *cp; cp++)
+ {
+#if SHOPT_MULTIBYTE
+ if (mbwide() && ((chlen = mbsize(cp)) > 1))
+ {
+ stakwrite(cp,chlen);
+ cp += (chlen-1);
+ continue;
+ }
+#endif /* SHOPT_MULTIBYTE */
+ if( c=='\\') switch(*++cp)
+ {
+ case 'E':
+ c = ('a'==97?'\033':39); /* ASCII/EBCDIC */
+ break;
+ case 'a':
+ c = '\a';
+ break;
+ case 'b':
+ c = '\b';
+ break;
+ case 'c':
+ pp->cescape++;
+ pp->nextarg = nullarg;
+ goto done;
+ case 'f':
+ c = '\f';
+ break;
+ case 'n':
+ c = '\n';
+ break;
+ case 'r':
+ c = '\r';
+ break;
+ case 'v':
+ c = '\v';
+ break;
+ case 't':
+ c = '\t';
+ break;
+ case '\\':
+ c = '\\';
+ break;
+ case '0':
+ c = 0;
+ cpmax = cp + 4;
+ while(++cp<cpmax && *cp>='0' && *cp<='7')
+ {
+ c <<= 3;
+ c |= (*cp-'0');
+ }
+ default:
+ cp--;
+ }
+ stakputc(c);
+ }
+done:
+ c = staktell()-offset;
+ stakputc(0);
+ stakseek(offset);
+ return(c);
+}