xref: /openbsd/usr.bin/paste/paste.c (revision 09467b48)
1 /*	$OpenBSD: paste.c,v 1.26 2018/08/04 19:19:37 schwarze Exp $	*/
2 
3 /*
4  * Copyright (c) 1989 The Regents of the University of California.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Adam S. Moskowitz of Menlo Consulting.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 #include <sys/queue.h>
36 #include <sys/types.h>
37 #include <err.h>
38 #include <errno.h>
39 #include <limits.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44 
45 char *delim;
46 int delimcnt;
47 
48 int	tr(char *);
49 __dead void usage(void);
50 void	parallel(char **);
51 void	sequential(char **);
52 
53 int
54 main(int argc, char *argv[])
55 {
56 	extern char *optarg;
57 	extern int optind;
58 	int ch, seq;
59 
60 	if (pledge("stdio rpath", NULL) == -1)
61 		err(1, "pledge");
62 
63 	seq = 0;
64 	while ((ch = getopt(argc, argv, "d:s")) != -1) {
65 		switch (ch) {
66 		case 'd':
67 			delimcnt = tr(delim = optarg);
68 			break;
69 		case 's':
70 			seq = 1;
71 			break;
72 		case '?':
73 		default:
74 			usage();
75 		}
76 	}
77 	argc -= optind;
78 	argv += optind;
79 
80 	if (argc == 0)
81 		usage();
82 
83 	if (delim == NULL) {
84 		delimcnt = 1;
85 		delim = "\t";
86 	}
87 
88 	if (seq)
89 		sequential(argv);
90 	else
91 		parallel(argv);
92 	return 0;
93 }
94 
95 struct list {
96 	SIMPLEQ_ENTRY(list) entries;
97 	FILE *fp;
98 	int cnt;
99 	char *name;
100 };
101 
102 void
103 parallel(char **argv)
104 {
105 	SIMPLEQ_HEAD(, list) head = SIMPLEQ_HEAD_INITIALIZER(head);
106 	struct list *lp;
107 	char *line, *p;
108 	size_t linesize;
109 	ssize_t len;
110 	int cnt;
111 	int opencnt, output;
112 	char ch;
113 
114 	for (cnt = 0; (p = *argv) != NULL; ++argv, ++cnt) {
115 		if ((lp = malloc(sizeof(*lp))) == NULL)
116 			err(1, NULL);
117 
118 		if (p[0] == '-' && p[1] == '\0')
119 			lp->fp = stdin;
120 		else if ((lp->fp = fopen(p, "r")) == NULL)
121 			err(1, "%s", p);
122 		lp->cnt = cnt;
123 		lp->name = p;
124 		SIMPLEQ_INSERT_TAIL(&head, lp, entries);
125 	}
126 
127 	line = NULL;
128 	linesize = 0;
129 
130 	for (opencnt = cnt; opencnt;) {
131 		output = 0;
132 		SIMPLEQ_FOREACH(lp, &head, entries) {
133 			if (lp->fp == NULL) {
134 				if (output && lp->cnt &&
135 				    (ch = delim[(lp->cnt - 1) % delimcnt]))
136 					putchar(ch);
137 				continue;
138 			}
139 			if ((len = getline(&line, &linesize, lp->fp)) == -1) {
140 				if (ferror(lp->fp))
141 					err(1, "%s", lp->fp == stdin ?
142 					    "getline" : lp->name);
143 				if (--opencnt == 0)
144 					break;
145 				if (lp->fp != stdin)
146 					fclose(lp->fp);
147 				lp->fp = NULL;
148 				if (output && lp->cnt &&
149 				    (ch = delim[(lp->cnt - 1) % delimcnt]))
150 					putchar(ch);
151 				continue;
152 			}
153 			if (line[len - 1] == '\n')
154 				line[len - 1] = '\0';
155 			/*
156 			 * make sure that we don't print any delimiters
157 			 * unless there's a non-empty file.
158 			 */
159 			if (!output) {
160 				output = 1;
161 				for (cnt = 0; cnt < lp->cnt; ++cnt)
162 					if ((ch = delim[cnt % delimcnt]))
163 						putchar(ch);
164 			} else if ((ch = delim[(lp->cnt - 1) % delimcnt]))
165 				putchar(ch);
166 			fputs(line, stdout);
167 		}
168 		if (output)
169 			putchar('\n');
170 	}
171 	free(line);
172 }
173 
174 void
175 sequential(char **argv)
176 {
177 	FILE *fp;
178 	char *line, *p;
179 	size_t linesize;
180 	ssize_t len;
181 	int cnt;
182 
183 	line = NULL;
184 	linesize = 0;
185 	for (; (p = *argv) != NULL; ++argv) {
186 		if (p[0] == '-' && p[1] == '\0')
187 			fp = stdin;
188 		else if ((fp = fopen(p, "r")) == NULL) {
189 			warn("%s", p);
190 			continue;
191 		}
192 		cnt = -1;
193 		while ((len = getline(&line, &linesize, fp)) != -1) {
194 			if (line[len - 1] == '\n')
195 				line[len - 1] = '\0';
196 			if (cnt >= 0)
197 				putchar(delim[cnt]);
198 			if (++cnt == delimcnt)
199 				cnt = 0;
200 			fputs(line, stdout);
201 		}
202 		if (ferror(fp))
203 			err(1, "%s", fp == stdin ? "getline" : p);
204 		if (cnt >= 0)
205 			putchar('\n');
206 		if (fp != stdin)
207 			fclose(fp);
208 	}
209 	free(line);
210 }
211 
212 int
213 tr(char *arg)
214 {
215 	int cnt;
216 	char ch, *p;
217 
218 	for (p = arg, cnt = 0; (ch = *p++) != '\0'; ++arg, ++cnt) {
219 		if (ch == '\\') {
220 			switch (ch = *p++) {
221 			case 'n':
222 				*arg = '\n';
223 				break;
224 			case 't':
225 				*arg = '\t';
226 				break;
227 			case '0':
228 				*arg = '\0';
229 				break;
230 			default:
231 				*arg = ch;
232 				break;
233 			}
234 		} else
235 			*arg = ch;
236 	}
237 
238 	if (cnt == 0)
239 		errx(1, "no delimiters specified");
240 	return cnt;
241 }
242 
243 __dead void
244 usage(void)
245 {
246 	extern char *__progname;
247 	fprintf(stderr, "usage: %s [-s] [-d list] file ...\n", __progname);
248 	exit(1);
249 }
250