[net.sources] field revisited

silvert@dalcs.UUCP (Bill Silvert) (07/25/85)

Here is a reposting of Steve Bourne's field program from his book.  I
have made a number of enhancements (mainly a changeable field separator)
and added an expanded version of his manual entry.  Also included are
two sample programs, a primitive implementation of finger for those of
us without the real thing, and a program to check for changes to the
password file (field skips the password itself, so only substantive
changes are noted).

This is similar to cut, found on Ver. III and V, but can be used to
change the order of fields which cut doesn't.  I use field a lot on
tab-separated data files.
-----cut here-----cut here-----cut here-----cut here-----
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	field.1L
#	field.c
#	finger
#	checkp
# This archive created: Wed Jul 24 07:51:37 1985
# By:	Bill Silvert (Marine Ecology Lab, Dartmouth, N. S., Canada)
export PATH; PATH=/bin:$PATH
if test -f 'field.1L'
then
	echo shar: will not over-write existing file "'field.1L'"
else
cat << \SHAR_EOF > 'field.1L'
.TH FIELD 1L
.SH NAME
field \- select fields or columns from a file
.SH SYNOPSIS
.B field 
[-tc]
[-Tc]
[ n ] ...
.SH DESCRIPTION
The
.I field 
command copies selected, delimiter-separated fields from 
the standard input to the standard output. Fields are
numbered from 1 and a field may be requested more than once.
.PP
The default delimiter is a tab.
This can be changed to character
.I c
with the option -t\c
.I c .
If the form -T\c
.I c
is used, the delimiter
.I c
is used on input, but the output is still separated by tabs.
.SH EXAMPLE
The following command extracts the login names, user and group numbers
from the password file:
.PP
field -t: 1 3 4 </etc/passwd
.SH SEE ALSO
.I Cut ,
which is available on System V.
There is a version of
.I cut
on this system, but no manual entry yet.
.SH BUGS
The number of input or output fields may not exceed 256 and
the maximum line length is 4096 characters.
.SH AUTHORS
Taken from "The Unix System" by S. R. Bourne.
Modifications by William Silvert.
SHAR_EOF
fi # end of overwriting check
if test -f 'field.c'
then
	echo shar: will not over-write existing file "'field.c'"
else
cat << \SHAR_EOF > 'field.c'
/* field utility, from Bourne pp.228-9 */
#include <stdio.h>
static char SCCSID[] = "<@(#)field.c 1.11, 85/07/23>";
#define MAXF	256
#define MAXL	4096
#define IFS	'\t'	/* Field separator is tab on input, */
#define OFS	'\t'	/* and on output. */

int fv[MAXF];
int nf;
int mf;
char *fp[MAXF];
char L[MAXL];

main(argc,argv)
	int argc;
	char *argv[];
{
	register char *cp;
	register char **ap;
	register int c;
	int f;
	char opt, fs, ifs=IFS, ofs=OFS;

	while (argc>1) {
		if(sscanf(argv[1], "-%c%c", &opt, &fs) == 2) {
			switch(opt) {
			case 't':	/* change both field separators */
				ofs = fs;
			case 'T':	/* change only ifs */
				ifs = fs;
				break;
			default:
				printf("usage: %s [-tc] [ n ] ...\n", argv[0]);
				return(2);
			}
		}
		else if(sscanf(argv[1], "%d", &fv[nf++]) != 1) {
			printf("usage: %s [-tc] [ n ] ...\n", argv[0]);
			return(2);
		}
		argc--; argv++;
	}

	/* read and copy input */
	nf--;
	cp = L;
	ap = fp;
	*ap++ = cp;
	while(1){
		c = getc(stdin);
		if(c=='\n' || c== EOF) {
			int fc;
			if(cp==L && c==EOF) break;
			*cp++ = 0;
			mf = ap-fp;

			/* print this line */
			for(fc = 0; fc <= nf; fc++){
				putf(fv[fc]-1);
				if(fc != nf) putchar(ofs);
			}
			if(c == EOF) break;
			putchar('\n');
			cp = L;
			ap = fp;
			*ap++ = cp;
		}
		else if(c == ifs) {
			*cp++ = 0;
			*ap++ = cp;
		}
		else *cp++ = c;
	}
	return(0);
}

/* output field n from current line */
putf(n)
{
	register char *cp = fp[n];
	register char c;

	if(n<0 || n>=mf) return;
	while (c = *cp++) putchar(c);
}
SHAR_EOF
fi # end of overwriting check
if test -f 'finger'
then
	echo shar: will not over-write existing file "'finger'"
else
cat << \SHAR_EOF > 'finger'
: Silvert variant of finger -- use -a for all info
FIELD="field -t: 1 3 4 5 6"
case $# in
0)	field -T: 1 5 < /etc/passwd ;;
*)	for i
	do	case $i in
		-a)	FIELD="cat -v" ;;
		-*)	echo "$0: unknown flag $i" ; exit ;;
		*)	grep "^$i:" /etc/passwd | $FIELD ;;
		esac
	done ;;
esac
SHAR_EOF
chmod +x 'finger'
fi # end of overwriting check
if test -f 'checkp'
then
	echo shar: will not over-write existing file "'checkp'"
else
cat << \SHAR_EOF > 'checkp'
#! /bin/sh
# check /etc/passwd for changes other than new passwords
PATH=/usr/new:/usr/local:/usr/bin:/bin
PASSWD=/etc/passwd
SECURE=/usr/adm/security
STOP=$SECURE/passwd
TEMP=/tmp/pass$$
DIFF=/tmp/diff$$
trap '/bin/rm -f $TEMP $DIFF;exit' 0 1 2 3 15
< $PASSWD field -t: 1 3 4 5 6 7 > $TEMP
diff $TEMP $STOP > $DIFF
if	test -s $DIFF
then	echo "Subject: Changes to /etc/passwd" > $TEMP
	cat $TEMP $DIFF | mail root
	exit 1
else
	exit 0
fi
SHAR_EOF
chmod +x 'checkp'
fi # end of overwriting check
#	End of shell archive
exit 0
-- 
Bill Silvert
Marine Ecology Lab.
Dartmouth, NS
dalcs!silvert
dalcs!biomel!bill