14887Schin /*********************************************************************** 24887Schin * * 34887Schin * This software is part of the ast package * 4*12068SRoger.Faulkner@Oracle.COM * Copyright (c) 1992-2010 AT&T Intellectual Property * 54887Schin * and is licensed under the * 64887Schin * Common Public License, Version 1.0 * 78462SApril.Chin@Sun.COM * by AT&T Intellectual Property * 84887Schin * * 94887Schin * A copy of the License is available at * 104887Schin * http://www.opensource.org/licenses/cpl1.0.txt * 114887Schin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 124887Schin * * 134887Schin * Information and Software Systems Research * 144887Schin * AT&T Research * 154887Schin * Florham Park NJ * 164887Schin * * 174887Schin * Glenn Fowler <gsf@research.att.com> * 184887Schin * David Korn <dgk@research.att.com> * 194887Schin * * 204887Schin ***********************************************************************/ 214887Schin #pragma prototyped 224887Schin /* 234887Schin * David Korn 244887Schin * AT&T Bell Laboratories 254887Schin * 264887Schin * paste [-s] [-d delim] [file] ... 274887Schin * 284887Schin * paste lines from files together 294887Schin */ 304887Schin 314887Schin static const char usage[] = 32*12068SRoger.Faulkner@Oracle.COM "[-?\n@(#)$Id: paste (AT&T Research) 2009-11-28 $\n]" 334887Schin USAGE_LICENSE 344887Schin "[+NAME?paste - merge lines of files]" 354887Schin "[+DESCRIPTION?\bpaste\b concatenates the corresponding lines of a " 364887Schin "given input file and writes the resulting lines to standard " 374887Schin "output. By default \bpaste\b replaces the newline character of " 384887Schin "every line other than the last input file with the TAB character.]" 394887Schin "[+?Unless the \b-s\b option is specified, if an end-of-file is encountered " 404887Schin "on one or more input files, but not all input files, \bpaste\b " 414887Schin "behaves as if empty lines were read from the file(s) on which " 424887Schin "end-of-file was detected.]" 434887Schin "[+?Unless the \b-s\b option is specified, \bpaste\b is limited by " 444887Schin "the underlying operating system on how many \afile\a operands " 454887Schin "can be specified.]" 464887Schin "[+?If no \afile\a operands are given or if the \afile\a is \b-\b, \bpaste\b " 474887Schin "reads from standard input. The start of the file is defined as the " 484887Schin "current offset.]" 494887Schin 504887Schin "[s:serial?Paste the lines of one file at a time rather than one line " 514887Schin "from each file. In this case if the \b-d\b option is " 524887Schin "specified the delimiter will be reset to the first in the " 534887Schin "list at the beginning of each file.]" 544887Schin "[d:delimiters]:[list?\alist\a specifies a list of delimiters. These " 554887Schin "delimiters are used circularly instead of TAB to replace " 564887Schin "the newline character of the input lines. Unless the \b-s\b " 574887Schin "option is specified, the delimiter will be reset to the first " 584887Schin "element of \alist\a each time a line is processed from each file. " 594887Schin "The delimiter characters corresponding to \alist\a will be found " 604887Schin "by treating \alist\a as an ANSI-C string, except that the \b\\0\b " 614887Schin "sequence will insert the empty string instead of the null character.]" 624887Schin "\n" 634887Schin "\n[file ...]\n" 644887Schin "\n" 654887Schin "[+EXIT STATUS?]{" 664887Schin "[+0?All files processed successfully.]" 674887Schin "[+>0?An error occurred.]" 684887Schin "}" 694887Schin "[+SEE ALSO?\bcut\b(1), \bcat\b(1), \bjoin\b(1)]" 704887Schin ; 714887Schin 72*12068SRoger.Faulkner@Oracle.COM #include <cmd.h> 734887Schin 74*12068SRoger.Faulkner@Oracle.COM typedef struct Delim_s 75*12068SRoger.Faulkner@Oracle.COM { 76*12068SRoger.Faulkner@Oracle.COM const char* chr; 77*12068SRoger.Faulkner@Oracle.COM size_t len; 78*12068SRoger.Faulkner@Oracle.COM } Delim_t; 794887Schin 804887Schin /* 814887Schin * paste the lines of the <nstreams> defined in <streams> and put results 824887Schin * to <out> 834887Schin */ 844887Schin 85*12068SRoger.Faulkner@Oracle.COM static int paste(int nstream,Sfio_t* streams[],Sfio_t *out, register const char *delim, int dsiz, int dlen, Delim_t* mp) 864887Schin { 874887Schin register const char *cp; 88*12068SRoger.Faulkner@Oracle.COM register int d, n, i, z, more=1; 894887Schin register Sfio_t *fp; 904887Schin do 914887Schin { 924887Schin d = (dlen>0?0:-1); 934887Schin for(n=more-1,more=0; n < nstream;) 944887Schin { 954887Schin if(fp=streams[n]) 964887Schin { 974887Schin if(cp = sfgetr(fp,'\n',0)) 984887Schin { 994887Schin if(n==0) 1004887Schin more = 1; 1014887Schin else if(!more) /* first stream with output */ 1024887Schin { 103*12068SRoger.Faulkner@Oracle.COM if(dsiz == 1) 1044887Schin sfnputc(out, *delim, n); 1054887Schin else if(dlen>0) 1064887Schin { 1074887Schin for(d=n; d>dlen; d-=dlen) 108*12068SRoger.Faulkner@Oracle.COM sfwrite(out,delim,dsiz); 1094887Schin if(d) 110*12068SRoger.Faulkner@Oracle.COM { 111*12068SRoger.Faulkner@Oracle.COM if(mp) 112*12068SRoger.Faulkner@Oracle.COM for (i = z = 0; i < d; i++) 113*12068SRoger.Faulkner@Oracle.COM z += mp[i].len; 114*12068SRoger.Faulkner@Oracle.COM else 115*12068SRoger.Faulkner@Oracle.COM z = d; 116*12068SRoger.Faulkner@Oracle.COM sfwrite(out,delim,z); 117*12068SRoger.Faulkner@Oracle.COM } 1184887Schin } 1194887Schin more = n+1; 1204887Schin } 1214887Schin if(sfwrite(out,cp,sfvalue(fp)-((n+1)<nstream)) < 0) 1224887Schin return(-1); 1234887Schin } 1244887Schin else 1254887Schin streams[n] = 0; 1264887Schin } 1274887Schin if(++n<nstream && more && d>=0) 1284887Schin { 1294887Schin register int c; 1304887Schin if(d >= dlen) 1314887Schin d = 0; 132*12068SRoger.Faulkner@Oracle.COM if(mp) 133*12068SRoger.Faulkner@Oracle.COM sfwrite(out,mp[d].chr,mp[d].len); 134*12068SRoger.Faulkner@Oracle.COM else if(c=delim[d]) 1354887Schin sfputc(out,c); 136*12068SRoger.Faulkner@Oracle.COM d++; 1374887Schin } 1384887Schin else if(n==nstream && !streams[n-1] && more) 1394887Schin sfputc(out,'\n'); 1404887Schin } 141*12068SRoger.Faulkner@Oracle.COM } while(more); 1424887Schin return(0); 1434887Schin } 1444887Schin 1454887Schin /* 1464887Schin * Handles paste -s, for file <in> to file <out> using delimiters <delim> 1474887Schin */ 148*12068SRoger.Faulkner@Oracle.COM static int spaste(Sfio_t *in,register Sfio_t* out,register const char *delim,int dsiz,int dlen,Delim_t* mp) 1494887Schin { 1504887Schin register const char *cp; 1514887Schin register int d=0; 152*12068SRoger.Faulkner@Oracle.COM if((cp = sfgetr(in,'\n',0)) && sfwrite(out,cp,sfvalue(in)-1) < 0) 153*12068SRoger.Faulkner@Oracle.COM return(-1); 1544887Schin while(cp=sfgetr(in, '\n',0)) 1554887Schin { 1564887Schin if(dlen) 1574887Schin { 1584887Schin register int c; 1594887Schin if(d >= dlen) 1604887Schin d = 0; 161*12068SRoger.Faulkner@Oracle.COM if(mp) 162*12068SRoger.Faulkner@Oracle.COM sfwrite(out,mp[d].chr,mp[d].len); 163*12068SRoger.Faulkner@Oracle.COM else if(c=delim[d]) 1644887Schin sfputc(out,c); 165*12068SRoger.Faulkner@Oracle.COM d++; 1664887Schin } 1674887Schin if(sfwrite(out,cp,sfvalue(in)-1) < 0) 1684887Schin return(-1); 1694887Schin } 1704887Schin sfputc(out,'\n'); 1714887Schin return(0); 1724887Schin } 1734887Schin 1744887Schin int 1754887Schin b_paste(int argc,register char *argv[], void* context) 1764887Schin { 1774887Schin register int n, sflag=0; 1784887Schin register Sfio_t *fp, **streams; 1794887Schin register char *cp, *delim; 180*12068SRoger.Faulkner@Oracle.COM char *ep; 181*12068SRoger.Faulkner@Oracle.COM Delim_t *mp; 182*12068SRoger.Faulkner@Oracle.COM int dlen, dsiz; 1834887Schin char defdelim[2]; 1844887Schin 1854887Schin cmdinit(argc, argv, context, ERROR_CATALOG, 0); 1864887Schin delim = 0; 1874887Schin while (n = optget(argv, usage)) switch (n) 1884887Schin { 1894887Schin case 'd': 1904887Schin delim = opt_info.arg; 1914887Schin break; 1924887Schin case 's': 1934887Schin sflag++; 1944887Schin break; 1954887Schin case ':': 1964887Schin error(2, "%s", opt_info.arg); 1974887Schin break; 1984887Schin case '?': 1994887Schin error(ERROR_usage(2), "%s", opt_info.arg); 2004887Schin break; 2014887Schin } 2024887Schin argv += opt_info.index; 2034887Schin if(error_info.errors) 2044887Schin error(ERROR_usage(2),"%s", optusage(NiL)); 205*12068SRoger.Faulkner@Oracle.COM if(!delim || !*delim) 206*12068SRoger.Faulkner@Oracle.COM { 207*12068SRoger.Faulkner@Oracle.COM delim = defdelim; 208*12068SRoger.Faulkner@Oracle.COM delim[0] = '\t'; 209*12068SRoger.Faulkner@Oracle.COM delim[1] = 0; 210*12068SRoger.Faulkner@Oracle.COM } 211*12068SRoger.Faulkner@Oracle.COM dlen = dsiz = stresc(delim); 212*12068SRoger.Faulkner@Oracle.COM mp = 0; 213*12068SRoger.Faulkner@Oracle.COM if (mbwide()) 2144887Schin { 215*12068SRoger.Faulkner@Oracle.COM cp = delim; 216*12068SRoger.Faulkner@Oracle.COM ep = delim + dlen; 217*12068SRoger.Faulkner@Oracle.COM dlen = 0; 218*12068SRoger.Faulkner@Oracle.COM while (cp < ep) 219*12068SRoger.Faulkner@Oracle.COM { 220*12068SRoger.Faulkner@Oracle.COM mbchar(cp); 221*12068SRoger.Faulkner@Oracle.COM dlen++; 222*12068SRoger.Faulkner@Oracle.COM } 223*12068SRoger.Faulkner@Oracle.COM if(dlen < dsiz) 224*12068SRoger.Faulkner@Oracle.COM { 225*12068SRoger.Faulkner@Oracle.COM if (!(mp = newof(0, Delim_t, dlen, 0))) 226*12068SRoger.Faulkner@Oracle.COM error(ERROR_system(1), "out of space"); 227*12068SRoger.Faulkner@Oracle.COM cp = delim; 228*12068SRoger.Faulkner@Oracle.COM dlen = 0; 229*12068SRoger.Faulkner@Oracle.COM while (cp < ep) 230*12068SRoger.Faulkner@Oracle.COM { 231*12068SRoger.Faulkner@Oracle.COM mp[dlen].chr = cp; 232*12068SRoger.Faulkner@Oracle.COM mbchar(cp); 233*12068SRoger.Faulkner@Oracle.COM mp[dlen].len = cp - mp[dlen].chr; 234*12068SRoger.Faulkner@Oracle.COM dlen++; 235*12068SRoger.Faulkner@Oracle.COM } 236*12068SRoger.Faulkner@Oracle.COM } 2374887Schin } 2384887Schin if(cp = *argv) 2394887Schin { 2404887Schin n = argc - opt_info.index; 2414887Schin argv++; 2424887Schin } 2434887Schin else 2444887Schin n = 1; 2454887Schin if(!sflag) 2464887Schin { 2474887Schin if (!(streams = (Sfio_t**)stakalloc(n*sizeof(Sfio_t*)))) 2484887Schin error(ERROR_exit(1), "out of space"); 2494887Schin n = 0; 2504887Schin } 2514887Schin do 2524887Schin { 2534887Schin if(!cp || streq(cp,"-")) 2544887Schin fp = sfstdin; 2554887Schin else if(!(fp = sfopen(NiL,cp,"r"))) 2564887Schin error(ERROR_system(0),"%s: cannot open",cp); 2574887Schin if(fp && sflag) 2584887Schin { 259*12068SRoger.Faulkner@Oracle.COM if(spaste(fp,sfstdout,delim,dsiz,dlen,mp) < 0) 2604887Schin error(ERROR_system(0),"write failed"); 2614887Schin if(fp!=sfstdin) 2624887Schin sfclose(fp); 2634887Schin } 2648462SApril.Chin@Sun.COM else if(!sflag) 2654887Schin streams[n++] = fp; 2668462SApril.Chin@Sun.COM } while(cp= *argv++); 2674887Schin if(!sflag) 2684887Schin { 269*12068SRoger.Faulkner@Oracle.COM if(error_info.errors==0 && paste(n,streams,sfstdout,delim,dsiz,dlen,mp) < 0) 2704887Schin error(ERROR_system(0),"write failed"); 2714887Schin while(--n>=0) 2724887Schin if((fp=streams[n]) && fp!=sfstdin) 2734887Schin sfclose(fp); 2744887Schin } 275*12068SRoger.Faulkner@Oracle.COM if (mp) 276*12068SRoger.Faulkner@Oracle.COM free(mp); 2774887Schin return(error_info.errors); 2784887Schin } 279