Add GNU LGPL headers to all .c .C and .h files
[oweals/cde.git] / cde / programs / dtksh / ksh93 / src / cmd / ksh93 / bltins / print.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* $XConsortium: print.c /main/3 1995/11/01 16:29:05 rswiston $ */
24 /***************************************************************
25 *                                                              *
26 *                      AT&T - PROPRIETARY                      *
27 *                                                              *
28 *        THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF        *
29 *                    AT&T BELL LABORATORIES                    *
30 *         AND IS NOT TO BE DISCLOSED OR USED EXCEPT IN         *
31 *            ACCORDANCE WITH APPLICABLE AGREEMENTS             *
32 *                                                              *
33 *                Copyright (c) 1995 AT&T Corp.                 *
34 *              Unpublished & Not for Publication               *
35 *                     All Rights Reserved                      *
36 *                                                              *
37 *       The copyright notice above does not evidence any       *
38 *      actual or intended publication of such source code      *
39 *                                                              *
40 *               This software was created by the               *
41 *           Advanced Software Technology Department            *
42 *                    AT&T Bell Laboratories                    *
43 *                                                              *
44 *               For further information contact                *
45 *                    {research,attmail}!dgk                    *
46 *                                                              *
47 ***************************************************************/
48
49 /* : : generated by proto : : */
50
51 #if !defined(__PROTO__)
52 #if defined(__STDC__) || defined(__cplusplus) || defined(_proto) || defined(c_plusplus)
53 #if defined(__cplusplus)
54 #define __MANGLE__      "C"
55 #else
56 #define __MANGLE__
57 #endif
58 #define __STDARG__
59 #define __PROTO__(x)    x
60 #define __OTORP__(x)
61 #define __PARAM__(n,o)  n
62 #if !defined(__STDC__) && !defined(__cplusplus)
63 #if !defined(c_plusplus)
64 #define const
65 #endif
66 #define signed
67 #define void            int
68 #define volatile
69 #define __V_            char
70 #else
71 #define __V_            void
72 #endif
73 #else
74 #define __PROTO__(x)    ()
75 #define __OTORP__(x)    x
76 #define __PARAM__(n,o)  o
77 #define __MANGLE__
78 #define __V_            char
79 #define const
80 #define signed
81 #define void            int
82 #define volatile
83 #endif
84 #if defined(__cplusplus) || defined(c_plusplus)
85 #define __VARARG__      ...
86 #else
87 #define __VARARG__
88 #endif
89 #if defined(__STDARG__)
90 #define __VA_START__(p,a)       va_start(p,a)
91 #else
92 #define __VA_START__(p,a)       va_start(p)
93 #endif
94 #endif
95 #include        "defs.h"
96 #include        <error.h>
97 #include        <stak.h>
98 #include        "io.h"
99 #include        "name.h"
100 #include        "history.h"
101 #include        "builtins.h"
102
103 union types_t
104 {
105         unsigned char   c;
106         int             i;
107         long            l;
108         double          d;
109         float           f;
110         char            *s;
111         int             *ip;
112         char            **p;
113 };
114
115 static int              getarg __PROTO__((int, union types_t *));
116 static int              extend __PROTO__((char*, int, int, char**));
117 static char             *genformat __PROTO__((char*));
118 static int              fmtvecho __PROTO__((const char*));
119 #define fmtre(x)        (x)
120
121 static char             bsd_univ,cescape, raw, echon;
122 static char             **nextarg;
123 static const char       *options;
124 static const char       preformat[] = "%&%@";
125
126 /*
127  * Need to handle write failures to avoid locking output pool
128  */
129 static int outexceptf __PARAM__((Sfio_t* iop, int mode, Sfdisc_t* dp), (iop, mode, dp)) __OTORP__(Sfio_t* iop; int mode; Sfdisc_t* dp;){
130         if(mode==SF_DPOP || mode==SF_CLOSE)
131                 free((__V_*)dp);
132         else if(mode==SF_WRITE)
133         {
134                 int save = errno;
135                 sfpurge(iop);
136                 sfpool(iop,NIL(Sfio_t*),SF_WRITE);
137                 errno = save;
138                 error(ERROR_system(1),e_badwrite,sffileno(iop));
139         }
140         return(0);
141 }
142
143 #ifndef SHOPT_ECHOPRINT
144    int    b_echo __PARAM__((int argc, char *argv[],__V_ *extra), (argc, argv, extra)) __OTORP__(int argc; char *argv[];__V_ *extra;){
145         options = sh_optecho+5;
146         raw = echon = 0;
147         NOT_USED(argc);
148         NOT_USED(extra);
149         /* This mess is because /bin/echo on BSD is different */
150         if(!sh.universe)
151         {
152                 register char *universe;
153                 if(universe=astconf("_AST_UNIVERSE",0,0))
154                         bsd_univ = (strcmp(universe,"ucb")==0);
155                 sh.universe = 1;
156         }
157         if(!bsd_univ)
158                 return(b_print(0,argv,0));
159         options = sh_optecho;
160         raw = 1;
161         if(argv[1] && strcmp(argv[1],"-n")==0)
162                 echon = 1;
163         return(b_print(0,argv+echon,0));
164    }
165 #endif /* SHOPT_ECHOPRINT */
166
167 int    b_printf __PARAM__((int argc, char *argv[],__V_ *extra), (argc, argv, extra)) __OTORP__(int argc; char *argv[];__V_ *extra;){
168         NOT_USED(argc);
169         NOT_USED(extra);
170         options = sh_optprintf;
171         return(b_print(-1,argv,0));
172 }
173
174 /*
175  * argc==0 when called from echo
176  * argc==-1 when called from printf
177  */
178
179 int    b_print __PARAM__((int argc, char *argv[], __V_ *extra), (argc, argv, extra)) __OTORP__(int argc; char *argv[]; __V_ *extra;){
180         register Sfio_t *outfile;
181         register int n, fd = 1;
182         const char *msg = e_file+4;
183         char *format = 0;
184         int sflag = 0, nflag, rflag;
185         NOT_USED(extra);
186         if(argc>0)
187         {
188                 options = sh_optprint;
189                 nflag = rflag = 0;
190                 format = 0;
191         }
192         else if(argc==0)
193         {
194                 nflag = echon;
195                 rflag = raw;
196                 argv++;
197                 goto skip;
198         }
199         while((n = optget(argv,options))) switch(n)
200         {
201                 case 'n':
202                         nflag++;
203                         break;
204                 case 'p':
205                         fd = sh.coutpipe;
206                         msg = e_query;
207                         break;
208                 case 'f':
209                         format = opt_arg;
210                         break;
211                 case 's':
212                         /* print to history file */
213                         if(!sh_histinit())
214                                 error(ERROR_system(1),e_history);
215                         fd = sffileno(sh.hist_ptr->histfp);
216                         sh_onstate(SH_HISTORY);
217                         sflag++;
218                         break;
219                 case 'e':
220                         rflag = 0;
221                         break;
222                 case 'r':
223                         rflag = 1;
224                         break;
225                 case 'u':
226                         fd = (int)strtol(opt_arg,&opt_arg,10);
227                         if(*opt_arg)
228                                 fd = -1;
229                         else if(fd<0 || fd >= sh.lim.open_max)
230                                 fd = -1;
231                         else if(sh_inuse(fd) || (sh.hist_ptr && fd==sffileno(sh.hist_ptr->histfp)))
232                                 fd = -1;
233                         break;
234                 case ':':
235                         /* The followin is for backward compatibility */
236                         if(strcmp(opt_option,"-R")==0)
237                         {
238                                 rflag = 1;
239                                 if(error_info.errors==0)
240                                 {
241                                         argv += opt_index+1;
242                                         /* special case test for -Rn */
243                                         if(strchr(argv[-1],'n'))
244                                                 nflag++;
245                                         if(*argv && strcmp(*argv,"-n")==0)
246                                         {
247
248                                                 nflag++;
249                                                 argv++;
250                                         }
251                                         goto skip2;
252                                 }
253                         }
254                         else
255                                 error(2, opt_arg);
256                         break;
257                 case '?':
258                         error(ERROR_usage(2), opt_arg);
259                         break;
260         }
261         argv += opt_index;
262         if(error_info.errors || (argc<0 && !(format = *argv++)))
263                 error(ERROR_usage(2),optusage((char*)0));
264 skip:
265         if(format)
266                 format = genformat(format);
267         /* handle special case of '-' operand for print */
268         if(argc>0 && *argv && strcmp(*argv,"-")==0 && strcmp(argv[-1],"--"))
269                 argv++;
270 skip2:
271         if(fd < 0)
272         {
273                 errno = EBADF;
274                 n = 0;
275         }
276         else if(!(n=sh.fdstatus[fd]))
277                 n = sh_iocheckfd(fd);
278 #ifndef __osf__
279         if(!(n&IOWRITE))
280         {
281                 /* don't print error message for stdout for compatibility */
282                 if(fd==1)
283                         return(1);
284                 error(ERROR_system(1),msg);
285         }
286 #endif
287         if(!(outfile=sh.sftable[fd]))
288         {
289                 Sfdisc_t *dp;
290                 sh_onstate(SH_NOTRACK);
291                 n = SF_WRITE|((n&IOREAD)?SF_READ:0);
292                 sh.sftable[fd] = outfile = sfnew(NIL(Sfio_t*),sh.outbuff,IOBSIZE,fd,n);
293                 sh_offstate(SH_NOTRACK);
294                 sfpool(outfile,sh.outpool,SF_WRITE);
295                 if(dp = new_of(Sfdisc_t,0))
296                 {
297                         dp->exceptf = outexceptf;
298                         dp->seekf = 0;
299                         dp->writef = 0;
300                         dp->readf = 0;
301                         sfdisc(outfile,dp);
302                 }
303         }
304         cescape = 0;
305         /* turn off share to guarantee atomic writes for printf */
306         n = sfset(outfile,SF_SHARE|SF_PUBLIC,0);
307         if(format)
308         {
309                 /* printf style print */
310                 Sfio_t *pool;
311                 sh_offstate(SH_STOPOK);
312                 pool=sfpool(sfstderr,NIL(Sfio_t*),SF_WRITE);
313                 nextarg = argv;
314                 do
315                 {
316                         if(sh.trapnote&SH_SIGSET)
317                                 break;
318                         sfprintf(outfile,format,extend,getarg);
319                 }
320                 while(*nextarg && nextarg!=argv);
321                 sfpool(sfstderr,pool,SF_WRITE);
322         }
323         else
324         {
325                 /* echo style print */
326                 if(sh_echolist(outfile,rflag,argv) && !nflag)
327                         sfputc(outfile,'\n');
328         }
329         if(sflag)
330         {
331                 hist_flush(sh.hist_ptr);
332                 sh_offstate(SH_HISTORY);
333         }
334         else if(n&SF_SHARE)
335         {
336                 sfset(outfile,SF_SHARE|SF_PUBLIC,1);
337                 sfsync(outfile);
338         }
339         return(0);
340 }
341
342 /*
343  * echo the argument list onto <outfile>
344  * if <raw> is non-zero then \ is not a special character.
345  * returns 0 for \c otherwise 1.
346  */
347
348 int sh_echolist __PARAM__((Sfio_t *outfile, int raw, char *argv[]), (outfile, raw, argv)) __OTORP__(Sfio_t *outfile; int raw; char *argv[];){
349         register char   *cp;
350         register int    n;
351         while(!cescape && (cp= *argv++))
352         {
353                 if(!raw  && (n=fmtvecho(cp))>=0)
354                 {
355                         if(n)
356                                 sfwrite(outfile,stakptr(staktell()),n);
357                 }
358                 else
359                         sfputr(outfile,cp,-1);
360                 if(*argv)
361                         sfputc(outfile,' ');
362                 sh_sigcheck();
363         }
364         return(!cescape);
365 }
366
367
368 static char *genformat __PARAM__((char *format), (format)) __OTORP__(char *format;){
369         register char *fp;
370         stakseek(0);
371         stakputs(preformat);
372         stakputs(format);
373         fp = (char*)stakfreeze(1);
374         stresc(fp+sizeof(preformat)-1);
375         return(fp);
376 }
377
378 static int getarg __PARAM__((int format,union types_t *value), (format, value)) __OTORP__(int format;union types_t *value;){
379         register char *argp = *nextarg;
380         char *lastchar = "";
381         register int neg = 0;
382         if(!argp)
383         {
384                 switch(format)
385                 {
386                         case 'c':
387                                 value->c = 0;
388                                 break;
389                         case 's':
390                         case 'q':
391                         case 'P':
392                         case 'R':
393                         case 'b':
394                                 value->s = "";
395                                 break;
396                         case 'f':
397                                 value->f = 0.;
398                                 break;
399                         case 'F':
400                                 value->d = 0.;
401                                 break;
402                         case 'n':
403                         {
404                                 static int intvar;
405                                 value->ip = &intvar;
406                                 break;
407                         }
408                         default:
409                                 value->l = 0;
410                 }
411                 return(0);
412         }
413         switch(format)
414         {
415                 case 'p':
416                         value->p = (char**)strtol(argp,&lastchar,10);
417                         break;
418                 case 'n':
419                 {
420                         Namval_t *np;
421                         np = nv_open(argp,sh.var_tree,NV_VARNAME|NV_NOASSIGN|NV_ARRAY);
422                         nv_unset(np);
423                         nv_onattr(np,NV_INTEGER);
424                         np->nvalue.lp = new_of(long,0);
425                         nv_setsize(np,10);
426                         if(sizeof(int)==sizeof(long))
427                                 value->ip = (int*)np->nvalue.lp;
428                         else
429                         {
430                                 struct temp { int hi; int low; } *sp = (struct temp*)(np->nvalue.lp);
431                                 sp->hi = 0;
432                                 sp->low = 1;
433                                 if(*np->nvalue.lp==1)
434                                         value->ip = &sp->low;
435                                 else
436                                         value->ip = &sp->hi;
437                         }
438                         nv_close(np);
439                         break;
440                 }
441                 case 'q':
442                 case 'b':
443                 case 's':
444                 case 'P':
445                 case 'R':
446                         value->s = argp;
447                         break;
448                 case 'c':
449                         value->c = *argp;
450                         break;
451                 case 'u':
452                 case 'U':
453                 case 'd':
454                 case 'D':
455                         switch(*argp)
456                         {
457                                 case '\'': case '"':
458                                         value->l = argp[1];
459                                         break;
460                                 default:
461                                         value->l = (long)sh_strnum(argp,&lastchar);
462                                         if(lastchar == *nextarg)
463                                         {
464                                                 value->l = *argp;
465                                                 lastchar = "";
466                                         }
467                         }
468                         if(neg)
469                                 value->l = -value->l;
470                         if(sizeof(int)!=sizeof(long) && format=='d')
471                                 value->i = (int)value->l;
472                         break;
473                 case 'f':
474                 case 'F':
475                         value->d = sh_strnum(*nextarg,&lastchar);
476                         if(sizeof(float)!=sizeof(double) && format=='f')
477                                 value->f = (float)value->d;
478                         break;
479                 default:
480                         value->l = 0;
481                         error(ERROR_exit(1),e_formspec,format);
482         }
483         if(*lastchar)
484                 error(ERROR_exit(1),e_argtype,format);
485         nextarg++;
486         return(0);
487 }
488
489 /*
490  * This routine adds new % escape sequences to printf
491  */
492 static int extend __PARAM__((char *invalue,int format,int precis,char **outval), (invalue, format, precis, outval)) __OTORP__(char *invalue;int format;int precis;char **outval;){
493         register int n;
494         NOT_USED(precis);
495         switch(format)
496         {
497                 case 'b':
498                         if((n=fmtvecho(invalue))>=0)
499                         {
500                                 *outval = stakptr(staktell());
501                                 return(n);
502                         }
503                         *outval = invalue;
504                         return(strlen(*outval));
505                 case 'q':
506                         *outval = sh_fmtq(invalue);
507                         return(strlen(*outval));
508                 case 'P':
509                         *outval = fmtmatch(invalue);
510                         if(*outval==0)
511                                 error(ERROR_exit(1),e_badregexp,invalue);
512                         return(strlen(*outval));
513                 case 'R':
514                         *outval = fmtre(invalue);
515                         if(*outval==0)
516                                 error(ERROR_exit(1),e_badregexp,invalue);
517                         return(strlen(*outval));
518                 default:
519                         *outval = 0;
520                         return(-1);
521         }
522 }
523
524 /*
525  * construct System V echo string out of <cp>
526  * If there are not escape sequences, returns -1
527  * Otherwise, puts null terminated result on stack, but doesn't freeze it
528  * returns lenght of output.
529  */
530
531 static int fmtvecho __PARAM__((const char *string), (string)) __OTORP__(const char *string;){
532         static char *nullarg;
533         register const char *cp = string, *cpmax;
534         register int c;
535         register int offset = staktell();
536         int chlen;
537         if (MB_CUR_MAX > 1)
538         {
539            while (1)
540            {
541               if ((chlen = mblen(cp, MB_CUR_MAX)) > 1)
542               {
543                  /* Skip over multibyte characters */
544                  cp += chlen;
545               }
546               else
547               {
548                  c = *cp++;
549                  if ((c == '\0') || (c == '\\'))
550                     break;
551               }
552            }
553         }
554         else
555            while((c= *cp++) && (c!='\\'));
556         if(c==0)
557                 return(-1);
558         c = --cp - string;
559         if(c>0)
560                 stakwrite((__V_*)string,c);
561         /* for(; c= *cp; cp++) */
562         while(1)
563         {
564                 if ((MB_CUR_MAX > 1) && ((chlen = mblen(cp, MB_CUR_MAX)) > 1))
565                 {
566                         /* Skip over multibyte characters */
567                         int i;
568                         for (i = 0; i < chlen; i++)
569                         {
570                            c = *cp++;
571                            stakputc(c);
572                         }
573                 }
574                 else
575                 {
576                         if ((c = *cp) == '\0')
577                                 break;
578
579                         if( c=='\\') switch(*++cp)
580                         {
581                                 case 'E':
582                                         c = '\033';
583                                         break;
584                                 case 'a':
585                                         c = '\007';
586                                         break;
587                                 case 'b':
588                                         c = '\b';
589                                         break;
590                                 case 'c':
591                                         cescape++;
592                                         nextarg = &nullarg;
593                                         goto done;
594                                 case 'f':
595                                         c = '\f';
596                                         break;
597                                 case 'n':
598                                         c = '\n';
599                                         break;
600                                 case 'r':
601                                         c = '\r';
602                                         break;
603                                 case 'v':
604                                         c = '\013';
605                                         break;
606                                 case 't':
607                                         c = '\t';
608                                         break;
609                                 case '\\':
610                                         c = '\\';
611                                         break;
612                                 case '0':
613                                         c = 0;
614                                         cpmax = cp + 4;
615                                         while(++cp<cpmax && *cp>='0' && *cp<='7')
616                                         {
617                                                 c <<= 3;
618                                                 c |= (*cp-'0');
619                                         }
620                                 default:
621                                         cp--;
622                         }
623                         stakputc(c);
624                         cp++;
625                 }
626         }
627 done:
628         c = staktell()-offset;
629         stakseek(offset);
630         return(c);
631 }
632