1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1992-2010 AT&T Intellectual Property * 5 * and is licensed under the * 6 * Common Public License, Version 1.0 * 7 * by AT&T Intellectual Property * 8 * * 9 * A copy of the License is available at * 10 * http://www.opensource.org/licenses/cpl1.0.txt * 11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 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) 2009-11-28 $\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,register char *argv[], void* 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 while (n = optget(argv, usage)) switch (n) 188 { 189 case 'd': 190 delim = opt_info.arg; 191 break; 192 case 's': 193 sflag++; 194 break; 195 case ':': 196 error(2, "%s", opt_info.arg); 197 break; 198 case '?': 199 error(ERROR_usage(2), "%s", opt_info.arg); 200 break; 201 } 202 argv += opt_info.index; 203 if(error_info.errors) 204 error(ERROR_usage(2),"%s", optusage(NiL)); 205 if(!delim || !*delim) 206 { 207 delim = defdelim; 208 delim[0] = '\t'; 209 delim[1] = 0; 210 } 211 dlen = dsiz = stresc(delim); 212 mp = 0; 213 if (mbwide()) 214 { 215 cp = delim; 216 ep = delim + dlen; 217 dlen = 0; 218 while (cp < ep) 219 { 220 mbchar(cp); 221 dlen++; 222 } 223 if(dlen < dsiz) 224 { 225 if (!(mp = newof(0, Delim_t, dlen, 0))) 226 error(ERROR_system(1), "out of space"); 227 cp = delim; 228 dlen = 0; 229 while (cp < ep) 230 { 231 mp[dlen].chr = cp; 232 mbchar(cp); 233 mp[dlen].len = cp - mp[dlen].chr; 234 dlen++; 235 } 236 } 237 } 238 if(cp = *argv) 239 { 240 n = argc - opt_info.index; 241 argv++; 242 } 243 else 244 n = 1; 245 if(!sflag) 246 { 247 if (!(streams = (Sfio_t**)stakalloc(n*sizeof(Sfio_t*)))) 248 error(ERROR_exit(1), "out of space"); 249 n = 0; 250 } 251 do 252 { 253 if(!cp || streq(cp,"-")) 254 fp = sfstdin; 255 else if(!(fp = sfopen(NiL,cp,"r"))) 256 error(ERROR_system(0),"%s: cannot open",cp); 257 if(fp && sflag) 258 { 259 if(spaste(fp,sfstdout,delim,dsiz,dlen,mp) < 0) 260 error(ERROR_system(0),"write failed"); 261 if(fp!=sfstdin) 262 sfclose(fp); 263 } 264 else if(!sflag) 265 streams[n++] = fp; 266 } while(cp= *argv++); 267 if(!sflag) 268 { 269 if(error_info.errors==0 && paste(n,streams,sfstdout,delim,dsiz,dlen,mp) < 0) 270 error(ERROR_system(0),"write failed"); 271 while(--n>=0) 272 if((fp=streams[n]) && fp!=sfstdin) 273 sfclose(fp); 274 } 275 if (mp) 276 free(mp); 277 return(error_info.errors); 278 } 279