1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1992-2012 AT&T Intellectual Property * 5 * and is licensed under the * 6 * Eclipse Public License, Version 1.0 * 7 * by AT&T Intellectual Property * 8 * * 9 * A copy of the License is available at * 10 * http://www.eclipse.org/org/documents/epl-v10.html * 11 * (with md5 checksum b35adb5213ca9657e911e9befb180842) * 12 * * 13 * Information and Software Systems Research * 14 * AT&T Research * 15 * Florham Park NJ * 16 * * 17 * Glenn Fowler <gsf@research.att.com> * 18 * David Korn <dgk@research.att.com> * 19 * * 20 ***********************************************************************/ 21 #pragma prototyped 22 /* 23 * David Korn 24 * AT&T Bell Laboratories 25 * 26 * paste [-s] [-d delim] [file] ... 27 * 28 * paste lines from files together 29 */ 30 31 static const char usage[] = 32 "[-?\n@(#)$Id: paste (AT&T Research) 2010-06-12 $\n]" 33 USAGE_LICENSE 34 "[+NAME?paste - merge lines of files]" 35 "[+DESCRIPTION?\bpaste\b concatenates the corresponding lines of a " 36 "given input file and writes the resulting lines to standard " 37 "output. By default \bpaste\b replaces the newline character of " 38 "every line other than the last input file with the TAB character.]" 39 "[+?Unless the \b-s\b option is specified, if an end-of-file is encountered " 40 "on one or more input files, but not all input files, \bpaste\b " 41 "behaves as if empty lines were read from the file(s) on which " 42 "end-of-file was detected.]" 43 "[+?Unless the \b-s\b option is specified, \bpaste\b is limited by " 44 "the underlying operating system on how many \afile\a operands " 45 "can be specified.]" 46 "[+?If no \afile\a operands are given or if the \afile\a is \b-\b, \bpaste\b " 47 "reads from standard input. The start of the file is defined as the " 48 "current offset.]" 49 50 "[s:serial?Paste the lines of one file at a time rather than one line " 51 "from each file. In this case if the \b-d\b option is " 52 "specified the delimiter will be reset to the first in the " 53 "list at the beginning of each file.]" 54 "[d:delimiters]:[list?\alist\a specifies a list of delimiters. These " 55 "delimiters are used circularly instead of TAB to replace " 56 "the newline character of the input lines. Unless the \b-s\b " 57 "option is specified, the delimiter will be reset to the first " 58 "element of \alist\a each time a line is processed from each file. " 59 "The delimiter characters corresponding to \alist\a will be found " 60 "by treating \alist\a as an ANSI-C string, except that the \b\\0\b " 61 "sequence will insert the empty string instead of the null character.]" 62 "\n" 63 "\n[file ...]\n" 64 "\n" 65 "[+EXIT STATUS?]{" 66 "[+0?All files processed successfully.]" 67 "[+>0?An error occurred.]" 68 "}" 69 "[+SEE ALSO?\bcut\b(1), \bcat\b(1), \bjoin\b(1)]" 70 ; 71 72 #include <cmd.h> 73 74 typedef struct Delim_s 75 { 76 const char* chr; 77 size_t len; 78 } Delim_t; 79 80 /* 81 * paste the lines of the <nstreams> defined in <streams> and put results 82 * to <out> 83 */ 84 85 static int paste(int nstream,Sfio_t* streams[],Sfio_t *out, register const char *delim, int dsiz, int dlen, Delim_t* mp) 86 { 87 register const char *cp; 88 register int d, n, i, z, more=1; 89 register Sfio_t *fp; 90 do 91 { 92 d = (dlen>0?0:-1); 93 for(n=more-1,more=0; n < nstream;) 94 { 95 if(fp=streams[n]) 96 { 97 if(cp = sfgetr(fp,'\n',0)) 98 { 99 if(n==0) 100 more = 1; 101 else if(!more) /* first stream with output */ 102 { 103 if(dsiz == 1) 104 sfnputc(out, *delim, n); 105 else if(dlen>0) 106 { 107 for(d=n; d>dlen; d-=dlen) 108 sfwrite(out,delim,dsiz); 109 if(d) 110 { 111 if(mp) 112 for (i = z = 0; i < d; i++) 113 z += mp[i].len; 114 else 115 z = d; 116 sfwrite(out,delim,z); 117 } 118 } 119 more = n+1; 120 } 121 if(sfwrite(out,cp,sfvalue(fp)-((n+1)<nstream)) < 0) 122 return(-1); 123 } 124 else 125 streams[n] = 0; 126 } 127 if(++n<nstream && more && d>=0) 128 { 129 register int c; 130 if(d >= dlen) 131 d = 0; 132 if(mp) 133 sfwrite(out,mp[d].chr,mp[d].len); 134 else if(c=delim[d]) 135 sfputc(out,c); 136 d++; 137 } 138 else if(n==nstream && !streams[n-1] && more) 139 sfputc(out,'\n'); 140 } 141 } while(more); 142 return(0); 143 } 144 145 /* 146 * Handles paste -s, for file <in> to file <out> using delimiters <delim> 147 */ 148 static int spaste(Sfio_t *in,register Sfio_t* out,register const char *delim,int dsiz,int dlen,Delim_t* mp) 149 { 150 register const char *cp; 151 register int d=0; 152 if((cp = sfgetr(in,'\n',0)) && sfwrite(out,cp,sfvalue(in)-1) < 0) 153 return(-1); 154 while(cp=sfgetr(in, '\n',0)) 155 { 156 if(dlen) 157 { 158 register int c; 159 if(d >= dlen) 160 d = 0; 161 if(mp) 162 sfwrite(out,mp[d].chr,mp[d].len); 163 else if(c=delim[d]) 164 sfputc(out,c); 165 d++; 166 } 167 if(sfwrite(out,cp,sfvalue(in)-1) < 0) 168 return(-1); 169 } 170 sfputc(out,'\n'); 171 return(0); 172 } 173 174 int 175 b_paste(int argc, char** argv, Shbltin_t* context) 176 { 177 register int n, sflag=0; 178 register Sfio_t *fp, **streams; 179 register char *cp, *delim; 180 char *ep; 181 Delim_t *mp; 182 int dlen, dsiz; 183 char defdelim[2]; 184 185 cmdinit(argc, argv, context, ERROR_CATALOG, 0); 186 delim = 0; 187 for (;;) 188 { 189 switch (optget(argv, usage)) 190 { 191 case 'd': 192 delim = opt_info.arg; 193 continue; 194 case 's': 195 sflag++; 196 continue; 197 case ':': 198 error(2, "%s", opt_info.arg); 199 break; 200 case '?': 201 error(ERROR_usage(2), "%s", opt_info.arg); 202 break; 203 } 204 break; 205 } 206 argv += opt_info.index; 207 if(error_info.errors) 208 error(ERROR_usage(2),"%s", optusage(NiL)); 209 if(!delim || !*delim) 210 { 211 delim = defdelim; 212 delim[0] = '\t'; 213 delim[1] = 0; 214 } 215 if (!(delim = strdup(delim))) 216 error(ERROR_system(1), "out of space"); 217 dlen = dsiz = stresc(delim); 218 mp = 0; 219 if (mbwide()) 220 { 221 cp = delim; 222 ep = delim + dlen; 223 dlen = 0; 224 while (cp < ep) 225 { 226 mbchar(cp); 227 dlen++; 228 } 229 if(dlen < dsiz) 230 { 231 if (!(mp = newof(0, Delim_t, dlen, 0))) 232 { 233 free(delim); 234 error(ERROR_system(1), "out of space"); 235 } 236 cp = delim; 237 dlen = 0; 238 while (cp < ep) 239 { 240 mp[dlen].chr = cp; 241 mbchar(cp); 242 mp[dlen].len = cp - mp[dlen].chr; 243 dlen++; 244 } 245 } 246 } 247 if(cp = *argv) 248 { 249 n = argc - opt_info.index; 250 argv++; 251 } 252 else 253 n = 1; 254 if(!sflag) 255 { 256 if (!(streams = (Sfio_t**)stakalloc(n*sizeof(Sfio_t*)))) 257 error(ERROR_exit(1), "out of space"); 258 n = 0; 259 } 260 do 261 { 262 if(!cp || streq(cp,"-")) 263 fp = sfstdin; 264 else if(!(fp = sfopen(NiL,cp,"r"))) 265 error(ERROR_system(0),"%s: cannot open",cp); 266 if(fp && sflag) 267 { 268 if(spaste(fp,sfstdout,delim,dsiz,dlen,mp) < 0) 269 error(ERROR_system(0),"write failed"); 270 if(fp!=sfstdin) 271 sfclose(fp); 272 } 273 else if(!sflag) 274 streams[n++] = fp; 275 } while(cp= *argv++); 276 if(!sflag) 277 { 278 if(error_info.errors==0 && paste(n,streams,sfstdout,delim,dsiz,dlen,mp) < 0) 279 error(ERROR_system(0),"write failed"); 280 while(--n>=0) 281 if((fp=streams[n]) && fp!=sfstdin) 282 sfclose(fp); 283 } 284 if (mp) 285 free(mp); 286 free(delim); 287 return(error_info.errors); 288 } 289