10f66f451Sopenharmony_ci/* paste.c - Merge corresponding lines 20f66f451Sopenharmony_ci * 30f66f451Sopenharmony_ci * Copyright 2012 Felix Janda <felix.janda@posteo.de> 40f66f451Sopenharmony_ci * 50f66f451Sopenharmony_ci * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/paste.html 60f66f451Sopenharmony_ci * 70f66f451Sopenharmony_ci * Deviations from posix: the FILE argument isn't mandatory, none == '-' 80f66f451Sopenharmony_ci 90f66f451Sopenharmony_ciUSE_PASTE(NEWTOY(paste, "d:s", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LOCALE)) 100f66f451Sopenharmony_ci 110f66f451Sopenharmony_ciconfig PASTE 120f66f451Sopenharmony_ci bool "paste" 130f66f451Sopenharmony_ci default y 140f66f451Sopenharmony_ci help 150f66f451Sopenharmony_ci usage: paste [-s] [-d DELIMITERS] [FILE...] 160f66f451Sopenharmony_ci 170f66f451Sopenharmony_ci Merge corresponding lines from each input file. 180f66f451Sopenharmony_ci 190f66f451Sopenharmony_ci -d List of delimiter characters to separate fields with (default is \t) 200f66f451Sopenharmony_ci -s Sequential mode: turn each input file into one line of output 210f66f451Sopenharmony_ci*/ 220f66f451Sopenharmony_ci 230f66f451Sopenharmony_ci#define FOR_paste 240f66f451Sopenharmony_ci#include "toys.h" 250f66f451Sopenharmony_ci 260f66f451Sopenharmony_ciGLOBALS( 270f66f451Sopenharmony_ci char *d; 280f66f451Sopenharmony_ci 290f66f451Sopenharmony_ci int files; 300f66f451Sopenharmony_ci) 310f66f451Sopenharmony_ci 320f66f451Sopenharmony_ci// \0 is weird, and -d "" is also weird. 330f66f451Sopenharmony_ci 340f66f451Sopenharmony_cistatic void paste_files(void) 350f66f451Sopenharmony_ci{ 360f66f451Sopenharmony_ci FILE **fps = (void *)toybuf; 370f66f451Sopenharmony_ci char *dpos, *dstr, *buf, c; 380f66f451Sopenharmony_ci int i, any, dcount, dlen, len, seq = toys.optflags&FLAG_s; 390f66f451Sopenharmony_ci 400f66f451Sopenharmony_ci // Loop through lines until no input left 410f66f451Sopenharmony_ci for (;;) { 420f66f451Sopenharmony_ci 430f66f451Sopenharmony_ci // Start of each line/file resets delimiter cycle 440f66f451Sopenharmony_ci dpos = TT.d; 450f66f451Sopenharmony_ci 460f66f451Sopenharmony_ci for (i = any = dcount = dlen = 0; seq || i<TT.files; i++) { 470f66f451Sopenharmony_ci size_t blen; 480f66f451Sopenharmony_ci unsigned wc; 490f66f451Sopenharmony_ci FILE *ff = seq ? *fps : fps[i]; 500f66f451Sopenharmony_ci 510f66f451Sopenharmony_ci // Read and output line, preserving embedded NUL bytes. 520f66f451Sopenharmony_ci 530f66f451Sopenharmony_ci buf = 0; 540f66f451Sopenharmony_ci len = 0; 550f66f451Sopenharmony_ci if (!ff || 0>=(len = getline(&buf, &blen, ff))) { 560f66f451Sopenharmony_ci if (ff && ff!=stdin) fclose(ff); 570f66f451Sopenharmony_ci if (seq) return; 580f66f451Sopenharmony_ci fps[i] = 0; 590f66f451Sopenharmony_ci if (!any) continue; 600f66f451Sopenharmony_ci } 610f66f451Sopenharmony_ci dcount = any ? 1 : i; 620f66f451Sopenharmony_ci any = 1; 630f66f451Sopenharmony_ci 640f66f451Sopenharmony_ci // Output delimiters as necessary: not at beginning/end of line, 650f66f451Sopenharmony_ci // catch up if first few files had no input but a later one did. 660f66f451Sopenharmony_ci // Entire line with no input means no output. 670f66f451Sopenharmony_ci 680f66f451Sopenharmony_ci while (dcount) { 690f66f451Sopenharmony_ci 700f66f451Sopenharmony_ci // Find next delimiter, which can be "", \n, or UTF8 w/combining chars 710f66f451Sopenharmony_ci dstr = dpos; 720f66f451Sopenharmony_ci dlen = 0; 730f66f451Sopenharmony_ci dcount--; 740f66f451Sopenharmony_ci 750f66f451Sopenharmony_ci if (!*TT.d) {;} 760f66f451Sopenharmony_ci else if (*dpos == '\\') { 770f66f451Sopenharmony_ci if (*++dpos=='0') dpos++; 780f66f451Sopenharmony_ci else { 790f66f451Sopenharmony_ci dlen = 1; 800f66f451Sopenharmony_ci if ((c = unescape(*dpos))) { 810f66f451Sopenharmony_ci dstr = &c; 820f66f451Sopenharmony_ci dpos++; 830f66f451Sopenharmony_ci } 840f66f451Sopenharmony_ci } 850f66f451Sopenharmony_ci } else { 860f66f451Sopenharmony_ci while (0<(dlen = utf8towc(&wc, dpos, 99))) { 870f66f451Sopenharmony_ci dpos += dlen; 880f66f451Sopenharmony_ci if (!(dlen = wcwidth(wc))) continue; 890f66f451Sopenharmony_ci if (dlen<0) dpos = dstr+1; 900f66f451Sopenharmony_ci break; 910f66f451Sopenharmony_ci } 920f66f451Sopenharmony_ci dlen = dpos-dstr; 930f66f451Sopenharmony_ci } 940f66f451Sopenharmony_ci if (!*dpos) dpos = TT.d; 950f66f451Sopenharmony_ci 960f66f451Sopenharmony_ci if (dlen) fwrite(dstr, dlen, 1, stdout); 970f66f451Sopenharmony_ci } 980f66f451Sopenharmony_ci 990f66f451Sopenharmony_ci if (0<len) { 1000f66f451Sopenharmony_ci fwrite(buf, len-(buf[len-1]=='\n'), 1, stdout); 1010f66f451Sopenharmony_ci free(buf); 1020f66f451Sopenharmony_ci } 1030f66f451Sopenharmony_ci } 1040f66f451Sopenharmony_ci 1050f66f451Sopenharmony_ci // Only need a newline if we output something 1060f66f451Sopenharmony_ci if (any) xputc('\n'); 1070f66f451Sopenharmony_ci else break; 1080f66f451Sopenharmony_ci } 1090f66f451Sopenharmony_ci} 1100f66f451Sopenharmony_ci 1110f66f451Sopenharmony_cistatic void do_paste(int fd, char *name) 1120f66f451Sopenharmony_ci{ 1130f66f451Sopenharmony_ci FILE **fps = (void *)toybuf; 1140f66f451Sopenharmony_ci 1150f66f451Sopenharmony_ci if (!(fps[TT.files++] = (fd ? fdopen(fd, "r") : stdin))) perror_exit(0); 1160f66f451Sopenharmony_ci if (TT.files >= sizeof(toybuf)/sizeof(FILE *)) perror_exit("tilt"); 1170f66f451Sopenharmony_ci if (toys.optflags&FLAG_s) { 1180f66f451Sopenharmony_ci paste_files(); 1190f66f451Sopenharmony_ci xputc('\n'); 1200f66f451Sopenharmony_ci TT.files = 0; 1210f66f451Sopenharmony_ci } 1220f66f451Sopenharmony_ci} 1230f66f451Sopenharmony_ci 1240f66f451Sopenharmony_civoid paste_main(void) 1250f66f451Sopenharmony_ci{ 1260f66f451Sopenharmony_ci if (!(toys.optflags&FLAG_d)) TT.d = "\t"; 1270f66f451Sopenharmony_ci 1280f66f451Sopenharmony_ci loopfiles_rw(toys.optargs, O_RDONLY, 0, do_paste); 1290f66f451Sopenharmony_ci if (!(toys.optflags&FLAG_s)) paste_files(); 1300f66f451Sopenharmony_ci} 131