xref: /freebsd/usr.bin/paste/paste.c (revision 4f29da19bd44f0e99f021510460a81bf754c21d2)
1 /*
2  * Copyright (c) 1989, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Adam S. Moskowitz of Menlo Consulting.
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. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *	This product includes software developed by the University of
19  *	California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36 
37 #ifndef lint
38 static const char copyright[] =
39 "@(#) Copyright (c) 1989, 1993\n\
40 	The Regents of the University of California.  All rights reserved.\n";
41 #endif /* not lint */
42 
43 #if 0
44 #ifndef lint
45 static char sccsid[] = "@(#)paste.c	8.1 (Berkeley) 6/6/93";
46 #endif /* not lint */
47 #endif
48 
49 #include <sys/cdefs.h>
50 __FBSDID("$FreeBSD$");
51 
52 #include <sys/types.h>
53 
54 #include <err.h>
55 #include <errno.h>
56 #include <limits.h>
57 #include <locale.h>
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <unistd.h>
62 #include <wchar.h>
63 
64 wchar_t *delim;
65 int delimcnt;
66 
67 int parallel(char **);
68 int sequential(char **);
69 int tr(wchar_t *);
70 static void usage(void);
71 
72 wchar_t tab[] = L"\t";
73 
74 int
75 main(int argc, char *argv[])
76 {
77 	int ch, rval, seq;
78 	wchar_t *warg;
79 	const char *arg;
80 	size_t len;
81 
82 	setlocale(LC_CTYPE, "");
83 
84 	seq = 0;
85 	while ((ch = getopt(argc, argv, "d:s")) != -1)
86 		switch(ch) {
87 		case 'd':
88 			arg = optarg;
89 			len = mbsrtowcs(NULL, &arg, 0, NULL);
90 			if (len == (size_t)-1)
91 				err(1, "delimiters");
92 			warg = malloc((len + 1) * sizeof(*warg));
93 			if (warg == NULL)
94 				err(1, NULL);
95 			arg = optarg;
96 			len = mbsrtowcs(warg, &arg, len + 1, NULL);
97 			if (len == (size_t)-1)
98 				err(1, "delimiters");
99 			delimcnt = tr(delim = warg);
100 			break;
101 		case 's':
102 			seq = 1;
103 			break;
104 		case '?':
105 		default:
106 			usage();
107 		}
108 	argc -= optind;
109 	argv += optind;
110 
111 	if (*argv == NULL)
112 		usage();
113 	if (!delim) {
114 		delimcnt = 1;
115 		delim = tab;
116 	}
117 
118 	if (seq)
119 		rval = sequential(argv);
120 	else
121 		rval = parallel(argv);
122 	exit(rval);
123 }
124 
125 typedef struct _list {
126 	struct _list *next;
127 	FILE *fp;
128 	int cnt;
129 	char *name;
130 } LIST;
131 
132 int
133 parallel(char **argv)
134 {
135 	LIST *lp;
136 	int cnt;
137 	wint_t ich;
138 	wchar_t ch;
139 	char *p;
140 	LIST *head, *tmp;
141 	int opencnt, output;
142 
143 	for (cnt = 0, head = NULL; (p = *argv); ++argv, ++cnt) {
144 		if ((lp = malloc(sizeof(LIST))) == NULL)
145 			err(1, NULL);
146 		if (p[0] == '-' && !p[1])
147 			lp->fp = stdin;
148 		else if (!(lp->fp = fopen(p, "r")))
149 			err(1, "%s", p);
150 		lp->next = NULL;
151 		lp->cnt = cnt;
152 		lp->name = p;
153 		if (!head)
154 			head = tmp = lp;
155 		else {
156 			tmp->next = lp;
157 			tmp = lp;
158 		}
159 	}
160 
161 	for (opencnt = cnt; opencnt;) {
162 		for (output = 0, lp = head; lp; lp = lp->next) {
163 			if (!lp->fp) {
164 				if (output && lp->cnt &&
165 				    (ch = delim[(lp->cnt - 1) % delimcnt]))
166 					putwchar(ch);
167 				continue;
168 			}
169 			if ((ich = getwc(lp->fp)) == WEOF) {
170 				if (!--opencnt)
171 					break;
172 				lp->fp = NULL;
173 				if (output && lp->cnt &&
174 				    (ch = delim[(lp->cnt - 1) % delimcnt]))
175 					putwchar(ch);
176 				continue;
177 			}
178 			/*
179 			 * make sure that we don't print any delimiters
180 			 * unless there's a non-empty file.
181 			 */
182 			if (!output) {
183 				output = 1;
184 				for (cnt = 0; cnt < lp->cnt; ++cnt)
185 					if ((ch = delim[cnt % delimcnt]))
186 						putwchar(ch);
187 			} else if ((ch = delim[(lp->cnt - 1) % delimcnt]))
188 				putwchar(ch);
189 			if (ich == '\n')
190 				continue;
191 			do {
192 				putwchar(ich);
193 			} while ((ich = getwc(lp->fp)) != WEOF && ich != '\n');
194 		}
195 		if (output)
196 			putwchar('\n');
197 	}
198 
199 	return (0);
200 }
201 
202 int
203 sequential(char **argv)
204 {
205 	FILE *fp;
206 	int cnt, failed, needdelim;
207 	wint_t ch;
208 	char *p;
209 
210 	failed = 0;
211 	for (; (p = *argv); ++argv) {
212 		if (p[0] == '-' && !p[1])
213 			fp = stdin;
214 		else if (!(fp = fopen(p, "r"))) {
215 			warn("%s", p);
216 			failed = 1;
217 			continue;
218 		}
219 		cnt = needdelim = 0;
220 		while ((ch = getwc(fp)) != WEOF) {
221 			if (needdelim) {
222 				needdelim = 0;
223 				if (delim[cnt] != '\0')
224 					putwchar(delim[cnt]);
225 				if (++cnt == delimcnt)
226 					cnt = 0;
227 			}
228 			if (ch != '\n')
229 				putwchar(ch);
230 			else
231 				needdelim = 1;
232 		}
233 		if (needdelim)
234 			putwchar('\n');
235 		if (fp != stdin)
236 			(void)fclose(fp);
237 	}
238 
239 	return (failed != 0);
240 }
241 
242 int
243 tr(wchar_t *arg)
244 {
245 	int cnt;
246 	wchar_t ch, *p;
247 
248 	for (p = arg, cnt = 0; (ch = *p++); ++arg, ++cnt)
249 		if (ch == '\\')
250 			switch(ch = *p++) {
251 			case 'n':
252 				*arg = '\n';
253 				break;
254 			case 't':
255 				*arg = '\t';
256 				break;
257 			case '0':
258 				*arg = '\0';
259 				break;
260 			default:
261 				*arg = ch;
262 				break;
263 		} else
264 			*arg = ch;
265 
266 	if (!cnt)
267 		errx(1, "no delimiters specified");
268 	return(cnt);
269 }
270 
271 static void
272 usage(void)
273 {
274 	(void)fprintf(stderr, "usage: paste [-s] [-d delimiters] file ...\n");
275 	exit(1);
276 }
277