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