xref: /openbsd-src/usr.bin/lam/lam.c (revision 0d961ef63e5ace6d35842b14bbf23f50d84ae2af)
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