1/*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 1989, 1993
5 *	The Regents of the University of California.  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/types.h>
36
37#include <err.h>
38#include <errno.h>
39#include <limits.h>
40#include <locale.h>
41#include <stdio.h>
42#include <stdlib.h>
43#include <string.h>
44#include <unistd.h>
45#include <wchar.h>
46
47static wchar_t *delim;
48static int delimcnt;
49
50static int parallel(char **);
51static int sequential(char **);
52static int tr(wchar_t *);
53static void usage(void) __dead2;
54
55static wchar_t tab[] = L"\t";
56
57int
58main(int argc, char *argv[])
59{
60	int ch, rval, seq;
61	wchar_t *warg;
62	const char *arg;
63	size_t len;
64
65	setlocale(LC_CTYPE, "");
66
67	seq = 0;
68	while ((ch = getopt(argc, argv, "d:s")) != -1)
69		switch(ch) {
70		case 'd':
71			arg = optarg;
72			len = mbsrtowcs(NULL, &arg, 0, NULL);
73			if (len == (size_t)-1)
74				err(1, "delimiters");
75			warg = malloc((len + 1) * sizeof(*warg));
76			if (warg == NULL)
77				err(1, NULL);
78			arg = optarg;
79			len = mbsrtowcs(warg, &arg, len + 1, NULL);
80			if (len == (size_t)-1)
81				err(1, "delimiters");
82			delimcnt = tr(delim = warg);
83			break;
84		case 's':
85			seq = 1;
86			break;
87		case '?':
88		default:
89			usage();
90		}
91	argc -= optind;
92	argv += optind;
93
94	if (*argv == NULL)
95		usage();
96	if (!delim) {
97		delimcnt = 1;
98		delim = tab;
99	}
100
101	if (seq)
102		rval = sequential(argv);
103	else
104		rval = parallel(argv);
105	exit(rval);
106}
107
108typedef struct _list {
109	struct _list *next;
110	FILE *fp;
111	int cnt;
112	char *name;
113} LIST;
114
115static int
116parallel(char **argv)
117{
118	LIST *lp;
119	int cnt;
120	wint_t ich;
121	wchar_t ch;
122	char *p;
123	LIST *head, *tmp;
124	int opencnt, output;
125
126	for (cnt = 0, head = tmp = NULL; (p = *argv); ++argv, ++cnt) {
127		if ((lp = malloc(sizeof(LIST))) == NULL)
128			err(1, NULL);
129		if (p[0] == '-' && !p[1])
130			lp->fp = stdin;
131		else if (!(lp->fp = fopen(p, "r")))
132			err(1, "%s", p);
133		lp->next = NULL;
134		lp->cnt = cnt;
135		lp->name = p;
136		if (!head)
137			head = tmp = lp;
138		else {
139			tmp->next = lp;
140			tmp = lp;
141		}
142	}
143
144	for (opencnt = cnt; opencnt;) {
145		for (output = 0, lp = head; lp; lp = lp->next) {
146			if (!lp->fp) {
147				if (output && lp->cnt &&
148				    (ch = delim[(lp->cnt - 1) % delimcnt]))
149					putwchar(ch);
150				continue;
151			}
152			if ((ich = getwc(lp->fp)) == WEOF) {
153				if (!--opencnt)
154					break;
155				lp->fp = NULL;
156				if (output && lp->cnt &&
157				    (ch = delim[(lp->cnt - 1) % delimcnt]))
158					putwchar(ch);
159				continue;
160			}
161			/*
162			 * make sure that we don't print any delimiters
163			 * unless there's a non-empty file.
164			 */
165			if (!output) {
166				output = 1;
167				for (cnt = 0; cnt < lp->cnt; ++cnt)
168					if ((ch = delim[cnt % delimcnt]))
169						putwchar(ch);
170			} else if ((ch = delim[(lp->cnt - 1) % delimcnt]))
171				putwchar(ch);
172			if (ich == '\n')
173				continue;
174			do {
175				putwchar(ich);
176			} while ((ich = getwc(lp->fp)) != WEOF && ich != '\n');
177		}
178		if (output)
179			putwchar('\n');
180	}
181
182	return (0);
183}
184
185static int
186sequential(char **argv)
187{
188	FILE *fp;
189	int cnt, failed, needdelim;
190	wint_t ch;
191	char *p;
192
193	failed = 0;
194	for (; (p = *argv); ++argv) {
195		if (p[0] == '-' && !p[1])
196			fp = stdin;
197		else if (!(fp = fopen(p, "r"))) {
198			warn("%s", p);
199			failed = 1;
200			continue;
201		}
202		cnt = needdelim = 0;
203		while ((ch = getwc(fp)) != WEOF) {
204			if (needdelim) {
205				needdelim = 0;
206				if (delim[cnt] != '\0')
207					putwchar(delim[cnt]);
208				if (++cnt == delimcnt)
209					cnt = 0;
210			}
211			if (ch != '\n')
212				putwchar(ch);
213			else
214				needdelim = 1;
215		}
216		if (needdelim)
217			putwchar('\n');
218		if (fp != stdin)
219			(void)fclose(fp);
220	}
221
222	return (failed != 0);
223}
224
225static int
226tr(wchar_t *arg)
227{
228	int cnt;
229	wchar_t ch, *p;
230
231	for (p = arg, cnt = 0; (ch = *p++); ++arg, ++cnt)
232		if (ch == '\\')
233			switch(ch = *p++) {
234			case 'n':
235				*arg = '\n';
236				break;
237			case 't':
238				*arg = '\t';
239				break;
240			case '0':
241				*arg = '\0';
242				break;
243			default:
244				*arg = ch;
245				break;
246		} else
247			*arg = ch;
248
249	if (!cnt)
250		errx(1, "no delimiters specified");
251	return(cnt);
252}
253
254static void
255usage(void)
256{
257	(void)fprintf(stderr, "usage: paste [-s] [-d delimiters] file ...\n");
258	exit(1);
259}
260