/*********************************************************************** * * * This software is part of the ast package * * Copyright (c) 1992-2007 AT&T Knowledge Ventures * * and is licensed under the * * Common Public License, Version 1.0 * * by AT&T Knowledge Ventures * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * * * * Information and Software Systems Research * * AT&T Research * * Florham Park NJ * * * * Glenn Fowler <gsf@research.att.com> * * David Korn <dgk@research.att.com> * * * ***********************************************************************/ #pragma prototyped /* * David Korn * AT&T Bell Laboratories * * paste [-s] [-d delim] [file] ... * * paste lines from files together */ static const char usage[] = "[-?\n@(#)$Id: paste (AT&T Research) 1999-06-22 $\n]" USAGE_LICENSE "[+NAME?paste - merge lines of files]" "[+DESCRIPTION?\bpaste\b concatenates the corresponding lines of a " "given input file and writes the resulting lines to standard " "output. By default \bpaste\b replaces the newline character of " "every line other than the last input file with the TAB character.]" "[+?Unless the \b-s\b option is specified, if an end-of-file is encountered " "on one or more input files, but not all input files, \bpaste\b " "behaves as if empty lines were read from the file(s) on which " "end-of-file was detected.]" "[+?Unless the \b-s\b option is specified, \bpaste\b is limited by " "the underlying operating system on how many \afile\a operands " "can be specified.]" "[+?If no \afile\a operands are given or if the \afile\a is \b-\b, \bpaste\b " "reads from standard input. The start of the file is defined as the " "current offset.]" "[s:serial?Paste the lines of one file at a time rather than one line " "from each file. In this case if the \b-d\b option is " "specified the delimiter will be reset to the first in the " "list at the beginning of each file.]" "[d:delimiters]:[list?\alist\a specifies a list of delimiters. These " "delimiters are used circularly instead of TAB to replace " "the newline character of the input lines. Unless the \b-s\b " "option is specified, the delimiter will be reset to the first " "element of \alist\a each time a line is processed from each file. " "The delimiter characters corresponding to \alist\a will be found " "by treating \alist\a as an ANSI-C string, except that the \b\\0\b " "sequence will insert the empty string instead of the null character.]" "\n" "\n[file ...]\n" "\n" "[+EXIT STATUS?]{" "[+0?All files processed successfully.]" "[+>0?An error occurred.]" "}" "[+SEE ALSO?\bcut\b(1), \bcat\b(1), \bjoin\b(1)]" ; #include <cmd.h> /* * paste the lines of the <nstreams> defined in <streams> and put results * to <out> */ static int paste(int nstream,Sfio_t* streams[],Sfio_t *out, register const char *delim,int dlen) { register const char *cp; register int d, n, more=1; register Sfio_t *fp; do { d = (dlen>0?0:-1); for(n=more-1,more=0; n < nstream;) { if(fp=streams[n]) { if(cp = sfgetr(fp,'\n',0)) { if(n==0) more = 1; else if(!more) /* first stream with output */ { if(dlen==1) sfnputc(out, *delim, n); else if(dlen>0) { for(d=n; d>dlen; d-=dlen) sfwrite(out,delim,dlen); if(d) sfwrite(out,delim,d); } more = n+1; } if(sfwrite(out,cp,sfvalue(fp)-((n+1)<nstream)) < 0) return(-1); } else streams[n] = 0; } if(++n<nstream && more && d>=0) { register int c; if(d >= dlen) d = 0; if(c=delim[d++]) sfputc(out,c); } else if(n==nstream && !streams[n-1] && more) sfputc(out,'\n'); } } while(more); return(0); } /* * Handles paste -s, for file <in> to file <out> using delimiters <delim> */ static int spaste(Sfio_t *in,register Sfio_t* out,register const char *delim,int dlen) { register const char *cp; register int d=0; if(cp = sfgetr(in,'\n',0)) { if(sfwrite(out,cp,sfvalue(in)-1) < 0) return(-1); } while(cp=sfgetr(in, '\n',0)) { if(dlen) { register int c; if(d >= dlen) d = 0; if(c=delim[d++]) sfputc(out,c); } if(sfwrite(out,cp,sfvalue(in)-1) < 0) return(-1); } sfputc(out,'\n'); return(0); } int b_paste(int argc,register char *argv[], void* context) { register int n, sflag=0; register Sfio_t *fp, **streams; register char *cp, *delim; int dlen; char defdelim[2]; cmdinit(argc, argv, context, ERROR_CATALOG, 0); delim = 0; while (n = optget(argv, usage)) switch (n) { case 'd': delim = opt_info.arg; break; case 's': sflag++; break; case ':': error(2, "%s", opt_info.arg); break; case '?': error(ERROR_usage(2), "%s", opt_info.arg); break; } argv += opt_info.index; if(error_info.errors) error(ERROR_usage(2),"%s", optusage(NiL)); if(delim) dlen = stresc(delim); else { *(delim = defdelim) = '\t'; dlen = 1; } if(cp = *argv) { n = argc - opt_info.index; argv++; } else n = 1; if(!sflag) { if (!(streams = (Sfio_t**)stakalloc(n*sizeof(Sfio_t*)))) error(ERROR_exit(1), "out of space"); n = 0; } do { if(!cp || streq(cp,"-")) fp = sfstdin; else if(!(fp = sfopen(NiL,cp,"r"))) { error(ERROR_system(0),"%s: cannot open",cp); error_info.errors = 1; } if(fp && sflag) { if(spaste(fp,sfstdout,delim,dlen) < 0) { error(ERROR_system(0),"write failed"); error_info.errors = 1; } if(fp!=sfstdin) sfclose(fp); } else streams[n++] = fp; } while(cp= *argv++); if(!sflag) { if(error_info.errors==0 && paste(n,streams,sfstdout,delim,dlen) < 0) { error(ERROR_system(0),"write failed"); error_info.errors = 1; } while(--n>=0) { if((fp=streams[n]) && fp!=sfstdin) sfclose(fp); } } return(error_info.errors); }