xref: /aosp_15_r20/external/toybox/toys/posix/paste.c (revision cf5a6c84e2b8763fc1a7db14496fd4742913b199)
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