paste.c - sbase - suckless unix tools
(HTM) git clone git://git.suckless.org/sbase
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) LICENSE
---
paste.c (2734B)
---
1 /* See LICENSE file for copyright and license details. */
2 #include <stdlib.h>
3 #include <string.h>
4
5 #include "utf.h"
6 #include "util.h"
7
8 struct fdescr {
9 FILE *fp;
10 const char *name;
11 };
12
13 static void
14 sequential(struct fdescr *dsc, int fdescrlen, Rune *delim, size_t delimlen)
15 {
16 Rune c, last;
17 size_t i, d;
18
19 for (i = 0; i < fdescrlen; i++) {
20 d = 0;
21 last = 0;
22
23 while (efgetrune(&c, dsc[i].fp, dsc[i].name)) {
24 if (last == '\n') {
25 if (delim[d] != '\0')
26 efputrune(&delim[d], stdout, "<stdout>");
27 d = (d + 1) % delimlen;
28 }
29
30 if (c != '\n')
31 efputrune(&c, stdout, "<stdout>");
32 last = c;
33 }
34
35 if (last == '\n')
36 efputrune(&last, stdout, "<stdout>");
37 }
38 }
39
40 static void
41 parallel(struct fdescr *dsc, int fdescrlen, Rune *delim, size_t delimlen)
42 {
43 Rune c, d;
44 size_t i, m;
45 ssize_t last;
46
47 nextline:
48 last = -1;
49
50 for (i = 0; i < fdescrlen; i++) {
51 d = delim[i % delimlen];
52 c = 0;
53
54 while (efgetrune(&c, dsc[i].fp, dsc[i].name)) {
55 for (m = last + 1; m < i; m++) {
56 if (delim[m % delimlen] != '\0')
57 efputrune(&delim[m % delimlen], stdout, "<stdout>");
58 }
59 last = i;
60 if (c == '\n') {
61 if (i != fdescrlen - 1)
62 c = d;
63 efputrune(&c, stdout, "<stdout>");
64 break;
65 }
66 efputrune(&c, stdout, "<stdout>");
67 }
68
69 if (c == 0 && last != -1) {
70 if (i == fdescrlen - 1)
71 putchar('\n');
72 else if (d != '\0')
73 efputrune(&d, stdout, "<stdout>");
74 last++;
75 }
76 }
77 if (last != -1)
78 goto nextline;
79 }
80
81 static void
82 usage(void)
83 {
84 eprintf("usage: %s [-s] [-d list] file ...\n", argv0);
85 }
86
87 int
88 main(int argc, char *argv[])
89 {
90 struct fdescr *dsc;
91 Rune *delim_rune = NULL;
92 size_t delim_runelen, i, delim_bytelen = 1;
93 int seq = 0, ret = 0;
94 char *delim = "\t";
95
96 ARGBEGIN {
97 case 's':
98 seq = 1;
99 break;
100 case 'd':
101 delim = EARGF(usage());
102 delim_bytelen = unescape(delim);
103 break;
104 default:
105 usage();
106 } ARGEND
107
108 if (!argc)
109 usage();
110
111 /* populate delimiters */
112 delim_rune = ereallocarray(NULL,
113 utfmemlen(delim, delim_bytelen) + 1, sizeof(*delim_rune));
114 if (!(delim_runelen = utfntorunestr(delim, delim_bytelen, delim_rune))) {
115 usage();
116 }
117
118 /* populate file list */
119 dsc = ereallocarray(NULL, argc, sizeof(*dsc));
120
121 for (i = 0; i < argc; i++) {
122 if (!strcmp(argv[i], "-")) {
123 argv[i] = "<stdin>";
124 dsc[i].fp = stdin;
125 } else if (!(dsc[i].fp = fopen(argv[i], "r"))) {
126 eprintf("fopen %s:", argv[i]);
127 }
128 dsc[i].name = argv[i];
129 }
130
131 if (seq) {
132 sequential(dsc, argc, delim_rune, delim_runelen);
133 } else {
134 parallel(dsc, argc, delim_rune, delim_runelen);
135 }
136
137 for (i = 0; i < argc; i++)
138 if (dsc[i].fp != stdin && fshut(dsc[i].fp, argv[i]))
139 ret |= fshut(dsc[i].fp, argv[i]);
140
141 ret |= fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>");
142
143 return ret;
144 }