1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1992-2009 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) 2008-04-01 $\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 73 #include <cmd.h> 74 75 /* 76 * paste the lines of the <nstreams> defined in <streams> and put results 77 * to <out> 78 */ 79 80 static int paste(int nstream,Sfio_t* streams[],Sfio_t *out, register const char *delim,int dlen) 81 { 82 register const char *cp; 83 register int d, n, more=1; 84 register Sfio_t *fp; 85 do 86 { 87 d = (dlen>0?0:-1); 88 for(n=more-1,more=0; n < nstream;) 89 { 90 if(fp=streams[n]) 91 { 92 if(cp = sfgetr(fp,'\n',0)) 93 { 94 if(n==0) 95 more = 1; 96 else if(!more) /* first stream with output */ 97 { 98 if(dlen==1) 99 sfnputc(out, *delim, n); 100 else if(dlen>0) 101 { 102 for(d=n; d>dlen; d-=dlen) 103 sfwrite(out,delim,dlen); 104 if(d) 105 sfwrite(out,delim,d); 106 } 107 more = n+1; 108 } 109 if(sfwrite(out,cp,sfvalue(fp)-((n+1)<nstream)) < 0) 110 return(-1); 111 } 112 else 113 streams[n] = 0; 114 } 115 if(++n<nstream && more && d>=0) 116 { 117 register int c; 118 if(d >= dlen) 119 d = 0; 120 if(c=delim[d++]) 121 sfputc(out,c); 122 } 123 else if(n==nstream && !streams[n-1] && more) 124 sfputc(out,'\n'); 125 } 126 } 127 while(more); 128 return(0); 129 } 130 131 /* 132 * Handles paste -s, for file <in> to file <out> using delimiters <delim> 133 */ 134 static int spaste(Sfio_t *in,register Sfio_t* out,register const char *delim,int dlen) 135 { 136 register const char *cp; 137 register int d=0; 138 if(cp = sfgetr(in,'\n',0)) 139 { 140 if(sfwrite(out,cp,sfvalue(in)-1) < 0) 141 return(-1); 142 } 143 while(cp=sfgetr(in, '\n',0)) 144 { 145 if(dlen) 146 { 147 register int c; 148 if(d >= dlen) 149 d = 0; 150 if(c=delim[d++]) 151 sfputc(out,c); 152 } 153 if(sfwrite(out,cp,sfvalue(in)-1) < 0) 154 return(-1); 155 } 156 sfputc(out,'\n'); 157 return(0); 158 } 159 160 int 161 b_paste(int argc,register char *argv[], void* context) 162 { 163 register int n, sflag=0; 164 register Sfio_t *fp, **streams; 165 register char *cp, *delim; 166 int dlen; 167 char defdelim[2]; 168 169 cmdinit(argc, argv, context, ERROR_CATALOG, 0); 170 delim = 0; 171 while (n = optget(argv, usage)) switch (n) 172 { 173 case 'd': 174 delim = opt_info.arg; 175 break; 176 case 's': 177 sflag++; 178 break; 179 case ':': 180 error(2, "%s", opt_info.arg); 181 break; 182 case '?': 183 error(ERROR_usage(2), "%s", opt_info.arg); 184 break; 185 } 186 argv += opt_info.index; 187 if(error_info.errors) 188 error(ERROR_usage(2),"%s", optusage(NiL)); 189 if(delim) 190 dlen = stresc(delim); 191 else 192 { 193 *(delim = defdelim) = '\t'; 194 dlen = 1; 195 } 196 if(cp = *argv) 197 { 198 n = argc - opt_info.index; 199 argv++; 200 } 201 else 202 n = 1; 203 if(!sflag) 204 { 205 if (!(streams = (Sfio_t**)stakalloc(n*sizeof(Sfio_t*)))) 206 error(ERROR_exit(1), "out of space"); 207 n = 0; 208 } 209 do 210 { 211 if(!cp || streq(cp,"-")) 212 fp = sfstdin; 213 else if(!(fp = sfopen(NiL,cp,"r"))) 214 error(ERROR_system(0),"%s: cannot open",cp); 215 if(fp && sflag) 216 { 217 if(spaste(fp,sfstdout,delim,dlen) < 0) 218 error(ERROR_system(0),"write failed"); 219 if(fp!=sfstdin) 220 sfclose(fp); 221 } 222 else if(!sflag) 223 streams[n++] = fp; 224 } while(cp= *argv++); 225 if(!sflag) 226 { 227 if(error_info.errors==0 && paste(n,streams,sfstdout,delim,dlen) < 0) 228 error(ERROR_system(0),"write failed"); 229 while(--n>=0) 230 if((fp=streams[n]) && fp!=sfstdin) 231 sfclose(fp); 232 } 233 return(error_info.errors); 234 } 235 236