1 /*        $NetBSD: lam.c,v 1.8 2011/09/04 20:28:09 joerg Exp $        */
2 
3 /*-
4  * Copyright (c) 1993
5  *        The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #ifndef lint
34 __COPYRIGHT("@(#) Copyright (c) 1993\
35  The Regents of the University of California.  All rights reserved.");
36 #endif /* not lint */
37 
38 #ifndef lint
39 #if 0
40 static char sccsid[] = "@(#)lam.c       8.1 (Berkeley) 6/6/93";
41 #endif
42 __RCSID("$NetBSD: lam.c,v 1.8 2011/09/04 20:28:09 joerg Exp $");
43 #endif /* not lint */
44 
45 /*
46  *        lam - laminate files
47  *        Author:  John Kunze, UCB
48  */
49 
50 #include <ctype.h>
51 #include <err.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 
56 #define   MAXOFILES 20
57 #define   BIGBUFSIZ 5 * BUFSIZ
58 
59 struct    openfile {                    /* open file structure */
60           FILE      *fp;                /* file pointer */
61           short     eof;                /* eof flag */
62           short     pad;                /* pad flag for missing columns */
63           char      eol;                /* end of line character */
64           const char *sepstring;        /* string to print before each line */
65           const char *format; /* printf(3) style string spec. */
66 }         input[MAXOFILES];
67 
68 static int          morefiles;                    /* set by getargs(), changed by gatherline() */
69 static int          nofinalnl;                    /* normally append \n to each output line */
70 static char         line[BIGBUFSIZ];
71 static char         *linep;
72 
73 __dead static void   error(const char *, const char *);
74 static char         *gatherline(struct openfile *);
75 static void          getargs(char *[]);
76 static char         *pad(struct openfile *);
77 
78 int
main(int argc,char * argv[])79 main(int argc, char *argv[])
80 {
81           struct    openfile *ip;
82 
83           getargs(argv);
84           if (!morefiles)
85                     error("lam - laminate files", "");
86           for (;;) {
87                     linep = line;
88                     for (ip = input; ip->fp != NULL; ip++)
89                               linep = gatherline(ip);
90                     if (!morefiles)
91                               exit(0);
92                     fputs(line, stdout);
93                     fputs(ip->sepstring, stdout);
94                     if (!nofinalnl)
95                               putchar('\n');
96           }
97 }
98 
99 static void
getargs(char * av[])100 getargs(char *av[])
101 {
102           struct openfile *ip = input;
103           char *p, *c;
104           static char fmtbuf[BUFSIZ];
105           char *fmtp = fmtbuf;
106           int P, S, F, T;
107 
108           P = S = F = T = 0;            /* capitalized options */
109           while ((p = *++av) != NULL) {
110                     if (*p != '-' || !p[1]) {
111                               if (++morefiles >= MAXOFILES)
112                                         errx(1, "too many input files");
113                               if (*p == '-')
114                                         ip->fp = stdin;
115                               else if ((ip->fp = fopen(p, "r")) == NULL)
116                                         errx(1, "open %s", p);
117                               ip->pad = P;
118                               if (!ip->sepstring)
119                                         ip->sepstring = (S ? (ip-1)->sepstring : "");
120                               if (!ip->format)
121                                         ip->format = ((P || F) ? (ip-1)->format : "%s");
122                               if (!ip->eol)
123                                         ip->eol = (T ? (ip-1)->eol : '\n');
124                               ip++;
125                               continue;
126                     }
127                     c = ++p;
128                     switch (tolower((unsigned char) *c)) {
129                     case 's':
130                               if (*++p || (p = *++av))
131                                         ip->sepstring = p;
132                               else
133                                         error("Need string after -%s", c);
134                               S = (*c == 'S' ? 1 : 0);
135                               break;
136                     case 't':
137                               if (*++p || (p = *++av))
138                                         ip->eol = *p;
139                               else
140                                         error("Need character after -%s", c);
141                               T = (*c == 'T' ? 1 : 0);
142                               nofinalnl = 1;
143                               break;
144                     case 'p':
145                               ip->pad = 1;
146                               P = (*c == 'P' ? 1 : 0);
147                               /* FALLTHROUGH */
148                     case 'f':
149                               F = (*c == 'F' ? 1 : 0);
150                               if (*++p || (p = *++av)) {
151                                         fmtp += strlen(fmtp) + 1;
152                                         if (fmtp >= fmtbuf + sizeof(fmtbuf))
153                                                   errx(1, "no more format space");
154                                         /* restrict format string to only valid width formatters */
155                                         if (strspn(p, "-.0123456789") != strlen(p))
156                                                   errx(1, "invalid format string `%s'", p);
157                                         if (snprintf(fmtp, fmtbuf + sizeof(fmtbuf) - fmtp, "%%%ss", p)
158                                                   >= fmtbuf + sizeof(fmtbuf) - fmtp)
159                                                             errx(1, "no more format space");
160                                         sprintf(fmtp, "%%%ss", p);
161                                         ip->format = fmtp;
162                               }
163                               else
164                                         error("Need string after -%s", c);
165                               break;
166                     default:
167                               error("What do you mean by -%s?", c);
168                               break;
169                     }
170           }
171           ip->fp = NULL;
172           if (!ip->sepstring)
173                     ip->sepstring = "";
174 }
175 
176 static char *
pad(struct openfile * ip)177 pad(struct openfile *ip)
178 {
179           char *lp = linep;
180 
181           strlcpy(lp, ip->sepstring, line + sizeof(line) - lp);
182           lp += strlen(lp);
183           if (ip->pad) {
184                     snprintf(lp, line + sizeof(line) - lp, ip->format, "");
185                     lp += strlen(lp);
186           }
187           return (lp);
188 }
189 
190 static char *
gatherline(struct openfile * ip)191 gatherline(struct openfile *ip)
192 {
193           char s[BUFSIZ];
194           int c;
195           char *p;
196           char *lp = linep;
197           char *end = s + sizeof(s) - 1;
198 
199           if (ip->eof)
200                     return (pad(ip));
201           for (p = s; (c = fgetc(ip->fp)) != EOF && p < end; p++)
202                     if ((*p = c) == ip->eol)
203                               break;
204           *p = '\0';
205           if (c == EOF) {
206                     ip->eof = 1;
207                     if (ip->fp == stdin)
208                               fclose(stdin);
209                     morefiles--;
210                     return (pad(ip));
211           }
212           strlcpy(lp, ip->sepstring, line + sizeof(line) - lp);
213           lp += strlen(lp);
214           snprintf(lp, line + sizeof(line) - lp, ip->format, s);
215           lp += strlen(lp);
216           return (lp);
217 }
218 
219 static void
error(const char * msg,const char * s)220 error(const char *msg, const char *s)
221 {
222           warnx(msg, s);
223           fprintf(stderr,
224 "\nUsage:  lam [ -[fp] min.max ] [ -s sepstring ] [ -t c ] file ...\n");
225           if (strncmp("lam - ", msg, 6) == 0)
226                     fprintf(stderr, "Options:\n\t%s\t%s\t%s\t%s\t%s",
227                         "-f min.max     field widths for file fragments\n",
228                         "-p min.max     like -f, but pad missing fragments\n",
229                         "-s sepstring   fragment separator\n",
230 "-t c               input line terminator is c, no \\n after output lines\n",
231                         "Capitalized options affect more than one file.\n");
232           exit(1);
233 }
234