1 /***********************************************************************
3 * This software is part of the ast package *
4 * Copyright (c) 1982-2010 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Common Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
9 * A copy of the License is available at *
10 * http://www.opensource.org/licenses/cpl1.0.txt *
11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
13 * Information and Software Systems Research *
17 * David Korn <dgk@research.att.com> *
19 ***********************************************************************/
23 * print [-nrps] [-f format] [-u filenum] [arg...]
24 * printf format [arg...]
68 static int extend(Sfio_t
*,void*, Sffmt_t
*);
69 static const char preformat
[] = "";
70 static char *genformat(char*);
71 static int fmtvecho(const char*, struct printf
*);
72 static ssize_t
fmtbase64(Sfio_t
*, char*, int);
82 static char* nullarg
[] = { 0, 0 };
85 int B_echo(int argc
, char *argv
[],void *extra
)
89 prdata
.options
= sh_optecho
+5;
90 prdata
.raw
= prdata
.echon
= 0;
91 prdata
.sh
= ((Shbltin_t
*)extra
)->shp
;
93 /* This mess is because /bin/echo on BSD is different */
94 if(!prdata
.sh
->universe
)
96 register char *universe
;
97 if(universe
=astconf("UNIVERSE",0,0))
98 bsd_univ
= (strcmp(universe
,"ucb")==0);
99 prdata
.sh
->universe
= 1;
102 return(b_print(0,argv
,&prdata
));
103 prdata
.options
= sh_optecho
;
105 while(argv
[1] && *argv
[1]=='-')
107 if(strcmp(argv
[1],"-n")==0)
110 else if(strcmp(argv
[1],"-e")==0)
112 else if(strcmp(argv
[1],"-ne")==0 || strcmp(argv
[1],"-en")==0)
117 #endif /* SHOPT_ECHOE */
122 return(b_print(0,argv
,&prdata
));
124 #endif /* SHOPT_ECHOPRINT */
126 int b_printf(int argc
, char *argv
[],void *extra
)
130 memset(&prdata
,0,sizeof(prdata
));
131 prdata
.sh
= ((Shbltin_t
*)extra
)->shp
;
132 prdata
.options
= sh_optprintf
;
133 return(b_print(-1,argv
,&prdata
));
137 * argc==0 when called from echo
138 * argc==-1 when called from printf
141 int b_print(int argc
, char *argv
[], void *extra
)
143 register Sfio_t
*outfile
;
144 register int exitval
=0,n
, fd
= 1;
145 register Shell_t
*shp
= ((Shbltin_t
*)extra
)->shp
;
146 const char *options
, *msg
= e_file
+4;
148 int sflag
= 0, nflag
=0, rflag
=0, vflag
=0;
151 options
= sh_optprint
;
157 struct print
*pp
= (struct print
*)extra
;
159 options
= pp
->options
;
168 while((n
= optget(argv
,options
))) switch(n
)
178 format
= opt_info
.arg
;
181 /* print to history file */
182 if(!sh_histinit((void*)shp
))
183 errormsg(SH_DICT
,ERROR_system(1),e_history
);
184 fd
= sffileno(shp
->hist_ptr
->histfp
);
185 sh_onstate(SH_HISTORY
);
195 fd
= (int)strtol(opt_info
.arg
,&opt_info
.arg
,10);
198 else if(fd
<0 || fd
>= shp
->lim
.open_max
)
200 else if(!(sh
.inuse_bits
&(1<<fd
)) && (sh_inuse(fd
) || (shp
->hist_ptr
&& fd
==sffileno(shp
->hist_ptr
->histfp
))))
211 /* The following is for backward compatibility */
212 #if OPT_VERSION >= 19990123
213 if(strcmp(opt_info
.name
,"-R")==0)
215 if(strcmp(opt_info
.option
,"-R")==0)
219 if(error_info
.errors
==0)
221 argv
+= opt_info
.index
+1;
222 /* special case test for -Rn */
223 if(strchr(argv
[-1],'n'))
225 if(*argv
&& strcmp(*argv
,"-n")==0)
235 errormsg(SH_DICT
,2, "%s", opt_info
.arg
);
238 errormsg(SH_DICT
,ERROR_usage(2), "%s", opt_info
.arg
);
241 argv
+= opt_info
.index
;
242 if(error_info
.errors
|| (argc
<0 && !(format
= *argv
++)))
243 errormsg(SH_DICT
,ERROR_usage(2),"%s",optusage((char*)0));
245 errormsg(SH_DICT
,ERROR_usage(2),"-%c and -f are mutually exclusive",vflag
);
248 format
= genformat(format
);
249 /* handle special case of '-' operand for print */
250 if(argc
>0 && *argv
&& strcmp(*argv
,"-")==0 && strcmp(argv
[-1],"--"))
258 else if(!(n
=shp
->fdstatus
[fd
]))
259 n
= sh_iocheckfd(shp
,fd
);
262 /* don't print error message for stdout for compatibility */
265 errormsg(SH_DICT
,ERROR_system(1),msg
);
267 if(!(outfile
=shp
->sftable
[fd
]))
269 sh_onstate(SH_NOTRACK
);
270 n
= SF_WRITE
|((n
&IOREAD
)?SF_READ
:0);
271 shp
->sftable
[fd
] = outfile
= sfnew(NIL(Sfio_t
*),shp
->outbuff
,IOBSIZE
,fd
,n
);
272 sh_offstate(SH_NOTRACK
);
273 sfpool(outfile
,shp
->outpool
,SF_WRITE
);
275 /* turn off share to guarantee atomic writes for printf */
276 n
= sfset(outfile
,SF_SHARE
|SF_PUBLIC
,0);
279 /* printf style print */
282 memset(&pdata
, 0, sizeof(pdata
));
284 pdata
.hdr
.version
= SFIO_VERSION
;
285 pdata
.hdr
.extf
= extend
;
286 pdata
.nextarg
= argv
;
287 sh_offstate(SH_STOPOK
);
288 pool
=sfpool(sfstderr
,NIL(Sfio_t
*),SF_WRITE
);
291 if(shp
->trapnote
&SH_SIGSET
)
293 pdata
.hdr
.form
= format
;
294 sfprintf(outfile
,"%!",&pdata
);
295 } while(*pdata
.nextarg
&& pdata
.nextarg
!=argv
);
296 if(pdata
.nextarg
== nullarg
&& pdata
.argsize
>0)
297 sfwrite(outfile
,stakptr(staktell()),pdata
.argsize
);
298 if(sffileno(outfile
)!=sffileno(sfstderr
))
300 sfpool(sfstderr
,pool
,SF_WRITE
);
307 fmtbase64(outfile
,*argv
++,vflag
=='C');
309 sfputc(outfile
,'\n');
314 /* echo style print */
315 if(nflag
&& !argv
[0])
317 else if(sh_echolist(outfile
,rflag
,argv
) && !nflag
)
318 sfputc(outfile
,'\n');
322 hist_flush(shp
->hist_ptr
);
323 sh_offstate(SH_HISTORY
);
327 sfset(outfile
,SF_SHARE
|SF_PUBLIC
,1);
334 * echo the argument list onto <outfile>
335 * if <raw> is non-zero then \ is not a special character.
336 * returns 0 for \c otherwise 1.
339 int sh_echolist(Sfio_t
*outfile
, int raw
, char *argv
[])
346 while(!pdata
.cescape
&& (cp
= *argv
++))
348 if(!raw
&& (n
=fmtvecho(cp
,&pdata
))>=0)
351 sfwrite(outfile
,stakptr(staktell()),n
);
354 sfputr(outfile
,cp
,-1);
359 return(!pdata
.cescape
);
363 * modified version of stresc for generating formats
365 static char strformat(char *s
)
380 c
= chresc(s
- 1, &p
);
383 if(c
>UCHAR_MAX
&& mbwide())
388 #endif /* SHOPT_MULTIBYTE */
406 static char *genformat(char *format
)
412 fp
= (char*)stakfreeze(1);
413 strformat(fp
+sizeof(preformat
)-1);
417 static char *fmthtml(const char *string
)
419 register const char *cp
= string
;
420 register int c
, offset
= staktell();
421 while(c
= *(unsigned char*)cp
++)
425 if((s
=mbsize(cp
-1)) > 1)
430 #endif /* SHOPT_MULTIBYTE */
443 else if(!isprint(c
) && c
!='\n' && c
!='\r')
444 sfprintf(stkstd
,"&#%X;",CCMAPC(c
,CC_NATIVE
,CC_ASCII
));
449 return(stakptr(offset
));
453 static ssize_t
fmtbase64(Sfio_t
*iop
, char *string
, int alt
)
455 static void *fmtbase64(char *string
, ssize_t
*sz
, int alt
)
461 Namval_t
*np
= nv_open(string
, NiL
, NV_VARNAME
|NV_NOASSIGN
|NV_NOADD
);
462 static union types_t number
;
463 if(!np
|| nv_isnull(np
))
465 if(sh_isoption(SH_NOUNSET
))
466 errormsg(SH_DICT
,ERROR_exit(1),e_notset
,string
);
469 if(nv_isattr(np
,NV_INTEGER
))
472 if(nv_isattr(np
,NV_DOUBLE
))
474 if(nv_isattr(np
,NV_LONG
))
476 size
= sizeof(Sfdouble_t
);
479 else if(nv_isattr(np
,NV_SHORT
))
481 size
= sizeof(float);
486 size
= sizeof(double);
487 number
.d
= (double)d
;
492 if(nv_isattr(np
,NV_LONG
))
494 size
= sizeof(Sflong_t
);
495 number
.ll
= (Sflong_t
)d
;
497 else if(nv_isattr(np
,NV_SHORT
))
499 size
= sizeof(short);
504 size
= sizeof(short);
509 return(sfwrite(iop
, (void*)&number
, size
));
513 return((void*)&number
);
516 if(nv_isattr(np
,NV_BINARY
))
520 for(fp
=np
->nvfun
; fp
;fp
=fp
->next
)
522 if(fp
->disc
&& fp
->disc
->writef
)
526 return (*fp
->disc
->writef
)(np
, iop
, 0, fp
);
532 nv_onattr(np
,NV_RAW
);
534 nv_offattr(np
,NV_RAW
);
537 cp
= (char*)np
->nvalue
.cp
;
540 size
= sfwrite(iop
, cp
, size
);
544 else if(nv_isarray(np
) && nv_arrayptr(np
))
546 nv_outnode(np
,iop
,(alt
?-1:0),0);
552 if(alt
&& nv_isvtree(np
))
553 nv_onattr(np
,NV_EXPORT
);
554 if(!(cp
= nv_getval(np
)))
557 return(sfwrite(iop
,cp
,size
));
560 nv_onattr(np
,NV_RAW
);
562 if(nv_isattr(np
,NV_BINARY
))
563 nv_offattr(np
,NV_RAW
);
564 if((size
= nv_size(np
))==0)
572 static int varname(const char *str
, int n
)
574 register int c
,dot
=1,len
=1;
583 #ifdef SHOPT_MULTIBYTE
587 c
= *(unsigned char*)str
++;
589 if(dot
&& !(isalpha(c
)||c
=='_'))
591 else if(dot
==0 && !(isalnum(c
) || c
=='_' || c
== '.'))
598 static int extend(Sfio_t
* sp
, void* v
, Sffmt_t
* fe
)
601 register int neg
= 0;
603 Sfdouble_t longmin
= LDBL_LLONG_MIN
;
604 Sfdouble_t longmax
= LDBL_LLONG_MAX
;
605 int format
= fe
->fmt
;
608 union types_t
* value
= (union types_t
*)v
;
609 struct printf
* pp
= (struct printf
*)fe
;
610 register char* argp
= *pp
->nextarg
;
613 if(fe
->n_str
>0 && varname(fe
->t_str
,fe
->n_str
) && (!argp
|| varname(argp
,-1)))
621 sfprintf(pp
->sh
->strbuf
,"%s.%.*s%c",argp
,fe
->n_str
,fe
->t_str
,0);
622 argp
= sfstruse(pp
->sh
->strbuf
);
627 fe
->flags
|= SFFMT_VALUE
;
628 if(!argp
|| format
=='Z')
634 fe
->flags
&= ~SFFMT_LONG
;
650 fe
->flags
&= ~SFFMT_LONG
;
666 value
->ip
= &pp
->intvar
;
673 value
->ll
= tmxgettime();
676 if(!strchr("DdXxoUu",format
))
677 errormsg(SH_DICT
,ERROR_exit(1),e_formspec
,format
);
688 value
->p
= (char**)strtol(argp
,&lastchar
,10);
693 np
= nv_open(argp
,sh
.var_tree
,NV_VARNAME
|NV_NOASSIGN
|NV_NOARRAY
);
695 nv_onattr(np
,NV_INTEGER
);
696 if (np
->nvalue
.lp
= new_of(int32_t,0))
699 if(sizeof(int)==sizeof(int32_t))
700 value
->ip
= (int*)np
->nvalue
.lp
;
704 value
->ip
= (int*)(((char*)np
->nvalue
.lp
) + (*((char*)&sl
) ? 0 : sizeof(int)));
718 if(format
=='s' && fe
->base
>=0)
720 value
->p
= pp
->nextarg
;
721 pp
->nextarg
= nullarg
;
728 fe
->flags
&= ~SFFMT_LONG
;
731 if(mbwide() && (n
= mbsize(argp
)) > 1)
737 else if(fe
->base
>=0)
741 fe
->flags
&= ~SFFMT_LONG
;
748 longmax
= LDBL_ULLONG_MAX
;
750 if(fe
->size
==2 && strchr("bcsqHPRQTZ",*fe
->form
))
752 value
->ll
= ((unsigned char*)argp
)[0];
763 if(mbwide() && mbsize(w
) > 1)
764 value
->ll
= mbchar(w
);
766 value
->ll
= *(unsigned char*)w
++;
767 if(w
[0] && (w
[0] != argp
[0] || w
[1]))
769 errormsg(SH_DICT
,ERROR_warn(0),e_charconst
,argp
);
774 d
= sh_strnum(argp
,&lastchar
,0);
777 errormsg(SH_DICT
,ERROR_warn(0),e_overflow
,argp
);
783 errormsg(SH_DICT
,ERROR_warn(0),e_overflow
,argp
);
787 value
->ll
= (Sflong_t
)d
;
788 if(lastchar
== *pp
->nextarg
)
796 value
->ll
= -value
->ll
;
797 fe
->size
= sizeof(value
->ll
);
807 d
= sh_strnum(*pp
->nextarg
,&lastchar
,0);
812 d
= ((unsigned char*)argp
)[1];
813 if(argp
[2] && (argp
[2] != argp
[0] || argp
[3]))
815 errormsg(SH_DICT
,ERROR_warn(0),e_charconst
,argp
);
820 d
= sh_strnum(*pp
->nextarg
,&lastchar
,0);
826 fe
->size
= sizeof(value
->ld
);
831 fe
->size
= sizeof(value
->d
);
835 value
->ll
= (Sflong_t
)strelapsed(*pp
->nextarg
,&lastchar
,1);
838 value
->ll
= (Sflong_t
)tmxdate(*pp
->nextarg
,&lastchar
,TMX_NOW
);
843 fe
->size
= sizeof(value
->ll
);
844 errormsg(SH_DICT
,ERROR_exit(1),e_formspec
,format
);
848 value
->i
= value
->ll
;
851 errormsg(SH_DICT
,ERROR_warn(0),e_argtype
,format
);
864 if((n
=fmtvecho(value
->s
,pp
))>=0)
866 if(pp
->nextarg
== nullarg
)
871 value
->s
= stakptr(staktell());
876 sh
.strbuf2
= sfstropen();
877 fe
->size
= fmtbase64(sh
.strbuf2
,value
->s
, fe
->flags
&SFFMT_ALTER
);
878 value
->s
= sfstruse(sh
.strbuf2
);
879 fe
->flags
|= SFFMT_SHORT
;
882 value
->s
= fmthtml(value
->s
);
885 value
->s
= sh_fmtqf(value
->s
, !!(fe
->flags
& SFFMT_ALTER
), fold
);
889 char *s
= fmtmatch(value
->s
);
891 errormsg(SH_DICT
,ERROR_exit(1),e_badregexp
,value
->s
);
896 value
->s
= fmtre(value
->s
);
898 errormsg(SH_DICT
,ERROR_exit(1),e_badregexp
,value
->s
);
904 fe
->size
= sizeof(value
->ll
);
908 value
->s
= fmtelapsed(value
->ll
, 1);
916 n
= fe
->t_str
[fe
->n_str
];
917 fe
->t_str
[fe
->n_str
] = 0;
918 value
->s
= fmttmx(fe
->t_str
, value
->ll
);
919 fe
->t_str
[fe
->n_str
] = n
;
921 else value
->s
= fmttmx(NIL(char*), value
->ll
);
930 * construct System V echo string out of <cp>
931 * If there are not escape sequences, returns -1
932 * Otherwise, puts null terminated result on stack, but doesn't freeze it
933 * returns length of output.
936 static int fmtvecho(const char *string
, struct printf
*pp
)
938 register const char *cp
= string
, *cpmax
;
940 register int offset
= staktell();
947 if ((chlen
= mbsize(cp
)) > 1)
948 /* Skip over multibyte characters */
950 else if((c
= *cp
++)==0 || c
== '\\')
955 #endif /* SHOPT_MULTIBYTE */
956 while((c
= *cp
++) && (c
!='\\'));
961 stakwrite((void*)string
,c
);
965 if (mbwide() && ((chlen
= mbsize(cp
)) > 1))
971 #endif /* SHOPT_MULTIBYTE */
972 if( c
=='\\') switch(*++cp
)
975 c
= ('a'==97?'\033':39); /* ASCII/EBCDIC */
985 pp
->nextarg
= nullarg
;
1008 while(++cp
<cpmax
&& *cp
>='0' && *cp
<='7')
1019 c
= staktell()-offset
;