/*	$OpenBSD: strings.c,v 1.15 2009/10/27 23:59:43 deraadt Exp $	*/
/*	$NetBSD: strings.c,v 1.7 1995/02/15 15:49:19 jtc Exp $	*/

/*
 * Copyright (c) 1980, 1987, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <sys/types.h>

#include <a.out.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <locale.h>
#include <unistd.h>
#include <err.h>

#define FORMAT_DEC "%07ld "
#define FORMAT_OCT "%07lo "
#define FORMAT_HEX "%07lx "

#define DEF_LEN		4		/* default minimum string length */
#define ISSTR(ch)	(isascii(ch) && (isprint(ch) || ch == '\t'))

typedef struct exec	EXEC;		/* struct exec cast */

static long	foff;			/* offset in the file */
static int	hcnt,			/* head count */
		head_len,		/* length of header */
		read_len;		/* length to read */
static u_char	hbfr[sizeof(EXEC)];	/* buffer for struct exec */

static void usage(void);
int getch(void);

int
main(int argc, char *argv[])
{
	extern char *optarg;
	extern int optind;
	int ch, cnt;
	u_char *C;
	EXEC *head;
	int exitcode, minlen, maxlen, bfrlen;
	short asdata, fflg;
	u_char *bfr;
	char *file, *p;
	char *offset_format;

	setlocale(LC_ALL, "");

	/*
	 * for backward compatibility, allow '-' to specify 'a' flag; no
	 * longer documented in the man page or usage string.
	 */
	asdata = exitcode = fflg = 0;
	offset_format = NULL;
	minlen = -1;
	maxlen = -1;
	while ((ch = getopt(argc, argv, "0123456789an:m:oft:-")) != -1)
		switch((char)ch) {
		case '0': case '1': case '2': case '3': case '4':
		case '5': case '6': case '7': case '8': case '9':
			/*
			 * kludge: strings was originally designed to take
			 * a number after a dash.
			 */
			if (minlen == -1) {
				p = argv[optind - 1];
				if (p[0] == '-' && p[1] == ch && !p[2])
					minlen = atoi(++p);
				else
					minlen = atoi(argv[optind] + 1);
			}
			break;
		case '-':
		case 'a':
			asdata = 1;
			break;
		case 'f':
			fflg = 1;
			break;
		case 'n':
			minlen = atoi(optarg);
			break;
		case 'm':
			maxlen = atoi(optarg);
			break;
		case 'o':
			offset_format = FORMAT_OCT;
			break;
		case 't':
			switch (*optarg) {
			case 'o':
			        offset_format = FORMAT_OCT;
				break;
			case 'd':
				offset_format = FORMAT_DEC;
				break;
			case 'x':
				offset_format = FORMAT_HEX;
				break;
			default:
				usage();
				/* NOTREACHED */
			}
			break;
		case '?':
		default:
			usage();
		}
	argc -= optind;
	argv += optind;

	if (minlen == -1)
		minlen = DEF_LEN;
	else if (minlen < 1)
		errx(1, "length less than 1");
	if (maxlen != -1 && maxlen < minlen)
		errx(1, "max length less than min");
	bfrlen = maxlen == -1 ? minlen : maxlen;
	bfr = malloc(bfrlen + 1);
	if (!bfr)
		err(1, "malloc");
	bfr[bfrlen] = '\0';
	file = "stdin";
	do {
		if (*argv) {
			file = *argv++;
			if (!freopen(file, "r", stdin)) {
				warn("%s", file);
				exitcode = 1;
				goto nextfile;
			}
		}
		foff = 0;
#define DO_EVERYTHING()		{read_len = -1; head_len = 0; goto start;}
		read_len = -1;
		if (asdata)
			DO_EVERYTHING()
		else {
			head = (EXEC *)hbfr;
			if ((head_len =
			    read(fileno(stdin), head, sizeof(EXEC))) == -1)
				DO_EVERYTHING()
			if (head_len == sizeof(EXEC) && !N_BADMAG(*head)) {
				foff = N_TXTOFF(*head);
				if (fseek(stdin, foff, SEEK_SET) == -1)
					DO_EVERYTHING()
				read_len = head->a_text + head->a_data;
				head_len = 0;
			}
			else
				hcnt = 0;
		}
start:
		for (cnt = 0, C = bfr; (ch = getch()) != EOF;) {
			if (ISSTR(ch)) {
				*C++ = ch;
				if (++cnt < minlen)
					continue;
				if (maxlen != -1) {
					while ((ch = getch()) != EOF &&
					       ISSTR(ch) && cnt++ < maxlen)
						*C++ = ch;
					if (ch == EOF ||
					    (ch != 0 && ch != '\n')) {
						/* get all of too big string */
						while ((ch = getch()) != EOF &&
						       ISSTR(ch))
							;
						ungetc(ch, stdin);
						goto out;
					}
					*C = 0;
				}

				if (fflg)
					printf("%s:", file);

				if (offset_format) 
					printf(offset_format, foff - minlen);

				printf("%s", bfr);
				
				if (maxlen == -1)
					while ((ch = getch()) != EOF &&
					       ISSTR(ch))
						putchar((char)ch);
				putchar('\n');
			out:
				;
			}
			cnt = 0;
			C = bfr;
		}
nextfile: ;
	} while (*argv);
	exit(exitcode);
}

/*
 * getch --
 *	get next character from wherever
 */
int
getch(void)
{
	++foff;
	if (head_len) {
		if (hcnt < head_len)
			return((int)hbfr[hcnt++]);
		head_len = 0;
	}
	if (read_len == -1 || read_len-- > 0)
		return(getchar());
	return(EOF);
}

static void
usage(void)
{
	(void)fprintf(stderr,
	    "usage: strings [-afo] [-m number] [-n number] [-t radix] [file ...]\n");
	exit(1);
}