paste.c (3505B)
1 /* See LICENSE file for copyright and license details. */ 2 #include <locale.h> 3 #include <stdlib.h> 4 #include <stdio.h> 5 #include <string.h> 6 #include <unistd.h> 7 #include <wchar.h> 8 9 #include "util.h" 10 11 typedef struct { 12 FILE *fp; 13 const char *name; 14 } Fdescr; 15 16 static size_t unescape(wchar_t *); 17 static wint_t in(Fdescr *); 18 static void out(wchar_t); 19 static void sequential(Fdescr *, int, const wchar_t *, size_t); 20 static void parallel(Fdescr *, int, const wchar_t *, size_t); 21 22 static void 23 usage(void) 24 { 25 eprintf("usage: %s [-s] [-d list] file...\n", argv0); 26 } 27 28 int 29 main(int argc, char *argv[]) 30 { 31 const char *adelim = NULL; 32 int seq = 0; 33 wchar_t *delim = NULL; 34 size_t len; 35 Fdescr *dsc = NULL; 36 int i; 37 38 setlocale(LC_CTYPE, ""); 39 40 ARGBEGIN { 41 case 's': 42 seq = 1; 43 break; 44 case 'd': 45 adelim = EARGF(usage()); 46 break; 47 default: 48 usage(); 49 } ARGEND; 50 51 if (argc == 0) 52 usage(); 53 54 /* populate delimeters */ 55 if (!adelim) 56 adelim = "\t"; 57 58 len = mbstowcs(NULL, adelim, 0); 59 if (len == (size_t) - 1) 60 eprintf("invalid delimiter\n"); 61 62 delim = emalloc((len + 1) * sizeof(*delim)); 63 64 mbstowcs(delim, adelim, len); 65 len = unescape(delim); 66 if (len == 0) 67 eprintf("no delimiters specified\n"); 68 69 /* populate file list */ 70 dsc = emalloc(argc * sizeof(*dsc)); 71 72 for (i = 0; i < argc; i++) { 73 if (strcmp(argv[i], "-") == 0) 74 dsc[i].fp = stdin; 75 else 76 dsc[i].fp = fopen(argv[i], "r"); 77 78 if (!dsc[i].fp) 79 eprintf("can't open '%s':", argv[i]); 80 81 dsc[i].name = argv[i]; 82 } 83 84 if (seq) 85 sequential(dsc, argc, delim, len); 86 else 87 parallel(dsc, argc, delim, len); 88 89 for (i = 0; i < argc; i++) { 90 if (dsc[i].fp != stdin) 91 (void)fclose(dsc[i].fp); 92 } 93 94 free(delim); 95 free(dsc); 96 97 return 0; 98 } 99 100 static size_t 101 unescape(wchar_t *delim) 102 { 103 wchar_t c; 104 size_t i; 105 size_t len; 106 107 for (i = 0, len = 0; (c = delim[i++]) != '\0'; len++) { 108 if (c == '\\') { 109 switch (delim[i++]) { 110 case 'n': 111 delim[len] = '\n'; 112 break; 113 case 't': 114 delim[len] = '\t'; 115 break; 116 case '0': 117 delim[len] = '\0'; 118 break; 119 case '\\': 120 delim[len] = '\\'; 121 break; 122 case '\0': 123 default: 124 /* POSIX: unspecified results */ 125 return len; 126 } 127 } else 128 delim[len] = c; 129 } 130 131 return len; 132 } 133 134 static wint_t 135 in(Fdescr *f) 136 { 137 wint_t c = fgetwc(f->fp); 138 139 if (c == WEOF && ferror(f->fp)) 140 eprintf("'%s' read error:", f->name); 141 142 return c; 143 } 144 145 static void 146 out(wchar_t c) 147 { 148 putwchar(c); 149 if (ferror(stdout)) 150 eprintf("write error:"); 151 } 152 153 static void 154 sequential(Fdescr *dsc, int len, const wchar_t *delim, size_t cnt) 155 { 156 int i; 157 size_t d; 158 wint_t c, last; 159 160 for (i = 0; i < len; i++) { 161 d = 0; 162 last = WEOF; 163 164 while ((c = in(&dsc[i])) != WEOF) { 165 if (last == '\n') { 166 if (delim[d] != '\0') 167 out(delim[d]); 168 169 d++; 170 d %= cnt; 171 } 172 173 if (c != '\n') 174 out((wchar_t)c); 175 176 last = c; 177 } 178 179 if (last == '\n') 180 out((wchar_t)last); 181 } 182 } 183 184 static void 185 parallel(Fdescr *dsc, int len, const wchar_t *delim, size_t cnt) 186 { 187 int last, i; 188 wint_t c, o; 189 wchar_t d; 190 191 do { 192 last = 0; 193 for (i = 0; i < len; i++) { 194 d = delim[i % cnt]; 195 196 do { 197 o = in(&dsc[i]); 198 c = o; 199 switch (c) { 200 case WEOF: 201 if (last == 0) 202 break; 203 204 o = '\n'; 205 /* fallthrough */ 206 case '\n': 207 if (i != len - 1) 208 o = d; 209 break; 210 default: 211 break; 212 } 213 214 if (o != WEOF) { 215 /* pad with delimiters up to this point */ 216 while (++last < i) { 217 if (d != '\0') 218 out(d); 219 } 220 out((wchar_t)o); 221 } 222 } while (c != '\n' && c != WEOF); 223 } 224 } while (last > 0); 225 }