v08i078: Public-domain implementations of cut(1) and paste(1)

sources-request at mirror.UUCP sources-request at mirror.UUCP
Wed Feb 25 08:05:07 AEST 1987


Submitted by: pyramid!ptsfa!ptsfc!jgw (John Weald)
Mod.sources: Volume 8, Issue 78
Archive-name: cut+paste

Here is a verion of AT&T's cut and paste, it was not written from the source. 
Cheers!
	John Weald
	{ihnp4, pyramid}!ptsfa!ptsfc!jgw

[  This also includes another re-implementaion of getopt.  --r$  ]

#! /bin/sh
# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
# If all goes well, you will see the message "End of shell archive."
# Contents:  README cut.1 cut.c getopt.c makefile paste.1 paste.c
#   spaste.c
PATH=/bin:/usr/bin:/usr/ucb; export PATH
echo shar: extracting "'README'" '(968 characters)'
if test -f 'README' ; then 
  echo shar: will not over-write existing file "'README'"
else
sed 's/^X//' >README <<'@//E*O*F README//'
XThis is a public domain version of AT&T's cut and paste commands. Included is
Xyet another version of getopt(3). If you already have a version then 
Xchange the makefile.
X
XThe software has been tested on both 4.2BSD (CCI Power 6/32 aka Sperry 7000/40)
Xand SVR2 (3b2/400). It is currently in production mode on 4.2.
X
XThe only known difference is that this cut does not require the list to be in 
Xassending order.  e.g.
X	cut -f4,2,1 x  <=>  cut -f1,2,4 x
X
XWhen a range is specified then the range must be in order e.g.
X	cut -f3-2
Xwill cause an error message.
X	
XDepending upon your point of view this may or may not be a 
Xproblem.
X
XError messages are a little more helpful than in SVR1, in particular they 
Xtell you what the limits are for line lenghts etc.
X
XFeel free to change the software, send any bug reports to
X	{ihnp4, pyramid}!ptsfa!ptsfc!ccm   
Xsince I am off the unix mail until the next assignment!!
X
XHave fun,
X		John Weald
X		AGS Computers, Inc.
X		415-463-8711
X
@//E*O*F README//
if test 968 -ne "`wc -c <'README'`"; then
    echo shar: error transmitting "'README'" '(should have been 968 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'cut.1'" '(2652 characters)'
if test -f 'cut.1' ; then 
  echo shar: will not over-write existing file "'cut.1'"
else
sed 's/^X//' >cut.1 <<'@//E*O*F cut.1//'
X.if t .ds ' \h at .05m@\s+4\v at .333m@\'\v at -.333m@\s-4\h at .05m@
X.if n .ds ' '
X.if t .ds ` \h at .05m@\s+4\v at .333m@\`\v at -.333m@\s-4\h at .05m@
X.if n .ds ` `
X.TH CUT 1
X.SH NAME
Xcut \- cut out selected fields of each line of a file
X.SH SYNOPSIS
X\fBcut \-c\fP\^list [\|file1 file2 ...]
X.br
X\fBcut \-f\fP\^list [\fB\-d\fP\|char\|] [\fB\-s\fP] [\|file1 file2 ...]
X.SH DESCRIPTION
XUse
X.I cut\^
Xto cut out columns from a table or 
Xfields from each line of a file; in data base parlance, it implements
Xthe projection of a relation.
XThe fields as specified by
X.I list\^
Xcan be fixed length,
Xi.e., character positions as on a punched card
X(\fB\-c\fP option) or the length can vary from line to line
Xand be marked with a field delimiter character like
X.I tab\^
X(\fB\-f\fP option).
X.I Cut\^
Xcan be used as a filter;
Xif no files are given, the standard input is used.
X.PP
XThe meanings of the options are:
X.br
X.TP "\w'\-d\ char\ \ 'u"
X.I list\^
XA comma-separated 
Xlist of integer field numbers,
Xwith optional \fB\-\fP to indicate ranges
Xas in the 
X.B \-o
Xoption of
X.IR nroff / troff
Xfor page ranges;
Xe.g.,
X.BR 1,4,7 ;
X.BR 1\-3,8 ;
X.B \-5,10
X(short for \fB1\-5,10\fP); or
X.B 3\-
X(short for third through last field).
X.TP
X\fB\-c\fP\^\fIlist\fP
XThe
X.I list\^
Xfollowing 
X.B \-c
Xspecifies character
Xpositions (e.g.,
X.B \-c1\-72
Xwould pass the first 72 characters
Xof each line).
X.TP
X\fB\-f\fP\^\fIlist\fP
XThe \fIlist\fP following 
X.B \-f
Xis a list of fields
Xassumed to be separated in the file by a delimiter character (see 
X.B \-d
X);
Xe.g.,
X.B \-f1,7
Xcopies the first and seventh field only.
XLines with no field delimiters will be passed through intact (useful
Xfor table subheadings), unless 
X.B \-s
Xis specified.
X.TP
X\fB\-d\fP\^\fIchar\fP
XThe character following 
X.B \-d
Xis the field delimiter
X(\c
X.B \-f
Xoption only).
XDefault is
X.IR tab .
XSpace or other characters with special meaning to the shell must be quoted.
X.TP
X.B \-s
XSuppresses lines with no delimiter characters in case of
X.B \-f
Xoption.
XUnless specified, lines with no delimiters will be passed through untouched.
X.PP
XEither the
X.B \-c
Xor 
X.B \-f
Xoption must be specified.
X.SH HINTS
XUse
X.IR grep (1)
Xto make horizontal ``cuts''
X(by context) through a file, or
X.IR paste (1)
Xto put files together column-wise
X(i.e., horizontally).
XTo reorder columns in a table, use
X.I cut\^
Xand
X.IR paste .
X.SH EXAMPLES
X.TP 2.25i
Xcut \-d: \-f1,5 /etc/passwd
Xmapping of user \s-1ID\s0s to names
X.TP
Xname=\*`who am i | cut \-f1 \-d" "\*`
Xto set 
X.B name
Xto current login
Xname.
X.SH DIAGNOSTICS
X.PP
XError messages may come from getopt(3). All error messages contain limits
Xfor line and field lenghts.
X.SH SEE ALSO
Xgrep(1),
Xpaste(1).
Xgetopt(3)
@//E*O*F cut.1//
if test 2652 -ne "`wc -c <'cut.1'`"; then
    echo shar: error transmitting "'cut.1'" '(should have been 2652 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'cut.c'" '(4941 characters)'
if test -f 'cut.c' ; then 
  echo shar: will not over-write existing file "'cut.c'"
else
sed 's/^X//' >cut.c <<'@//E*O*F cut.c//'
X/*
X *  This acts the same as SV cut(1), except that the list of numbers
X *  does not have to be assending.
X *
X * John Weald
X */
X#include <stdio.h>
X#include <ctype.h>
X
X#define MAXLINE	1024		/* The max. length of a line		*/
X
Xextern void exit();
X
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X	extern char *optarg;
X	extern int optind;
X
X	static int fields[MAXLINE];/* The field markers. True if this field */
X				   /* is to be cut, False otherwise	*/
X	FILE *fp;		   /* All the input files or stdin	*/
X	char buf[MAXLINE];	   /* The input buffer			*/
X	int c;			   /* The command line option		*/
X	int err = 0;		   /* True if error in command line	*/
X	int fflag = 0;		   /* True if -f on command line	*/
X	int cflag = 0;		   /* True if -c on command line	*/
X	int suppress = 0;	   /* Suppress lines with no delimitor	*/
X	char fs = '\t';		   /* The field separator		*/
X
X	
X	while ((c = getopt(argc, argv, "f:d:c:s")) != EOF)
X	{
X		switch (c)
X		{
X		   case 'f':
X			/* By Field */
X			list(fields, optarg);
X			fflag++;
X			if (cflag)
X				err++;
X			break;
X
X		   case 'c':
X			/* By character */
X			list(fields, optarg);
X			/* Implied suppress */
X			suppress++;
X			cflag++;
X			if (fflag)
X				err++;
X			break;
X
X		   case 'd':
X			/* A new field spererator */
X			fs = *optarg;
X			break;
X
X		   case 's':
X			suppress++;
X			break;
X
X		   default:
X			prusage();
X		}
X	}
X
X	if (!cflag && !fflag)
X	{
X		fprintf(stderr, "Must have one of -f or -c\n");
X		err++;
X	}
X	if (err)
X		prusage();
X
X
X	/*
X	 * Loop on all the files.
X	 */
X	do {
X		if (optind == argc)
X			fp = stdin;
X		else if ((fp = fopen(argv[optind], "r")) == (FILE *)NULL)
X		{
X			fprintf(stderr, "Failed to open file %s\n", 
X					argv[optind]);
X			exit(1);
X		}
X
X		/*
X		 * Loop on all lines in the file.
X		 */
X		while (fgets(buf, sizeof(buf), fp) != (char *)NULL)
X		{
X			cut(buf, fields, fs, suppress, cflag);
X		}
X		(void)fclose(fp);
X	} while (++optind < argc);
X
X	exit(0);
X/* NOTREACHED */
X}
X
X/*
X * Cut the line. This handles both character and field cutting.
X * For character cutting the f array gives character positions, for
X * fields it gives the field number. It must be indexed by either the
X * character number or the field number.
X */
Xcut(in, f, fs, sup, c_or_f)
Xregister char *in;		/* The input line			*/
Xint f[];			/* The field cutting flags		*/
Xchar fs;			/* The field seperator			*/
Xint sup;			/* Suppress lines with no-delimitor?	*/
Xint c_or_f;			/* Cut by char. (true), or field (false)*/
X{
X	char obuf[MAXLINE];	/* Output buffer			*/
X	register char *optr = obuf;
X	register int i;		/* Character count			*/
X	register int fld;	/* The field count			*/
X	char *instart = in;	/* To print lines with no delimiters	*/
X
X	for (fld = 0, i = 0; i < MAXLINE; i++)
X	{
X		if (*in == '\n')
X		{
X			/* End of the line */
X
X			*optr = '\0';
X			/* Any thing to cut? */
X			if (optr != obuf)
X			{
X				/* Get ride of trailing seperator */
X				if (*(optr - 1) == fs)
X					*(optr - 1) = '\0';
X				puts(obuf);
X			}
X			else if (!sup)
X				printf(instart);
X			return;
X		}
X
X		if (f[c_or_f ? i : fld])
X		{
X			*optr++ = *in;
X		}
X
X		/* End of field? */
X		if (*in++ == fs)
X			fld++;
X	}
X
X	fprintf(stderr, "Line too long, maximum length is %d\n", MAXLINE);
X	exit(1);
X}
X
X/*
X * Parse the list argument. The format is:
X *	n,n
X * where n is either a number or a range of numbers in the format
X *	m-l
X * m or l  may be absent, indicating the start or end of the lines respectivly.
X * Numbers must be in increasing order for m-l format, but not for n,n.
X * Field numbers start at 1, but index into fields array starts at 0.
X * 
X */
Xlist(f, l)
Xint f[];		/* The fields				*/
Xchar *l;		/* The list				*/
X{
X	int range = 0;	/* True if m-l format			 */
X	int low, high;	/* the low and high numbers in a m-l pair*/
X	int i;
X
X	low = high = 0;
X
X	while (1)
X	{
X		switch(*l)
X		{
X		   case '\0':
X			/* Is it m-<nothing>EOL? */
X			if (range)
X			{
X				/* Select rest of fields */
X				for(i = low - 1; i < MAXLINE; i++)
X					f[i]++;
X			}
X			else
X				f[low-1]++;
X			return;
X
X		   case ',':
X			l++;
X			if (!range)
X				f[low-1]++;
X			range = 0;
X			low = 1;
X			break;
X
X		   case '-':
X			l++;
X			range++;
X			/* Is it m-<nothing> */
X			if (isdigit((int)*l))
X			{
X				high = atoi(l);
X				/* Skip the digits */
X				while (isdigit((int) *l))
X					l++;
X			}
X			else
X				high = MAXLINE;
X
X			/* Is the range the correct way around? */
X			if (low > high)
X			{
X				fprintf(stderr, "Bad c/f list: %d > %d\n", 
X							low, high);
X				exit(1);
X			}
X			/* Set the field flags for the range */
X			for(i = low - 1; i < high; i++)
X				f[i]++;
X			break;
X
X		    default:
X			/* either a number or an error */
X			if (!isdigit((int) *l))
X			{
X				fprintf(stderr, "Bad c/f list at %s\n", l);
X				exit(1);
X			}
X			low = atoi(l);
X			if (low == 0)
X			{
X				fprintf(stderr, "Fields start at 1 not 0\n");
X				exit(1);
X			}
X			/* Skip the digits */
X			while (isdigit((int) *l))
X				l++;
X			break;
X		}
X	}
X}
X
Xprusage()
X{
X	fprintf(stderr, "cut [-d<delimitor>] [-s] -c<list>|-f<list> [files]\n");
X	exit(1);
X}
@//E*O*F cut.c//
if test 4941 -ne "`wc -c <'cut.c'`"; then
    echo shar: error transmitting "'cut.c'" '(should have been 4941 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'getopt.c'" '(3739 characters)'
if test -f 'getopt.c' ; then 
  echo shar: will not over-write existing file "'getopt.c'"
else
sed 's/^X//' >getopt.c <<'@//E*O*F getopt.c//'
X/*
X * getopt(), a verson of getopt for those who do not have it and do not
X * want to steal the source from those that do.
X *
X * Same as getopt(3).
X *	Returns the next option letter in argv that matches a letter in optstr.
X *	Options are no longer parsed if the special option '--' is found
X *	or an argument that does not start in '-'.
X *	The global optind is set to the next index in argv to be processed.
X *	The global optarg is set to the argument, if the option has one.
X *	The global opterr can be set true if error messages are to be printed
X *	on the standard error file, or false if no message to be printed.
X *
X * ARGUMENTS:
X *	argc, argv - the argument count, and argument list
X *	optstr     - the list of valid options. The character ":" following
X *		     an option letter indicates that this option must have an 
X *		     argument. For example "abc:". This implies that the -:
X *		     is not  a valid option.
X *
X * RETURNS:
X *	Returns the next option letter, or EOF when all done. If an error
X *	encountered then the character '?' is returned.
X *
X * John Weald
X */
X#include <stdio.h>
X
X/*
X * Index into error array.
X */
X#define BAD_OPT 0	/* option letter not in optstr			*/
X#define MIS_ARG 1	/* option must have an argument			*/
X
Xchar *optarg;
Xint optind = 1;		/* argv[0] is program name			*/
Xint opterr = 1;		/* If true print error message			*/
X
X/*
X *
X * The basic data structures are optind, and the pointer "p."
X * optind keeps track of the next index into argv to parse arguments.
X * p is used to walk along the argv items looking for option letters or
X * arguments, when it is NULL the next  argv must be used . 
X * p is always left pointing to the previous option or NULL.
X * Consider the three equivalent valid argv's:
X *		1        2         3
X *		-a	 -b	   eric
X *	        -ab	eric
X *		-aberic
X * 
X */
X
Xint
Xgetopt(argc, argv, optstr)
Xint argc;
Xchar *argv[];
Xchar *optstr;		/* The list of valid options			*/
X{
X	extern void err();	/* Forward reference			*/
X
X	static char *p = (char *)NULL;
X
X	/*
X	 * parsed all the options in this argv[]?
X	 */
X	if (p == NULL || *++p == '\0' )
X	{
X		if (optind == argc)
X			return(EOF);
X		p = argv[optind];
X
X		/*  a '-' by itself is not an option (e.g. see paste(1)) */
X		if (*p++ != '-' || *p == '\0') 
X			return(EOF);
X
X		/* '--' marks the end of the option list.  */
X		if (*p == '-')
X		{
X			optind++;
X			return(EOF);
X		}
X	}
X
X	optind++;
X	/*
X	 * Look for a valid option 
X	 */
X	while (*p != *optstr)
X	{	
X		if (*optstr == '\0')
X		{
X			/* Reached end of optstr, option not there */
X			err(argv[0], BAD_OPT, *p);
X			return((int)'?');
X		}
X		if (*++optstr == ':')
X			optstr++;
X	}
X
X	/* If it does not need an argument then we are done.  */
X	if (*(optstr + 1) != ':')
X		return((int)*optstr);
X	
X	/*
X	 * If there are more characters in this argv then it must
X	 * be the argument.
X	 */
X	if (*++p != '\0')
X	{
X		optarg = p;
X		p = (char *)NULL;
X		return((int)*optstr);
X	}
X
X	/* 
X	 * It needs an argument, but no more argv's left
X	 */
X	if (optind == argc)
X	{
X		err(argv[0], MIS_ARG, *optstr);
X		p = (char *)NULL;
X		return((int)'?');
X	}
X
X	/*
X	 * Must be in next argv.
X	 */
X	optarg = argv[optind++];
X	p = (char *)NULL;
X	return((int)*optstr);
X}
X
Xstatic void
Xerr(a0, e, c)
Xchar *a0;		/* argv[0]. i.e. the program name		*/
Xint e;
Xchar c;
X{
X#ifdef NO_STDIO
X	static char *errors[] = {
X		": Illegal option -- ",
X		": option requires an argument -- "
X	};
X	static char eend[] = "c\n";
X	
X
X	if (opterr)
X	{
X		(void)write(2, a0, strlen(a0));
X		(void)write(2, errors[e], strlen(errors[e]));
X		eend[0] = c;
X		(void)write(2, eend, strlen(eend));
X	}
X#else
X	static char *errors[] = {
X		"%s: Illegal option -- %c\n",
X		"%s: option requires an argument -- %c\n"
X	};
X
X	if (opterr)
X		fprintf(stderr, errors[e], a0, c);
X#endif
X}
@//E*O*F getopt.c//
if test 3739 -ne "`wc -c <'getopt.c'`"; then
    echo shar: error transmitting "'getopt.c'" '(should have been 3739 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'makefile'" '(241 characters)'
if test -f 'makefile' ; then 
  echo shar: will not over-write existing file "'makefile'"
else
sed 's/^X//' >makefile <<'@//E*O*F makefile//'
Xall: cut paste
X
X
Xpaste: paste.o spaste.o getopt.o
X	cc -o paste paste.o spaste.o getopt.o
X
Xcut: cut.o getopt.o
X	cc -o cut cut.o getopt.o
X
Xlint: lint_cut lint_paste
X
Xlint_cut:
X	lint cut.c getopt.c
X
Xlint_paste:
X	lint paste.c spaste.c getopt.c
X
@//E*O*F makefile//
if test 241 -ne "`wc -c <'makefile'`"; then
    echo shar: error transmitting "'makefile'" '(should have been 241 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'paste.1'" '(2608 characters)'
if test -f 'paste.1' ; then 
  echo shar: will not over-write existing file "'paste.1'"
else
sed 's/^X//' >paste.1 <<'@//E*O*F paste.1//'
X.TH PASTE 1
X.SH NAME
Xpaste \- merge same lines of several files or subsequent lines of one file
X.SH SYNOPSIS
X\f3paste \fPfile1 file2 .\|.\|.
X.br
X\f3paste \-d\fP\|list file1 file2 .\|.\|.
X.br
X\f3paste \-s [\-d\fP\|list\|\f3] \fPfile1 file2 .\|.\|.
X.SH DESCRIPTION
XIn the first two forms,
X.I paste\^
Xconcatenates corresponding lines of the given input
Xfiles
X.IR file1 ,
X.IR file2 ,
Xetc.
XIt treats each file as a column or columns
Xof a table and pastes them together horizontally
X(parallel merging).
XIf you will, it is
Xthe counterpart of
X.IR cat (1)
Xwhich concatenates vertically, i.e.,
Xone file after the other.
XIn the last form above,
X.I paste\^
Xreplaces the function of an older command with the same name
Xby combining subsequent lines of the input file (serial merging).
XIn all cases,
Xlines are glued together with the
X.I tab\^
Xcharacter,
Xor with characters from an optionally specified
X.IR list .
XOutput is to the standard output, so it can be used as
Xthe start of a pipe,
Xor as a filter,
Xif \f3\-\fP is used in place of a file name.
X.PP
XThe meanings of the options are:
X.TP
X.B "\-d"
XWithout this option,
Xthe new-line characters of each but the last file
X(or last line in case of the
X.B \-s
Xoption)
Xare replaced
Xby a
X.I tab\^
Xcharacter.
XThis option allows replacing the
X.I tab\^
Xcharacter by one or more alternate characters (see below).
X.TP
X.I "list\^"
XOne or more characters immediately following
X.B \-d
Xreplace the default
X.I tab\^
Xas the line concatenation character.
XThe list is used circularly, i.e., when exhausted, it is reused.
XIn parallel merging (i.e., no
X.B \-s
Xoption),
Xthe lines from the last file are always terminated with a new-line character,
Xnot from the
X.IR list .
XThe list may contain the special escape sequences:
X.B \en
X(new-line),
X.B \et
X(tab),
X.B \e\e
X(backslash), and
X.B \e0
X(empty string, not a null character).
XQuoting may be necessary, if characters have special meaning to the shell
X(e.g., to get one backslash, use
X.I \-d\|"\e\e\e\e\^"
X).
X.TP
X.B "\-s"
XMerge subsequent lines rather than one from each input file.
XUse
X.I tab\^
Xfor concatenation, unless a
X.I list\^
Xis specified
Xwith
X.B \-d
Xoption.
XRegardless of the
X.IR list ,
Xthe very last character of the file is forced to be a new-line.
X.TP
X.B "\-"
XMay be used in place of any file name,
Xto read a line from the standard input.
X(There is no prompting).
X.SH EXAMPLES
X.TP 15m
Xls \|\(bv\| paste \|\-d" " \|\-
Xlist directory in one column
X.TP
Xls \|\(bv\| paste \|\- \|\- \|\- \|\-
Xlist directory in four columns
X.TP
Xpaste \|\-s \|\-d"\e\|t\e\|n" \|file
Xcombine pairs of lines into lines
X.SH "SEE ALSO"
Xcut(1), grep(1), pr(1).
@//E*O*F paste.1//
if test 2608 -ne "`wc -c <'paste.1'`"; then
    echo shar: error transmitting "'paste.1'" '(should have been 2608 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'paste.c'" '(4780 characters)'
if test -f 'paste.c' ; then 
  echo shar: will not over-write existing file "'paste.c'"
else
sed 's/^X//' >paste.c <<'@//E*O*F paste.c//'
X/*
X * A version of paste. This is compatable with AT&T paste command SVR2.
X *
X * John Weald
X *
X */
X#include <stdio.h>
X
X#define MAXLINE   1024			/* Max. allowed line length	*/
X#define MAXFILES  12			/* Max. number of input files   */
X
Xextern void exit();
X
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X	extern int optind;
X	extern char *optarg;
X
X	int c;				/* For getopt()			*/
X	char conchars[MAXFILES];	/* The concatination characters */
X	int nconchars = 1;		/* The number of conchars[]	*/
X	int serial = 0;			/* True if old type paste "-s"	*/
X
X	conchars[0] = '\t';
X
X	while ((c = getopt(argc, argv, "sd:")) != EOF)
X	{
X		switch(c)
X		{
X		   case 's':
X			/* Concatinate the same file serially */
X			serial++;
X			break;
X
X		   case 'd':
X			/* Use other than a single tab */
X			nconchars = setconcat(conchars, optarg);
X			break;
X
X		   default:
X			/* Does not return */
X			prusage();
X		}
X	}
X
X
X	if (serial)
X		spaste(&argv[optind], conchars, nconchars);
X	else
X		paste(&argv[optind], conchars, nconchars);
X	exit(0);
X/* NOTREACHED */
X}
X
X
X
X/*
X * paste()
X *
X * Do the actual paste.
X */
Xpaste(files, con, ncons)
Xchar *files[];		/* Null terminated list of input files		*/
Xchar con[];		/* The concatination characters			*/
Xchar ncons;		/* The number of above				*/
X{
X	char ibuf[MAXLINE+1];		/* The  input buffer		*/
X	char obuf[MAXLINE];		/* The  output buffer		*/
X	register char *iptr = ibuf;
X	register char *optr = obuf;
X	FILE *fps[MAXFILES];		/* One for each open file 	*/
X	int f;				/* Number of files opened	*/
X	int allfiles;			/* Ditto			*/
X	int inc;			/* True if concat. char. == '\0'*/
X	int ocount;			/* Output buffer char. count	*/
X	char c;				/* The current concat. char	*/
X	int i;
X
X	/*
X	 * Open all the input files, any filename of '-' means
X	 * the standard input. No file name means standard input.
X	 */
X	for (f = 0; files[f] != (char *)NULL; f++)
X	{
X		if (*files[f] == '-')
X			fps[f] = stdin;
X		else if ((fps[f] = fopen(files[f], "r")) == (FILE *)NULL)
X		{
X			fprintf(stderr, "Failed to open file %s\n", files[f]);
X			exit(1);
X		}
X		if (f >= MAXFILES)
X		{
X			fprintf(stderr, 
X			   "Too many files. Maximum allowed is %d\n", MAXFILES);
X			exit(1);
X		}
X	} 
X	if (files[0] == (char *)NULL)
X	{
X		fps[0] = stdin;
X		f++;
X	}
X
X	/*
X	 * Read all lines until no more lines in any file.
X	 */
X	allfiles = f;
X	while (f)
X	{
X		optr = obuf;
X		ocount = 0;
X		/*
X		 * Join lines from all files.
X		 *
X		 * The concatination character may be '\0' which
X		 * means no character. The variable inc is an indication
X		 * of the concatination character being '\0', we need to
X		 * if there is a concatination character to move up the
X		 * output buffer.
X		 *
X		 * The concatination characters are used in a round robin
X		 * list.
X		 */
X
X		for (inc = 0, i = 0; i < allfiles; i++)
X		{
X			iptr = ibuf;
X			optr += inc;
X			/* To save repeated evaluation */
X			c = con[i % ncons];
X			inc = c == '\0' ? 0 : 1;
X
X			if (fps[i] == (FILE *)NULL)
X			{
X				/* No more lines in this file. */
X
X				*optr = c;
X				continue;
X			}
X			if (fgets(ibuf, sizeof(ibuf), fps[i]) == (char *)NULL)
X			{
X				/* Reached EOF - finished with the file */
X				(void)fclose(fps[i]);
X				fps[i] = (FILE *)NULL;
X				*optr = c;
X				f--;
X				continue;
X			}
X
X			/*
X			 * Replace the newline with the concatination character.
X			 * There is no need to look for end-of-string since
X			 * we know that 
X			 * a) if ibuf is full to the max, then we will
X			 *    overflow obuf before we hit the end of ibuf.
X			 * b) if ibuf is not full, then it must contain a
X			 *    a newline character, but may or may not  
X			 *    fit into obuf.
X			 */
X			for (;  *iptr != '\n'; ocount++)
X			{
X				/* Need space for trailing null */
X				if (ocount >= sizeof(obuf) - 1)
X				{
X					fprintf(stderr, "Output line too long, maximum length is %d.\n", MAXLINE);
X					exit(1);
X				}
X				*optr++ = *iptr++;
X			}
X			*optr = c;
X
X		}
X		if (f)
X		{
X			*optr = '\0';
X			puts(obuf);
X		}
X	}
X}
X
X
X/*
X * setconcat()
X *
X * Parse the concatination characters and place them in the array c.
X * Return the number of concatination characters.
X *
X * Specials are:
X *	\n	- Newline
X *	\t	- Tab (default)
X *	\	- Backslash
X *	\0	- No concatination character
X */
Xstatic int
Xsetconcat(c, in)
Xchar *c;
Xchar *in;
X{
X	int i;		/* The number seen so far			*/
X
X	for (i = 0; *in != '\0'; in++, c++, i++)
X	{
X		if (i > MAXFILES)
X		{
X			fprintf(stderr, "Too many concatination characters, maximum allowed is %d\n", MAXFILES);
X			exit(1);
X		}
X		if (*in != '\\')
X		{
X			*c = *in;
X			continue;
X		}
X		in++;
X		switch (*in)
X		{
X		   case 'n':
X			*c = '\n';
X			break;
X		   case 't':
X			*c = '\t';
X			break;
X		   case '0':
X			*c = '\0';
X			break;
X		   default:
X			/* Includes '\\' */
X			*c = *in;
X			break;
X		}
X	}
X	return(i);
X}
X
Xstatic 
Xprusage()
X{
X	fprintf(stderr, "USAGE: paste [-s] [-d<list>] files\n");
X	exit(1);
X}
@//E*O*F paste.c//
if test 4780 -ne "`wc -c <'paste.c'`"; then
    echo shar: error transmitting "'paste.c'" '(should have been 4780 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'spaste.c'" '(2641 characters)'
if test -f 'spaste.c' ; then 
  echo shar: will not over-write existing file "'spaste.c'"
else
sed 's/^X//' >spaste.c <<'@//E*O*F spaste.c//'
X/*
X * Serially paste a file together
X *
X * John Weald
X */
X#include <stdio.h>
X
Xextern void exit();
X
X
Xspaste(files, c, n)
Xchar *files[];		/* Null terminate list of input files		*/
Xchar c[];		/* The concatintaion characters			*/
Xint n;			/* The number of above				*/
X{
X	int i;
X	FILE *fp;
X
X	if (files[0] == (char *)NULL)
X	{
X		spfile(stdin, c, n);
X		return;
X	}
X
X	for (i = 0; files[i] != (char *)NULL; i++)
X	{
X		if (*files[i] == '-')
X			fp = stdin;
X		else if ((fp = fopen(files[i], "r")) == (FILE *)NULL)
X		{
X			fprintf(stderr, "Failed to open file %s\n", files[i]);
X			exit(1);
X		}
X		spfile(fp, c, n);
X		(void)fclose(fp);
X	}
X}
X	
X/* 
X * Do the actual paste of a stream.
X *
X * The method here is to read in the stream and replace all newline
X * characters with concatintaion characters. 
X * Output occurs after each chuck is parsed, or if the concatination character
X * is the null seperator (otherwise puts() would screw up on whole chunk).
X *
X * The stream is read in BUFSIZ chunks using fread. The input buffer is one
X * larger than read, so that it can be null terminated. 
X *
X * When we read in each chunk we must check if it needs to be joined to the
X * previous one i.e. the last character on the last chunk was a newline.
X */
Xstatic
Xspfile(fp, con, ncons)
XFILE *fp;		/* serially paste this stream			*/
Xchar con[];		/* The concatintaion characters			*/
Xint ncons;		/* The number of above				*/
X{
X	char *pstart;	/* The start of the string			*/
X	char *ptr;	/* Walks down the stream			*/
X	char buf[BUFSIZ + 1]; /* To ensure null termination		*/
X	int n;		/* Number of bytes read with fread()		*/
X	int k = 0;	/* Index into concatination character array	*/
X	int join;	/* Join this chunk to the next?			*/
X	char last;	/* The very last character looked at.		*/
X
X
X	join = 0;
X	while ((n = fread(buf, sizeof(char), sizeof(buf) - 1, fp)) != 0)
X	{
X		if (join)
X		{
X			/* Join with last chunk */
X			putchar(con[k]);
X			k = (k + 1) % ncons;
X			join = 0;
X		}
X		/* Join to next chunk? */
X		if (buf[n-1] == '\n')
X		{
X			join++;
X			/* Ignore the newline */
X			n--;
X		}
X
X		/* ensure null terminated buffer */
X		buf[n] = '\0';
X
X		
X		/* 
X	 	 * walk thru this chunk 
X		 * replace all newlines with the next concat. char.
X		 */
X		for (pstart = ptr = buf; *ptr != '\0'; ptr++)
X		{
X			if (*ptr == '\n')
X			{
X				*ptr = con[k];
X				if (con[k] == '\0')
X				{
X					fputs(pstart, stdout);
X					pstart = ptr + 1;
X				}
X				k = (k + 1) % ncons;
X			}
X		}
X		fputs(pstart, stdout);
X
X		last = *(ptr - 1);
X	}
X
X	/*
X	 * Maybe they asked for the newline as the
X	 * concatination char. We would hate to give them two newlines
X	 * in a row.
X	 */
X	if (last != '\n')
X		putchar('\n');
X}
@//E*O*F spaste.c//
if test 2641 -ne "`wc -c <'spaste.c'`"; then
    echo shar: error transmitting "'spaste.c'" '(should have been 2641 characters)'
fi
fi # end of overwriting check
echo shar: "End of shell archive."
exit 0



More information about the Mod.sources mailing list