8fea61cfe79889eaf9485c33b1360e66d2579a87
[platform/upstream/bash.git] / examples / loadables / print.c
1 /*
2  * Copyright (c) 1989, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by the University of
16  *      California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 #include <sys/types.h>
35
36 #include <errno.h>
37 #include <limits.h>
38 #include <stdio.h>
39
40 #include "bashansi.h"
41 #include "shell.h"
42 #include "builtins.h"
43 #include "stdc.h"
44 #include "bashgetopt.h"
45
46 #if !defined (errno)
47 extern int errno;
48 #endif
49
50 #define PF(f, func) { \
51         if (fieldwidth) \
52                 if (precision) \
53                         (void)fprintf(ofp, f, fieldwidth, precision, func); \
54                 else \
55                         (void)fprintf(ofp, f, fieldwidth, func); \
56         else if (precision) \
57                 (void)fprintf(ofp, f, precision, func); \
58         else \
59                 (void)fprintf(ofp, f, func); \
60 }
61
62 static int       asciicode __P((void));
63 static void      escape __P((char *));
64 static int       getchr __P((void));
65 static double    getdouble __P((void));
66 static int       getint __P((int *));
67 static int       getlong __P((long *));
68 static char     *getstr __P((void));
69 static char     *mklong __P((char *, int));
70 static void      usage __P((void));
71
72 static char **gargv;
73
74 int print_builtin ();
75 static int printf_main ();
76 static int printargs ();
77
78 static FILE *ofp;
79
80 extern char *ansicstr ();
81 extern char *single_quote ();
82 extern char **make_builtin_argv ();
83
84 extern char *this_command_name;
85
86 extern int optind;
87
88 static char *print_doc[] = {
89   "Output the arguments.  The -f option means to use the argument as a",
90   "format string as would be supplied to printf(1).  The rest of the",
91   "options are as in ksh.",
92   (char *)NULL
93 };
94
95 struct builtin print_struct = {
96         "print",
97         print_builtin,
98         BUILTIN_ENABLED,
99         print_doc,
100         "print [-Rnprs] [-u unit] [-f format] [arguments]",
101         (char *)0
102 };
103
104 #ifndef ISOPTION
105 #define ISOPTION(s, c)  (s[0] == '-' && s[2] == '\0' && s[1] == c)
106 #endif
107
108 int
109 print_builtin (list)
110      WORD_LIST *list;
111 {
112   int c, r, nflag, raw, ofd, sflag;
113   char **v, *pfmt, *arg;
114   WORD_LIST *l;
115
116   nflag = raw = sflag = 0;
117   ofd = 1;
118   pfmt = 0;
119
120   reset_internal_getopt ();
121   while ((c = internal_getopt (list, "Rnprsu:f:")) != -1)
122     {
123       switch (c)
124         {
125         case 'R':
126           raw = 2;
127           loptend = lcurrent;
128           if (loptend && ISOPTION (loptend->word->word, 'n'))
129             {
130               loptend = loptend->next;
131               nflag = 1;
132             }
133           goto opt_end;
134         case 'r':
135           raw = 1;
136           break;
137         case 'n':
138           nflag = 1;
139           break;
140         case 's':
141           sflag = 1;
142           break;
143         case 'p':
144           break;        /* NOP */
145         case 'u':
146           if (all_digits (list_optarg))
147             ofd = atoi (list_optarg);
148           else
149             {
150               for (l = list; l->next && l->next != lcurrent; l = l->next);
151               lcurrent = loptend = l;
152               goto opt_end;
153             }
154           break;
155         case 'f':
156           pfmt = list_optarg;
157           break;
158         default:
159           builtin_usage ();
160           return (EX_USAGE);
161         }
162     }
163
164 opt_end:
165   list = loptend;
166
167   ofp = (ofd == 1) ? stdout : fdopen (dup (ofd), "w");
168
169   if (pfmt)
170     {
171       v = word_list_to_argv (list, 0, 2, &c);
172       v[0] = this_command_name;
173       v[1] = pfmt;
174       r = printf_main (c, v);
175       free (v);
176       return r;
177     }
178
179   if (raw)
180     {
181       for (l = list; l; l = l->next)
182         {
183           fprintf (ofp, "%s", l->word->word);
184           if (l->next)
185             fprintf (ofp, " ");
186         }
187       if (nflag == 0)
188         fprintf (ofp, "\n");
189       fflush (ofp);
190       return (0);       
191     }
192         
193   r = printargs (list, ofp);
194   if (r && nflag == 0)
195     fprintf (ofp, "\n");
196   if (ofd != 1)
197     fclose (ofp);
198   return 0;
199 }
200
201 static int printargs (list, ofp)
202      WORD_LIST *list;
203      FILE *ofp;
204 {
205   WORD_LIST *l;
206   char *ostr;
207   int sawc;
208
209   for (sawc = 0, l = list; l; l = l->next)
210     {
211       ostr = ansicstr (l->word->word, strlen (l->word->word), &sawc);
212       fprintf (ofp, "%s", ostr);
213       free (ostr);
214       if (sawc)
215         return (0);
216       if (l->next)
217         fprintf (ofp, " ");
218     }
219   return (1);
220 }
221
222 static int
223 printf_main(argc, argv)
224         int argc;
225         char *argv[];
226 {
227         static char *skip1, *skip2;
228         int ch, end, fieldwidth, precision;
229         char convch, nextch, *format, *fmt, *start;
230
231         while ((ch = getopt(argc, argv, "")) != EOF)
232                 switch (ch) {
233                 case '?':
234                 default:
235                         usage();
236                         return (1);
237                 }
238         argc -= optind;
239         argv += optind;
240
241         if (argc < 1) {
242                 usage();
243                 return (1);
244         }
245
246         /*
247          * Basic algorithm is to scan the format string for conversion
248          * specifications -- once one is found, find out if the field
249          * width or precision is a '*'; if it is, gather up value.  Note,
250          * format strings are reused as necessary to use up the provided
251          * arguments, arguments of zero/null string are provided to use
252          * up the format string.
253          */
254         skip1 = "#-+ 0";
255         skip2 = "*0123456789";
256
257         escape(fmt = format = *argv);           /* backslash interpretation */
258         gargv = ++argv;
259         for (;;) {
260                 end = 0;
261                 /* find next format specification */
262 next:           for (start = fmt;; ++fmt) {
263                         if (!*fmt) {
264                                 /* avoid infinite loop */
265                                 if (end == 1) {
266                                         warnx("missing format character",
267                                             NULL, NULL);
268                                         return (1);
269                                 }
270                                 end = 1;
271                                 if (fmt > start)
272                                         (void)printf("%s", start);
273                                 if (!*gargv)
274                                         return (0);
275                                 fmt = format;
276                                 goto next;
277                         }
278                         /* %% prints a % */
279                         if (*fmt == '%') {
280                                 if (*++fmt != '%')
281                                         break;
282                                 *fmt++ = '\0';
283                                 (void)printf("%s", start);
284                                 goto next;
285                         }
286                 }
287
288                 /* skip to field width */
289                 for (; strchr(skip1, *fmt); ++fmt);
290                 if (*fmt == '*') {
291                         if (getint(&fieldwidth))
292                                 return (1);
293                 } else
294                         fieldwidth = 0;
295
296                 /* skip to possible '.', get following precision */
297                 for (; strchr(skip2, *fmt); ++fmt);
298                 if (*fmt == '.')
299                         ++fmt;
300                 if (*fmt == '*') {
301                         if (getint(&precision))
302                                 return (1);
303                 } else
304                         precision = 0;
305
306                 /* skip to conversion char */
307                 for (; strchr(skip2, *fmt); ++fmt);
308                 if (!*fmt) {
309                         warnx("missing format character", NULL, NULL);
310                         return (1);
311                 }
312
313                 convch = *fmt;
314                 nextch = *++fmt;
315                 *fmt = '\0';
316                 switch(convch) {
317                 case 'c': {
318                         char p;
319
320                         p = getchr();
321                         PF(start, p);
322                         break;
323                 }
324                 case 's': {
325                         char *p;
326
327                         p = getstr();
328                         PF(start, p);
329                         break;
330                 }
331                 case 'b': {             /* expand escapes in argument */
332                         char *p;
333
334                         p = getstr();
335                         escape(p);
336                         PF(start, p);
337                         break;
338                 }
339                 case 'q': {             /* print with shell single quoting */
340                         char *p, *p2;
341
342                         p = getstr();
343                         p2 = single_quote(p);
344                         PF(start, p2);
345                         free(p2);
346                         break;
347                 }
348                 case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': {
349                         long p;
350                         char *f;
351                         
352                         if ((f = mklong(start, convch)) == NULL)
353                                 return (1);
354                         if (getlong(&p))
355                                 return (1);
356                         PF(f, p);
357                         break;
358                 }
359                 case 'e': case 'E': case 'f': case 'g': case 'G': {
360                         double p;
361
362                         p = getdouble();
363                         PF(start, p);
364                         break;
365                 }
366                 default:
367                         warnx("illegal format character", NULL, NULL);
368                         return (1);
369                 }
370                 *fmt = nextch;
371         }
372         /* NOTREACHED */
373 }
374
375 static char *
376 mklong(str, ch)
377         char *str;
378         int ch;
379 {
380         static char copy[64];
381         int len;
382
383         len = strlen(str) + 2;
384         memmove(copy, str, len - 3);
385         copy[len - 3] = 'l';
386         copy[len - 2] = ch;
387         copy[len - 1] = '\0';
388         return (copy);
389 }
390
391 static void
392 escape(fmt)
393         register char *fmt;
394 {
395         register char *store;
396         register int value, c;
397
398         for (store = fmt; c = *fmt; ++fmt, ++store) {
399                 if (c != '\\') {
400                         *store = c;
401                         continue;
402                 }
403                 switch (*++fmt) {
404                 case '\0':              /* EOS, user error */
405                         *store = '\\';
406                         *++store = '\0';
407                         return;
408                 case '\\':              /* backslash */
409                 case '\'':              /* single quote */
410                         *store = *fmt;
411                         break;
412                 case 'a':               /* bell/alert */
413                         *store = '\7';
414                         break;
415                 case 'b':               /* backspace */
416                         *store = '\b';
417                         break;
418                 case 'c':
419                         return;
420                 case 'e':
421                 case 'E':
422                         *store = '\033';
423                         break;
424                 case 'f':               /* form-feed */
425                         *store = '\f';
426                         break;
427                 case 'n':               /* newline */
428                         *store = '\n';
429                         break;
430                 case 'r':               /* carriage-return */
431                         *store = '\r';
432                         break;
433                 case 't':               /* horizontal tab */
434                         *store = '\t';
435                         break;
436                 case 'v':               /* vertical tab */
437                         *store = '\13';
438                         break;
439                                         /* octal constant */
440                 case '0': case '1': case '2': case '3':
441                 case '4': case '5': case '6': case '7':
442                         for (c = 3, value = 0;
443                             c-- && *fmt >= '0' && *fmt <= '7'; ++fmt) {
444                                 value <<= 3;
445                                 value += *fmt - '0';
446                         }
447                         --fmt;
448                         *store = value;
449                         break;
450                 default:
451                         *store = *fmt;
452                         break;
453                 }
454         }
455         *store = '\0';
456 }
457
458 static int
459 getchr()
460 {
461         if (!*gargv)
462                 return ('\0');
463         return ((int)**gargv++);
464 }
465
466 static char *
467 getstr()
468 {
469         if (!*gargv)
470                 return ("");
471         return (*gargv++);
472 }
473
474 static char *Number = "+-.0123456789";
475 static int
476 getint(ip)
477         int *ip;
478 {
479         long val;
480
481         if (getlong(&val))
482                 return (1);
483         if (val > INT_MAX) {
484                 warnx("%s: %s", *gargv, strerror(ERANGE));
485                 return (1);
486         }
487         *ip = val;
488         return (0);
489 }
490
491 static int
492 getlong(lp)
493         long *lp;
494 {
495         long val;
496         char *ep;
497
498         if (!*gargv) {
499                 *lp = 0;
500                 return (0);
501         }
502         if (strchr(Number, **gargv)) {
503                 errno = 0;
504                 val = strtol(*gargv, &ep, 0);
505                 if (*ep != '\0') {
506                         warnx("%s: illegal number", *gargv, NULL);
507                         return (1);
508                 }
509                 if (errno == ERANGE)
510                         if (val == LONG_MAX) {
511                                 warnx("%s: %s", *gargv, strerror(ERANGE));
512                                 return (1);
513                         }
514                         if (val == LONG_MIN) {
515                                 warnx("%s: %s", *gargv, strerror(ERANGE));
516                                 return (1);
517                         }
518                         
519                 *lp = val;
520                 ++gargv;
521                 return (0);
522         }
523         *lp =  (long)asciicode();
524         return (0);
525 }
526
527 static double
528 getdouble()
529 {
530         if (!*gargv)
531                 return ((double)0);
532         if (strchr(Number, **gargv))
533                 return (atof(*gargv++));
534         return ((double)asciicode());
535 }
536
537 static int
538 asciicode()
539 {
540         register int ch;
541
542         ch = **gargv;
543         if (ch == '\'' || ch == '"')
544                 ch = (*gargv)[1];
545         ++gargv;
546         return (ch);
547 }
548
549 static void
550 usage()
551 {
552         (void)fprintf(stderr, "usage: print [-Rnprs] [-u unit] [-f format] [arg ...]\n");
553 }