1 /* paste.c - Merge corresponding lines 2 * 3 * Copyright 2012 Felix Janda <felix.janda (at) 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 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 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 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