xref: /freebsd/usr.bin/paste/paste.c (revision 17ee9d00bc1ae1e598c38f25826f861e4bc6c3ce)
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 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 #ifndef lint
44 static char sccsid[] = "@(#)paste.c	8.1 (Berkeley) 6/6/93";
45 #endif /* not lint */
46 
47 #include <sys/types.h>
48 #include <errno.h>
49 #include <limits.h>
50 #include <stdio.h>
51 #include <string.h>
52 
53 char *delim;
54 int delimcnt;
55 
56 main(argc, argv)
57 	int argc;
58 	char **argv;
59 {
60 	extern char *optarg;
61 	extern int optind;
62 	int ch, seq;
63 
64 	seq = 0;
65 	while ((ch = getopt(argc, argv, "d:s")) != EOF)
66 		switch(ch) {
67 		case 'd':
68 			delimcnt = tr(delim = optarg);
69 			break;
70 		case 's':
71 			seq = 1;
72 			break;
73 		case '?':
74 		default:
75 			usage();
76 		}
77 	argc -= optind;
78 	argv += optind;
79 
80 	if (!delim) {
81 		delimcnt = 1;
82 		delim = "\t";
83 	}
84 
85 	if (seq)
86 		sequential(argv);
87 	else
88 		parallel(argv);
89 	exit(0);
90 }
91 
92 typedef struct _list {
93 	struct _list *next;
94 	FILE *fp;
95 	int cnt;
96 	char *name;
97 } LIST;
98 
99 parallel(argv)
100 	char **argv;
101 {
102 	register LIST *lp;
103 	register int cnt;
104 	register char ch, *p;
105 	LIST *head, *tmp;
106 	int opencnt, output;
107 	char buf[_POSIX2_LINE_MAX + 1], *malloc();
108 
109 	for (cnt = 0, head = NULL; p = *argv; ++argv, ++cnt) {
110 		if (!(lp = (LIST *)malloc((u_int)sizeof(LIST)))) {
111 			(void)fprintf(stderr, "paste: %s.\n", strerror(ENOMEM));
112 			exit(1);
113 		}
114 		if (p[0] == '-' && !p[1])
115 			lp->fp = stdin;
116 		else if (!(lp->fp = fopen(p, "r"))) {
117 			(void)fprintf(stderr, "paste: %s: %s.\n", p,
118 			    strerror(errno));
119 			exit(1);
120 		}
121 		lp->next = NULL;
122 		lp->cnt = cnt;
123 		lp->name = p;
124 		if (!head)
125 			head = tmp = lp;
126 		else {
127 			tmp->next = lp;
128 			tmp = lp;
129 		}
130 	}
131 
132 	for (opencnt = cnt; opencnt;) {
133 		for (output = 0, lp = head; lp; lp = lp->next) {
134 			if (!lp->fp) {
135 				if (output && lp->cnt &&
136 				    (ch = delim[(lp->cnt - 1) % delimcnt]))
137 					putchar(ch);
138 				continue;
139 			}
140 			if (!fgets(buf, sizeof(buf), lp->fp)) {
141 				if (!--opencnt)
142 					break;
143 				lp->fp = NULL;
144 				if (output && lp->cnt &&
145 				    (ch = delim[(lp->cnt - 1) % delimcnt]))
146 					putchar(ch);
147 				continue;
148 			}
149 			if (!(p = index(buf, '\n'))) {
150 				(void)fprintf(stderr,
151 				    "paste: %s: input line too long.\n",
152 				    lp->name);
153 				exit(1);
154 			}
155 			*p = '\0';
156 			/*
157 			 * make sure that we don't print any delimiters
158 			 * unless there's a non-empty file.
159 			 */
160 			if (!output) {
161 				output = 1;
162 				for (cnt = 0; cnt < lp->cnt; ++cnt)
163 					if (ch = delim[cnt % delimcnt])
164 						putchar(ch);
165 			} else if (ch = delim[(lp->cnt - 1) % delimcnt])
166 				putchar(ch);
167 			(void)printf("%s", buf);
168 		}
169 		if (output)
170 			putchar('\n');
171 	}
172 }
173 
174 sequential(argv)
175 	char **argv;
176 {
177 	register FILE *fp;
178 	register int cnt;
179 	register char ch, *p, *dp;
180 	char buf[_POSIX2_LINE_MAX + 1];
181 
182 	for (; p = *argv; ++argv) {
183 		if (p[0] == '-' && !p[1])
184 			fp = stdin;
185 		else if (!(fp = fopen(p, "r"))) {
186 			(void)fprintf(stderr, "paste: %s: %s.\n", p,
187 			    strerror(errno));
188 			continue;
189 		}
190 		if (fgets(buf, sizeof(buf), fp)) {
191 			for (cnt = 0, dp = delim;;) {
192 				if (!(p = index(buf, '\n'))) {
193 					(void)fprintf(stderr,
194 					    "paste: %s: input line too long.\n",
195 					    *argv);
196 					exit(1);
197 				}
198 				*p = '\0';
199 				(void)printf("%s", buf);
200 				if (!fgets(buf, sizeof(buf), fp))
201 					break;
202 				if (ch = *dp++)
203 					putchar(ch);
204 				if (++cnt == delimcnt) {
205 					dp = delim;
206 					cnt = 0;
207 				}
208 			}
209 			putchar('\n');
210 		}
211 		if (fp != stdin)
212 			(void)fclose(fp);
213 	}
214 }
215 
216 tr(arg)
217 	char *arg;
218 {
219 	register int cnt;
220 	register char ch, *p;
221 
222 	for (p = arg, cnt = 0; (ch = *p++); ++arg, ++cnt)
223 		if (ch == '\\')
224 			switch(ch = *p++) {
225 			case 'n':
226 				*arg = '\n';
227 				break;
228 			case 't':
229 				*arg = '\t';
230 				break;
231 			case '0':
232 				*arg = '\0';
233 				break;
234 			default:
235 				*arg = ch;
236 				break;
237 		} else
238 			*arg = ch;
239 
240 	if (!cnt) {
241 		(void)fprintf(stderr, "paste: no delimiters specified.\n");
242 		exit(1);
243 	}
244 	return(cnt);
245 }
246 
247 usage()
248 {
249 	(void)fprintf(stderr, "paste: [-s] [-d delimiters] file ...\n");
250 	exit(1);
251 }
252