xref: /openbsd-src/usr.bin/lam/lam.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /*	$OpenBSD: lam.c,v 1.16 2013/11/20 21:22:17 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>
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 	/* Process arguments, set numfiles to file argument count. */
75 	getargs(argc, argv);
76 	if (numfiles == 0)
77 		usage();
78 	/* Concatenate lines from each file, then print. */
79 	for (;;) {
80 		linep = line;
81 		/*
82 		 * For each file that has a line to print, numfile is
83 		 * incremented.  Thus if numfiles is 0, we are done.
84 		 */
85 		numfiles = 0;
86 		for (i = 0; i < INPUTSIZE - 1 && input[i].fp != NULL; i++)
87 			linep = gatherline(&input[i]);
88 		if (numfiles == 0)
89 			exit(0);
90 		fputs(line, stdout);
91 		/* Print terminating -s argument. */
92 		fputs(input[i].sepstring, stdout);
93 		if (!nofinalnl)
94 			putchar('\n');
95 	}
96 }
97 
98 void
99 getargs(int argc, char *argv[])
100 {
101 	struct openfile *ip = input;
102 	char *p;
103 	int ch, P, S, F, T;
104 	size_t siz;
105 
106 	P = S = F = T = 0;		/* capitalized options */
107 	while (optind < argc) {
108 		switch (ch = getopt(argc, argv, "F:f:P:p:S:s:T:t:")) {
109 		case 'F': case 'f':
110 			F = (ch == 'F');
111 			/* Validate format string argument. */
112 			for (p = optarg; *p != '\0'; p++)
113 				if (!isdigit((unsigned char)*p) &&
114 				    *p != '.' && *p != '-')
115 					errx(1, "%s: invalid width specified",
116 					     optarg);
117 			/* '%' + width + 's' + '\0' */
118 			siz = p - optarg + 3;
119 			if ((p = realloc(ip->format, siz)) == NULL)
120 				err(1, NULL);
121 			snprintf(p, siz, "%%%ss", optarg);
122 			ip->format = p;
123 			break;
124 		case 'P': case 'p':
125 			P = (ch == 'P');
126 			ip->pad = 1;
127 			break;
128 		case 'S': case 's':
129 			S = (ch == 'S');
130 			ip->sepstring = optarg;
131 			break;
132 		case 'T': case 't':
133 			T = (ch == 'T');
134 			if (strlen(optarg) != 1)
135 				usage();
136 			ip->eol = optarg[0];
137 			nofinalnl = 1;
138 			break;
139 		case -1:
140 			if (optind >= argc)
141 				break;		/* to support "--" */
142 			/* This is a file, not a flag. */
143 			++numfiles;
144 			if (numfiles >= INPUTSIZE)
145 				errx(1, "too many files");
146 			if (strcmp(argv[optind], "-") == 0)
147 				ip->fp = stdin;
148 			else if ((ip->fp = fopen(argv[optind], "r")) == NULL)
149 				err(1, "%s", argv[optind]);
150 			ip->pad = P;
151 			if (ip->sepstring == NULL)
152 				ip->sepstring = S ? (ip-1)->sepstring : "";
153 			if (ip->format == NULL)
154 				ip->format = (P || F) ? (ip-1)->format : "%s";
155 			if (ip->eol == '\0')
156 				ip->eol = T ? (ip-1)->eol : '\n';
157 			ip++;
158 			optind++;
159 			break;
160 		default:
161 			usage();
162 			/* NOTREACHED */
163 		}
164 	}
165 	ip->fp = NULL;
166 	if (ip->sepstring == NULL)
167 		ip->sepstring = "";
168 }
169 
170 char *
171 pad(struct openfile *ip)
172 {
173 	size_t n;
174 	char *lp = linep;
175 
176 	n = strlcpy(lp, ip->sepstring,  line + sizeof(line) - lp);
177 	lp += (n < line + sizeof(line) - lp) ? n : strlen(lp);
178 	if (ip->pad) {
179 		n = snprintf(lp, line + sizeof(line) - lp, ip->format, "");
180 		if (n > 0)
181 			lp += (n < line + sizeof(line) - lp) ? n : strlen(lp);
182 	}
183 	return (lp);
184 }
185 
186 /*
187  * Grab line from file, appending to linep.  Increments numfiles if file
188  * is still open.
189  */
190 char *
191 gatherline(struct openfile *ip)
192 {
193 	size_t n;
194 	char s[BUFSIZ];
195 	char *p;
196 	char *lp = linep;
197 	char *end = s + BUFSIZ - 1;
198 	int c;
199 
200 	if (ip->eof)
201 		return (pad(ip));
202 	for (p = s; (c = fgetc(ip->fp)) != EOF && p < end; p++)
203 		if ((*p = c) == ip->eol)
204 			break;
205 	*p = '\0';
206 	if (c == EOF) {
207 		ip->eof = 1;
208 		if (ip->fp == stdin)
209 			fclose(stdin);
210 		return (pad(ip));
211 	}
212 	/* Something will be printed. */
213 	numfiles++;
214 	n = strlcpy(lp, ip->sepstring, line + sizeof(line) - lp);
215 	lp += (n < line + sizeof(line) - lp) ? n : strlen(lp);
216 	n = snprintf(lp, line + sizeof(line) - lp, ip->format, s);
217 	if (n > 0)
218 		lp += (n < line + sizeof(line) - lp) ? n : strlen(lp);
219 	return (lp);
220 }
221 
222 void
223 usage(void)
224 {
225 	extern char *__progname;
226 
227 	fprintf(stderr,
228 	    "usage: %s [-f min.max] [-p min.max] [-s sepstring] [-t c] file ...\n",
229 	    __progname);
230 	exit(1);
231 }
232