/*
 *		scat file...
 *
 * Copy files to stdout.  Note: the program first looks for all
 * files, sorting the file names in ascending alphabetic order
 * The files are then output in that order.
 *
 * In sorting the files, the information is ordered:
 *
 *	file_name	disk[directory]
 *
 */

/*)BUILD	$(TKBOPTIONS) = {
			TASK	= ...CAT
		}
*/

#ifdef	DOCUMENTATION

title	scat	Concatenate Files
index		Concatenate Files

synopsis

	scat file_list

description

	Scat performs a wild-card lookup on all files in the argument list.
	It sorts these on ascending file name, copying them, in that
	order, to stdout.

	Scat removes trailing blanks, tabs, and other control characters
	from the end of each line.

diagnostics

	.lm +8
	.s.i -8;Unknown switch - ...
	.s.i -8;Illegal file name ...
	.s.i -8;Can't reopen file ...
	.s.i -8;Bad file name ...
	.s.i -8;Duplicate file name ...
	.s.i -8;Too many files ...
	.s
	After wild-card expansion, the file name buffer filled.
	.s.i -8;No room
	.s
	The program ran out of memory for the file name strings.
	.lm -8

author

	Martin Minow

bugs

	All files must be on the current network node.

#endif

#include <stdio.h>
#define	FALSE		0
#define	TRUE		1
#define	EOS		0
#define	MAX_NAMES	500		/* Max. file names		*/
static int	debug;
static char	filename[81];		/* Just file name		*/
static char	fullname[81];		/* dev:[...] + filename		*/
static char	dirname[81];		/* Just dev:[...]		*/
static char	work[81];		/* Work buffer for file names	*/
static char	record[512];		/* Work buffer for file copy	*/
static char	*wild_spec;		/* Current wild card spec	*/
static char	*text[MAX_NAMES];	/* store file names here	*/
static char	**ttop = text;		/* Top of text pointer		*/
static FILE	*infd;
static int	isarg = FALSE;

main(argc, argv)
int		argc;
char		*argv[];
{

	register int		c;
	register int		i;
	register char		*ap;

	for (i = 1; i < argc; i++) {
		if (*(ap = argv[i]) == '-') {
			while ((c = tolower(*++ap)) != 0) {
				switch (c) {
				case 'd':
					debug++;
					break;

				default:
					bug("W", "Unknown switch", ap);
					break;
				}
			}
		}
		else {
			wild_spec = argv[i];
			ttop = text;
			isarg = TRUE;
			getnames();
		}
	}
	if (isarg) {
		copyall();
	}
	else {
		copyfile(stdin);
	}
}

getnames()
/*
 * Find all files for wild_spec
 */
{
	register int		count;		/* Count files		*/
	register char		*wp;		/* -> wild_spec		*/
	register char		*dp;		/* -> dirname[]		*/

	wp = wild_spec;
	dp = dirname;
	do {
		*dp++ = (count = *wp++);
	} while (count != 0 && count != ':'
			&& count != '[' && count != '(');
	if (count == 0) {
		dp = dirname;
	}
	else if (count == '[' || count == '(') {
		do {
			*dp++ = (count = *wp++);
		} while (count != 0 && count != ']' && count != ')');
	}
	*dp = 0;
	if (debug)
		fprintf(stderr, "wild spec = \"%s\", dirname = \"%s\"\n",
				wild_spec, dirname);
	if ((infd = fwild(wild_spec, "r")) == NULL) {
		bug("W", "Can't open", wild_spec);
		return(0);
	}
	for (count = 0; fnext(infd) != NULL; count++) {
		setname(infd);
		save();
		if (debug)
			fprintf(stderr, "Saved file = \"%s\"\n", fullname);
	}
	if (debug)
		fprintf(stderr, "%d files processed\n\n", count);
	if (count == 0)
		bug("W", "No files matched", wild_spec);
	return(count);
}

copyall()
/*
 * Copy all files
 */
{
	char			**textp;	/* Pointer to names	*/
	register char		*tp;		/* Pointer to a name	*/
	register char		*np;		/* Filename pointer	*/
	register int		c;		/* Current character	*/

	for (textp = text; textp < ttop; textp++) {
		/*
		 * Get directory part
		 */
		for (tp = *textp; (c = *tp++) != 0 && c != '\t';);
		if (c == 0)
			error("Illegal file name \"%s\"\n", filename);
		np = cpystr(fullname, tp);
		/*
		 * Copy in name part
		 */
		for (tp = *textp; (c = *tp++) != 0 && c != '\t';)
			*np++ = c;
		*np = 0;
		if ((infd = fopen(fullname, "r")) == NULL)
			bug("W", "Can't reopen", np);
		else {
			copyfile(infd);
			fclose(infd);
		}
	}
}

copyfile(fd)
register FILE	*fd;
/*
 * Copy loop
 */
{
	register char		*rp;

	/*
	 * Note -- scat cleans up the end of the logical record
	 */
	while (fgets(record, sizeof record, fd) != NULL) {
		rp = &record[strlen(record) - 1];
		while (rp >= record && *rp <= ' ')
			rp--;
		rp[1] = EOS;
		fputss(record, stdout);
	}
}

setname(fd)
FILE 		*fd;
/*
 * Build file name
 */
{
	register char		*wp;
	register char		*np;
	register int		c;

	fgetname(fd, work);
	/*
	 * Skip over device name, if any
	 */
	for (wp = work; (c = *wp++) && c != ':';);
	if (c == 0)
		wp = work;
	/*
	 * Skip over [UIC] or [PPN] if present
	 */
	if (*wp == '[' || *wp == '(') {
		while ((c = *wp++) && c != ']' && c != ')')
		if (c == 0)
			error("Bad file name \"%s\"\n", work);
	}
	/*
	 * Wp now points to the first byte of the file name.
	 */
	if (debug)
		fprintf(stderr, "Setname, file name = \"%s\"\n", wp);
	cpystr(fullname, wp);
	/*
	 * Don't include version in sort argument, then append
	 * directory name.  Result is:
	 *	foo.bar<TAB>db0:[10,20]<NULL>
	 */
	for (wp = fullname; (c = *wp) && c != ';'; wp++);
	*wp++ = '\t';
	cpystr(wp, dirname);
}

save()
/*
 * Save fullname, add it to the text area (in sorted order)
 */
{
	register char	**textp;
	register char	**insert;
	register int	i;

	for (textp = text; textp < ttop; textp++) {
		if ((i = strcmp(*textp, fullname)) == 0) {
			fprintf("?SCAT-E-Duplicate file name \"%s\"\n",
				fullname);
			return;
		}
		else if (i > 0)
			break;
	}
	insert = textp;
	textp = ttop;
	if (++ttop >= &text[MAX_NAMES])
		error("?SCAT-F-Too many file names, %d max.\n", MAX_NAMES);

	while (textp >= insert) {
		textp[1] = *textp;
		textp--;
	}
	if ((*insert = malloc(strlen(fullname) + 1)) == NULL)
		error("?SCAT-F-No room for \"%s\"\n", fullname);
	cpystr(*insert, fullname);
}
	


usage(s)
char	*s;
{
	bug("E", s, NULL);
	exit(1);
}

bug(severity, mess, arg)
char		*severity;
char		*mess;
char		*arg;
/*
 * Error messages
 */
{
	fprintf(stderr, "?SCAT-%s-%s", severity, mess);
	if (arg != NULL)
		fprintf(stderr, ": \"%s\"", arg);
	fprintf(stderr, "\n");
	if (*severity != 'W')
		error("?SCAT-F-Can't continue");
}
