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