1 /* $OpenBSD: paste.c,v 1.27 2022/12/04 23:50:49 cheloha 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
main(int argc,char * argv[])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 default:
73 usage();
74 }
75 }
76 argc -= optind;
77 argv += optind;
78
79 if (argc == 0)
80 usage();
81
82 if (delim == NULL) {
83 delimcnt = 1;
84 delim = "\t";
85 }
86
87 if (seq)
88 sequential(argv);
89 else
90 parallel(argv);
91 return 0;
92 }
93
94 struct list {
95 SIMPLEQ_ENTRY(list) entries;
96 FILE *fp;
97 int cnt;
98 char *name;
99 };
100
101 void
parallel(char ** argv)102 parallel(char **argv)
103 {
104 SIMPLEQ_HEAD(, list) head = SIMPLEQ_HEAD_INITIALIZER(head);
105 struct list *lp;
106 char *line, *p;
107 size_t linesize;
108 ssize_t len;
109 int cnt;
110 int opencnt, output;
111 char ch;
112
113 for (cnt = 0; (p = *argv) != NULL; ++argv, ++cnt) {
114 if ((lp = malloc(sizeof(*lp))) == NULL)
115 err(1, NULL);
116
117 if (p[0] == '-' && p[1] == '\0')
118 lp->fp = stdin;
119 else if ((lp->fp = fopen(p, "r")) == NULL)
120 err(1, "%s", p);
121 lp->cnt = cnt;
122 lp->name = p;
123 SIMPLEQ_INSERT_TAIL(&head, lp, entries);
124 }
125
126 line = NULL;
127 linesize = 0;
128
129 for (opencnt = cnt; opencnt;) {
130 output = 0;
131 SIMPLEQ_FOREACH(lp, &head, entries) {
132 if (lp->fp == NULL) {
133 if (output && lp->cnt &&
134 (ch = delim[(lp->cnt - 1) % delimcnt]))
135 putchar(ch);
136 continue;
137 }
138 if ((len = getline(&line, &linesize, lp->fp)) == -1) {
139 if (ferror(lp->fp))
140 err(1, "%s", lp->fp == stdin ?
141 "getline" : lp->name);
142 if (--opencnt == 0)
143 break;
144 if (lp->fp != stdin)
145 fclose(lp->fp);
146 lp->fp = NULL;
147 if (output && lp->cnt &&
148 (ch = delim[(lp->cnt - 1) % delimcnt]))
149 putchar(ch);
150 continue;
151 }
152 if (line[len - 1] == '\n')
153 line[len - 1] = '\0';
154 /*
155 * make sure that we don't print any delimiters
156 * unless there's a non-empty file.
157 */
158 if (!output) {
159 output = 1;
160 for (cnt = 0; cnt < lp->cnt; ++cnt)
161 if ((ch = delim[cnt % delimcnt]))
162 putchar(ch);
163 } else if ((ch = delim[(lp->cnt - 1) % delimcnt]))
164 putchar(ch);
165 fputs(line, stdout);
166 }
167 if (output)
168 putchar('\n');
169 }
170 free(line);
171 }
172
173 void
sequential(char ** argv)174 sequential(char **argv)
175 {
176 FILE *fp;
177 char *line, *p;
178 size_t linesize;
179 ssize_t len;
180 int cnt;
181
182 line = NULL;
183 linesize = 0;
184 for (; (p = *argv) != NULL; ++argv) {
185 if (p[0] == '-' && p[1] == '\0')
186 fp = stdin;
187 else if ((fp = fopen(p, "r")) == NULL) {
188 warn("%s", p);
189 continue;
190 }
191 cnt = -1;
192 while ((len = getline(&line, &linesize, fp)) != -1) {
193 if (line[len - 1] == '\n')
194 line[len - 1] = '\0';
195 if (cnt >= 0)
196 putchar(delim[cnt]);
197 if (++cnt == delimcnt)
198 cnt = 0;
199 fputs(line, stdout);
200 }
201 if (ferror(fp))
202 err(1, "%s", fp == stdin ? "getline" : p);
203 if (cnt >= 0)
204 putchar('\n');
205 if (fp != stdin)
206 fclose(fp);
207 }
208 free(line);
209 }
210
211 int
tr(char * arg)212 tr(char *arg)
213 {
214 int cnt;
215 char ch, *p;
216
217 for (p = arg, cnt = 0; (ch = *p++) != '\0'; ++arg, ++cnt) {
218 if (ch == '\\') {
219 switch (ch = *p++) {
220 case 'n':
221 *arg = '\n';
222 break;
223 case 't':
224 *arg = '\t';
225 break;
226 case '0':
227 *arg = '\0';
228 break;
229 default:
230 *arg = ch;
231 break;
232 }
233 } else
234 *arg = ch;
235 }
236
237 if (cnt == 0)
238 errx(1, "no delimiters specified");
239 return cnt;
240 }
241
242 __dead void
usage(void)243 usage(void)
244 {
245 extern char *__progname;
246 fprintf(stderr, "usage: %s [-s] [-d list] file ...\n", __progname);
247 exit(1);
248 }
249