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