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