xref: /openbsd-src/usr.bin/lam/lam.c (revision 0b7734b3d77bb9b21afec6f4621cae6c805dbd45)
1 /*	$OpenBSD: lam.c,v 1.19 2015/10/09 01:37:08 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/param.h>	/* NOFILE_MAX */
39 
40 #include <ctype.h>
41 #include <err.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46 
47 #define	BIGBUFSIZ	5 * BUFSIZ
48 
49 struct	openfile {		/* open file structure */
50 	FILE	*fp;		/* file pointer */
51 	short	eof;		/* eof flag */
52 	short	pad;		/* pad flag for missing columns */
53 	char	eol;		/* end of line character */
54 	char	*sepstring;	/* string to print before each line */
55 	char	*format;	/* printf(3) style string spec. */
56 }	input[NOFILE_MAX + 1];	/* last one is for the last -s arg. */
57 #define INPUTSIZE sizeof(input) / sizeof(*input)
58 
59 int	numfiles;		/* number of open files */
60 int	nofinalnl;		/* normally append \n to each output line */
61 char	line[BIGBUFSIZ];
62 char	*linep;
63 
64 void	 usage(void);
65 char	*gatherline(struct openfile *);
66 void	 getargs(int, char *[]);
67 char	*pad(struct openfile *);
68 
69 int
70 main(int argc, char *argv[])
71 {
72 	int i;
73 
74 	if (pledge("stdio rpath", NULL) == -1)
75 		err(1, "pledge");
76 
77 	/* Process arguments, set numfiles to file argument count. */
78 	getargs(argc, argv);
79 	if (numfiles == 0)
80 		usage();
81 	/* Concatenate lines from each file, then print. */
82 	for (;;) {
83 		linep = line;
84 		/*
85 		 * For each file that has a line to print, numfile is
86 		 * incremented.  Thus if numfiles is 0, we are done.
87 		 */
88 		numfiles = 0;
89 		for (i = 0; i < INPUTSIZE - 1 && input[i].fp != NULL; i++)
90 			linep = gatherline(&input[i]);
91 		if (numfiles == 0)
92 			exit(0);
93 		fputs(line, stdout);
94 		/* Print terminating -s argument. */
95 		fputs(input[i].sepstring, stdout);
96 		if (!nofinalnl)
97 			putchar('\n');
98 	}
99 }
100 
101 void
102 getargs(int argc, char *argv[])
103 {
104 	struct openfile *ip = input;
105 	char *p;
106 	int ch, P, S, F, T;
107 	size_t siz;
108 
109 	P = S = F = T = 0;		/* capitalized options */
110 	while (optind < argc) {
111 		switch (ch = getopt(argc, argv, "F:f:P:p:S:s:T:t:")) {
112 		case 'F': case 'f':
113 			F = (ch == 'F');
114 			/* Validate format string argument. */
115 			for (p = optarg; *p != '\0'; p++)
116 				if (!isdigit((unsigned char)*p) &&
117 				    *p != '.' && *p != '-')
118 					errx(1, "%s: invalid width specified",
119 					     optarg);
120 			/* '%' + width + 's' + '\0' */
121 			siz = p - optarg + 3;
122 			if ((p = realloc(ip->format, siz)) == NULL)
123 				err(1, NULL);
124 			snprintf(p, siz, "%%%ss", optarg);
125 			ip->format = p;
126 			break;
127 		case 'P': case 'p':
128 			P = (ch == 'P');
129 			ip->pad = 1;
130 			break;
131 		case 'S': case 's':
132 			S = (ch == 'S');
133 			ip->sepstring = optarg;
134 			break;
135 		case 'T': case 't':
136 			T = (ch == 'T');
137 			if (strlen(optarg) != 1)
138 				usage();
139 			ip->eol = optarg[0];
140 			nofinalnl = 1;
141 			break;
142 		case -1:
143 			if (optind >= argc)
144 				break;		/* to support "--" */
145 			/* This is a file, not a flag. */
146 			++numfiles;
147 			if (numfiles >= INPUTSIZE)
148 				errx(1, "too many files");
149 			if (strcmp(argv[optind], "-") == 0)
150 				ip->fp = stdin;
151 			else if ((ip->fp = fopen(argv[optind], "r")) == NULL)
152 				err(1, "%s", argv[optind]);
153 			ip->pad = P;
154 			if (ip->sepstring == NULL)
155 				ip->sepstring = S ? (ip-1)->sepstring : "";
156 			if (ip->format == NULL)
157 				ip->format = (P || F) ? (ip-1)->format : "%s";
158 			if (ip->eol == '\0')
159 				ip->eol = T ? (ip-1)->eol : '\n';
160 			ip++;
161 			optind++;
162 			break;
163 		default:
164 			usage();
165 			/* NOTREACHED */
166 		}
167 	}
168 	ip->fp = NULL;
169 	if (ip->sepstring == NULL)
170 		ip->sepstring = "";
171 }
172 
173 char *
174 pad(struct openfile *ip)
175 {
176 	size_t n;
177 	char *lp = linep;
178 
179 	n = strlcpy(lp, ip->sepstring,  line + sizeof(line) - lp);
180 	lp += (n < line + sizeof(line) - lp) ? n : strlen(lp);
181 	if (ip->pad) {
182 		n = snprintf(lp, line + sizeof(line) - lp, ip->format, "");
183 		if (n > 0)
184 			lp += (n < line + sizeof(line) - lp) ? n : strlen(lp);
185 	}
186 	return (lp);
187 }
188 
189 /*
190  * Grab line from file, appending to linep.  Increments numfiles if file
191  * is still open.
192  */
193 char *
194 gatherline(struct openfile *ip)
195 {
196 	size_t n;
197 	char s[BUFSIZ];
198 	char *p;
199 	char *lp = linep;
200 	char *end = s + BUFSIZ - 1;
201 	int c;
202 
203 	if (ip->eof)
204 		return (pad(ip));
205 	for (p = s; (c = fgetc(ip->fp)) != EOF && p < end; p++)
206 		if ((*p = c) == ip->eol)
207 			break;
208 	*p = '\0';
209 	if (c == EOF) {
210 		ip->eof = 1;
211 		if (ip->fp == stdin)
212 			fclose(stdin);
213 		return (pad(ip));
214 	}
215 	/* Something will be printed. */
216 	numfiles++;
217 	n = strlcpy(lp, ip->sepstring, line + sizeof(line) - lp);
218 	lp += (n < line + sizeof(line) - lp) ? n : strlen(lp);
219 	n = snprintf(lp, line + sizeof(line) - lp, ip->format, s);
220 	if (n > 0)
221 		lp += (n < line + sizeof(line) - lp) ? n : strlen(lp);
222 	return (lp);
223 }
224 
225 void
226 usage(void)
227 {
228 	extern char *__progname;
229 
230 	fprintf(stderr,
231 	    "usage: %s [-f min.max] [-p min.max] [-s sepstring] [-t c] file ...\n",
232 	    __progname);
233 	exit(1);
234 }
235