1 /* $OpenBSD: lam.c,v 1.24 2021/12/03 15:15:22 deraadt Exp $ */
2 /* $NetBSD: lam.c,v 1.2 1994/11/14 20:27:42 jtc Exp $ */
3
4 /*-
5 * Copyright (c) 1993
6 * The Regents of the University of California. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33 /*
34 * lam - laminate files
35 * Author: John Kunze, UCB
36 */
37
38 #include <sys/types.h>
39
40 #include <ctype.h>
41 #include <err.h>
42 #include <locale.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <limits.h>
46 #include <string.h>
47 #include <unistd.h>
48
49 #define BIGBUFSIZ 5 * BUFSIZ
50
51 struct openfile { /* open file structure */
52 FILE *fp; /* file pointer */
53 int minwidth; /* pad this column to this width */
54 int maxwidth; /* truncate this column */
55 short eof; /* eof flag */
56 short pad; /* pad flag for missing columns */
57 char eol; /* end of line character */
58 char align; /* '0' for zero fill, '-' for left align */
59 char *sepstring; /* string to print before each line */
60 } *input;
61 int inputsize; /* number of openfile entries */
62
63 int output; /* line output produced */
64 int nofinalnl; /* normally append \n to each output line */
65 char line[BIGBUFSIZ];
66 char *linep;
67
68 int mbswidth_truncate(char *, int); /* utf8.c */
69
70 void usage(void);
71 char *gatherline(struct openfile *);
72 void getargs(int, char *[]);
73 char *pad(struct openfile *);
74
75 int
main(int argc,char * argv[])76 main(int argc, char *argv[])
77 {
78 int i;
79
80 setlocale(LC_CTYPE, "");
81
82 if (pledge("stdio rpath", NULL) == -1)
83 err(1, "pledge");
84
85 getargs(argc, argv);
86 if (inputsize == 0)
87 usage();
88
89 if (pledge("stdio", NULL) == -1)
90 err(1, "pledge");
91
92 /* Concatenate lines from each file, then print. */
93 for (;;) {
94 linep = line;
95 /*
96 * For each file that has a line to print, output is
97 * incremented. Thus if numfiles is 0, we are done.
98 */
99 output = 0;
100 for (i = 0; i < inputsize && input[i].fp != NULL; i++)
101 linep = gatherline(&input[i]);
102 if (output == 0)
103 exit(0);
104 fputs(line, stdout);
105 /* Print terminating -s argument. */
106 fputs(input[i].sepstring, stdout);
107 if (!nofinalnl)
108 putchar('\n');
109 }
110 }
111
112 void
getargs(int argc,char * argv[])113 getargs(int argc, char *argv[])
114 {
115 struct openfile *ip;
116 const char *errstr;
117 char *p, *q;
118 void *tmp;
119 int ch, P, S, F, T;
120
121 input = calloc(inputsize+1, sizeof *input);
122 if (input == NULL)
123 errx(1, "too many files");
124 ip = &input[inputsize];
125
126 P = S = F = T = 0; /* capitalized options */
127 while (optind < argc) {
128 switch (ch = getopt(argc, argv, "F:f:P:p:S:s:T:t:")) {
129 case 'P': case 'p':
130 P = (ch == 'P');
131 ip->pad = 1;
132 /* FALLTHROUGH */
133 case 'F': case 'f':
134 F = (ch == 'F');
135 /* Validate format string argument. */
136 p = optarg;
137 if (*p == '0' || *p == '-')
138 ip->align = *p++;
139 else
140 ip->align = ' ';
141 if ((q = strchr(p, '.')) != NULL)
142 *q++ = '\0';
143 if (*p != '\0') {
144 ip->minwidth = strtonum(p, 1, INT_MAX,
145 &errstr);
146 if (errstr != NULL)
147 errx(1, "minimum width is %s: %s",
148 errstr, p);
149 }
150 if (q != NULL) {
151 ip->maxwidth = strtonum(q, 1, INT_MAX,
152 &errstr);
153 if (errstr != NULL)
154 errx(1, "maximum width is %s: %s",
155 errstr, q);
156 } else
157 ip->maxwidth = INT_MAX;
158 break;
159 case 'S': case 's':
160 S = (ch == 'S');
161 ip->sepstring = optarg;
162 break;
163 case 'T': case 't':
164 T = (ch == 'T');
165 if (strlen(optarg) != 1)
166 usage();
167 ip->eol = optarg[0];
168 nofinalnl = 1;
169 break;
170 case -1:
171 if (optind >= argc)
172 break; /* to support "--" */
173 /* This is a file, not a flag. */
174 if (strcmp(argv[optind], "-") == 0)
175 ip->fp = stdin;
176 else if ((ip->fp = fopen(argv[optind], "r")) == NULL)
177 err(1, "%s", argv[optind]);
178 ip->pad = P;
179 if (ip->sepstring == NULL)
180 ip->sepstring = S ? (ip-1)->sepstring : "";
181 if (ip->eol == '\0')
182 ip->eol = T ? (ip-1)->eol : '\n';
183 if (ip->align == '\0') {
184 if (F || P) {
185 ip->align = (ip-1)->align;
186 ip->minwidth = (ip-1)->minwidth;
187 ip->maxwidth = (ip-1)->maxwidth;
188 } else
189 ip->maxwidth = INT_MAX;
190 }
191
192 ++inputsize;
193
194 /* Prepare for next file argument */
195 tmp = recallocarray(input, inputsize,
196 inputsize+1, sizeof *input);
197 if (tmp == NULL)
198 errx(1, "too many files");
199 input = tmp;
200 ip = &input[inputsize];
201 optind++;
202 break;
203 default:
204 usage();
205 /* NOTREACHED */
206 }
207 }
208 ip->fp = NULL;
209 if (ip->sepstring == NULL)
210 ip->sepstring = "";
211 }
212
213 char *
pad(struct openfile * ip)214 pad(struct openfile *ip)
215 {
216 size_t n;
217 char *lp = linep;
218 int i = 0;
219
220 n = strlcpy(lp, ip->sepstring, line + sizeof(line) - lp);
221 lp += (n < line + sizeof(line) - lp) ? n : strlen(lp);
222 if (ip->pad)
223 while (i++ < ip->minwidth && lp + 1 < line + sizeof(line))
224 *lp++ = ' ';
225 *lp = '\0';
226 return (lp);
227 }
228
229 /*
230 * Grab line from file, appending to linep. Increments printed if file
231 * is still open.
232 */
233 char *
gatherline(struct openfile * ip)234 gatherline(struct openfile *ip)
235 {
236 size_t n;
237 char s[BUFSIZ];
238 char *p;
239 char *lp = linep;
240 char *end = s + BUFSIZ - 1;
241 int c, width;
242
243 if (ip->eof)
244 return (pad(ip));
245 for (p = s; (c = fgetc(ip->fp)) != EOF && p < end; p++)
246 if ((*p = c) == ip->eol)
247 break;
248 *p = '\0';
249 if (c == EOF) {
250 ip->eof = 1;
251 if (ip->fp == stdin)
252 fclose(stdin);
253 return (pad(ip));
254 }
255 /* Something will be printed. */
256 output++;
257 n = strlcpy(lp, ip->sepstring, line + sizeof(line) - lp);
258 lp += (n < line + sizeof(line) - lp) ? n : strlen(lp);
259 width = mbswidth_truncate(s, ip->maxwidth);
260 if (ip->align != '-')
261 while (width++ < ip->minwidth && lp + 1 < line + sizeof(line))
262 *lp++ = ip->align;
263 n = strlcpy(lp, s, line + sizeof(line) - lp);
264 lp += (n < line + sizeof(line) - lp) ? n : strlen(lp);
265 if (ip->align == '-')
266 while (width++ < ip->minwidth && lp + 1 < line + sizeof(line))
267 *lp++ = ' ';
268 *lp = '\0';
269 return (lp);
270 }
271
272 void
usage(void)273 usage(void)
274 {
275 extern char *__progname;
276
277 fprintf(stderr,
278 "usage: %s [-F|f min.max] [-P|p min.max] [-S|s sepstring] [-T|t c] file ...\n",
279 __progname);
280 exit(1);
281 }
282