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