1*cf5a6c84SAndroid Build Coastguard Worker /* paste.c - Merge corresponding lines
2*cf5a6c84SAndroid Build Coastguard Worker *
3*cf5a6c84SAndroid Build Coastguard Worker * Copyright 2012 Felix Janda <[email protected]>
4*cf5a6c84SAndroid Build Coastguard Worker *
5*cf5a6c84SAndroid Build Coastguard Worker * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/paste.html
6*cf5a6c84SAndroid Build Coastguard Worker *
7*cf5a6c84SAndroid Build Coastguard Worker * Deviations from posix: the FILE argument isn't mandatory, none == '-'
8*cf5a6c84SAndroid Build Coastguard Worker
9*cf5a6c84SAndroid Build Coastguard Worker USE_PASTE(NEWTOY(paste, "d:s", TOYFLAG_USR|TOYFLAG_BIN))
10*cf5a6c84SAndroid Build Coastguard Worker
11*cf5a6c84SAndroid Build Coastguard Worker config PASTE
12*cf5a6c84SAndroid Build Coastguard Worker bool "paste"
13*cf5a6c84SAndroid Build Coastguard Worker default y
14*cf5a6c84SAndroid Build Coastguard Worker help
15*cf5a6c84SAndroid Build Coastguard Worker usage: paste [-s] [-d DELIMITERS] [FILE...]
16*cf5a6c84SAndroid Build Coastguard Worker
17*cf5a6c84SAndroid Build Coastguard Worker Merge corresponding lines from each input file.
18*cf5a6c84SAndroid Build Coastguard Worker
19*cf5a6c84SAndroid Build Coastguard Worker -d List of delimiter characters to separate fields with (default is \t)
20*cf5a6c84SAndroid Build Coastguard Worker -s Sequential mode: turn each input file into one line of output
21*cf5a6c84SAndroid Build Coastguard Worker */
22*cf5a6c84SAndroid Build Coastguard Worker
23*cf5a6c84SAndroid Build Coastguard Worker #define FOR_paste
24*cf5a6c84SAndroid Build Coastguard Worker #include "toys.h"
25*cf5a6c84SAndroid Build Coastguard Worker
GLOBALS(char * d;int files;)26*cf5a6c84SAndroid Build Coastguard Worker GLOBALS(
27*cf5a6c84SAndroid Build Coastguard Worker char *d;
28*cf5a6c84SAndroid Build Coastguard Worker
29*cf5a6c84SAndroid Build Coastguard Worker int files;
30*cf5a6c84SAndroid Build Coastguard Worker )
31*cf5a6c84SAndroid Build Coastguard Worker
32*cf5a6c84SAndroid Build Coastguard Worker // \0 is weird, and -d "" is also weird.
33*cf5a6c84SAndroid Build Coastguard Worker
34*cf5a6c84SAndroid Build Coastguard Worker static void paste_files(void)
35*cf5a6c84SAndroid Build Coastguard Worker {
36*cf5a6c84SAndroid Build Coastguard Worker FILE **fps = (void *)toybuf;
37*cf5a6c84SAndroid Build Coastguard Worker char *dpos, *dstr, *buf, c;
38*cf5a6c84SAndroid Build Coastguard Worker int i, any, dcount, dlen, len, seq = FLAG(s);
39*cf5a6c84SAndroid Build Coastguard Worker
40*cf5a6c84SAndroid Build Coastguard Worker // Loop through lines until no input left
41*cf5a6c84SAndroid Build Coastguard Worker for (;;) {
42*cf5a6c84SAndroid Build Coastguard Worker
43*cf5a6c84SAndroid Build Coastguard Worker // Start of each line/file resets delimiter cycle
44*cf5a6c84SAndroid Build Coastguard Worker dpos = TT.d;
45*cf5a6c84SAndroid Build Coastguard Worker
46*cf5a6c84SAndroid Build Coastguard Worker for (i = any = dcount = dlen = 0; seq || i<TT.files; i++) {
47*cf5a6c84SAndroid Build Coastguard Worker size_t blen;
48*cf5a6c84SAndroid Build Coastguard Worker unsigned wc;
49*cf5a6c84SAndroid Build Coastguard Worker FILE *ff = seq ? *fps : fps[i];
50*cf5a6c84SAndroid Build Coastguard Worker
51*cf5a6c84SAndroid Build Coastguard Worker // Read and output line, preserving embedded NUL bytes.
52*cf5a6c84SAndroid Build Coastguard Worker
53*cf5a6c84SAndroid Build Coastguard Worker buf = 0;
54*cf5a6c84SAndroid Build Coastguard Worker len = 0;
55*cf5a6c84SAndroid Build Coastguard Worker if (!ff || 0>=(len = getline(&buf, &blen, ff))) {
56*cf5a6c84SAndroid Build Coastguard Worker if (ff && ff!=stdin) fclose(ff);
57*cf5a6c84SAndroid Build Coastguard Worker if (seq) return;
58*cf5a6c84SAndroid Build Coastguard Worker fps[i] = 0;
59*cf5a6c84SAndroid Build Coastguard Worker if (!any) continue;
60*cf5a6c84SAndroid Build Coastguard Worker }
61*cf5a6c84SAndroid Build Coastguard Worker dcount = any ? 1 : i;
62*cf5a6c84SAndroid Build Coastguard Worker any = 1;
63*cf5a6c84SAndroid Build Coastguard Worker
64*cf5a6c84SAndroid Build Coastguard Worker // Output delimiters as necessary: not at beginning/end of line,
65*cf5a6c84SAndroid Build Coastguard Worker // catch up if first few files had no input but a later one did.
66*cf5a6c84SAndroid Build Coastguard Worker // Entire line with no input means no output.
67*cf5a6c84SAndroid Build Coastguard Worker
68*cf5a6c84SAndroid Build Coastguard Worker while (dcount) {
69*cf5a6c84SAndroid Build Coastguard Worker
70*cf5a6c84SAndroid Build Coastguard Worker // Find next delimiter, which can be "", \n, or UTF8 w/combining chars
71*cf5a6c84SAndroid Build Coastguard Worker dstr = dpos;
72*cf5a6c84SAndroid Build Coastguard Worker dlen = 0;
73*cf5a6c84SAndroid Build Coastguard Worker dcount--;
74*cf5a6c84SAndroid Build Coastguard Worker
75*cf5a6c84SAndroid Build Coastguard Worker if (!*TT.d) {;}
76*cf5a6c84SAndroid Build Coastguard Worker else if (*dpos == '\\') {
77*cf5a6c84SAndroid Build Coastguard Worker if (*++dpos=='0') dpos++;
78*cf5a6c84SAndroid Build Coastguard Worker else {
79*cf5a6c84SAndroid Build Coastguard Worker dlen = 1;
80*cf5a6c84SAndroid Build Coastguard Worker if ((c = unescape(*dpos))) {
81*cf5a6c84SAndroid Build Coastguard Worker dstr = &c;
82*cf5a6c84SAndroid Build Coastguard Worker dpos++;
83*cf5a6c84SAndroid Build Coastguard Worker }
84*cf5a6c84SAndroid Build Coastguard Worker }
85*cf5a6c84SAndroid Build Coastguard Worker } else {
86*cf5a6c84SAndroid Build Coastguard Worker while (0<(dlen = utf8towc(&wc, dpos, 99))) {
87*cf5a6c84SAndroid Build Coastguard Worker dpos += dlen;
88*cf5a6c84SAndroid Build Coastguard Worker if (!(dlen = wcwidth(wc))) continue;
89*cf5a6c84SAndroid Build Coastguard Worker if (dlen<0) dpos = dstr+1;
90*cf5a6c84SAndroid Build Coastguard Worker break;
91*cf5a6c84SAndroid Build Coastguard Worker }
92*cf5a6c84SAndroid Build Coastguard Worker dlen = dpos-dstr;
93*cf5a6c84SAndroid Build Coastguard Worker }
94*cf5a6c84SAndroid Build Coastguard Worker if (!*dpos) dpos = TT.d;
95*cf5a6c84SAndroid Build Coastguard Worker
96*cf5a6c84SAndroid Build Coastguard Worker if (dlen) fwrite(dstr, dlen, 1, stdout);
97*cf5a6c84SAndroid Build Coastguard Worker }
98*cf5a6c84SAndroid Build Coastguard Worker
99*cf5a6c84SAndroid Build Coastguard Worker if (0<len) {
100*cf5a6c84SAndroid Build Coastguard Worker fwrite(buf, len-(buf[len-1]=='\n'), 1, stdout);
101*cf5a6c84SAndroid Build Coastguard Worker free(buf);
102*cf5a6c84SAndroid Build Coastguard Worker }
103*cf5a6c84SAndroid Build Coastguard Worker }
104*cf5a6c84SAndroid Build Coastguard Worker
105*cf5a6c84SAndroid Build Coastguard Worker // Only need a newline if we output something
106*cf5a6c84SAndroid Build Coastguard Worker if (any) xputc('\n');
107*cf5a6c84SAndroid Build Coastguard Worker else break;
108*cf5a6c84SAndroid Build Coastguard Worker }
109*cf5a6c84SAndroid Build Coastguard Worker }
110*cf5a6c84SAndroid Build Coastguard Worker
do_paste(int fd,char * name)111*cf5a6c84SAndroid Build Coastguard Worker static void do_paste(int fd, char *name)
112*cf5a6c84SAndroid Build Coastguard Worker {
113*cf5a6c84SAndroid Build Coastguard Worker FILE **fps = (void *)toybuf;
114*cf5a6c84SAndroid Build Coastguard Worker
115*cf5a6c84SAndroid Build Coastguard Worker if (!(fps[TT.files++] = (fd ? fdopen(fd, "r") : stdin))) perror_exit(0);
116*cf5a6c84SAndroid Build Coastguard Worker if (TT.files >= sizeof(toybuf)/sizeof(FILE *)) perror_exit("tilt");
117*cf5a6c84SAndroid Build Coastguard Worker if (FLAG(s)) {
118*cf5a6c84SAndroid Build Coastguard Worker paste_files();
119*cf5a6c84SAndroid Build Coastguard Worker xputc('\n');
120*cf5a6c84SAndroid Build Coastguard Worker TT.files = 0;
121*cf5a6c84SAndroid Build Coastguard Worker }
122*cf5a6c84SAndroid Build Coastguard Worker }
123*cf5a6c84SAndroid Build Coastguard Worker
paste_main(void)124*cf5a6c84SAndroid Build Coastguard Worker void paste_main(void)
125*cf5a6c84SAndroid Build Coastguard Worker {
126*cf5a6c84SAndroid Build Coastguard Worker if (!FLAG(d)) TT.d = "\t";
127*cf5a6c84SAndroid Build Coastguard Worker
128*cf5a6c84SAndroid Build Coastguard Worker loopfiles_rw(toys.optargs, O_RDONLY, 0, do_paste);
129*cf5a6c84SAndroid Build Coastguard Worker if (!FLAG(s)) paste_files();
130*cf5a6c84SAndroid Build Coastguard Worker }
131