/*
 * |-----------------------------------------------------------|
 * | Copyright (c) 1991, 1990 MIPS Computer Systems, Inc.      |
 * | All Rights Reserved                                       |
 * |-----------------------------------------------------------|
 * |          Restricted Rights Legend                         |
 * | Use, duplication, or disclosure by the Government is      |
 * | subject to restrictions as set forth in                   |
 * | subparagraph (c)(1)(ii) of the Rights in Technical        |
 * | Data and Computer Software Clause of DFARS 252.227-7013.  |
 * |         MIPS Computer Systems, Inc.                       |
 * |         950 DeGuigne Avenue                               |
 * |         Sunnyvale, California 94088-3650, USA             |
 * |-----------------------------------------------------------|
 */
#ident	"$Header: strings.c,v 1.10.1.1 92/06/24 13:55:22 marker Exp $"

/*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#ifndef lint
char copyright[] =
"@(#) Copyright (c) 1980 Regents of the University of California.\n\
 All rights reserved.\n";
#endif not lint

#ifndef lint
static char sccsid[] = "@(#)strings.c	5.3 (Berkeley) 12/3/86";
#endif not lint

#include <sys/types.h>
#include <sys/file.h>
#include <a.out.h>
#include <stdio.h>
#include <ctype.h>
#include <locale.h>
#include <widec.h>
#include <wctype.h>
#include "elf_abi.h"

#define DEF_LEN		4		/* default minimum string length */
#define EOS		(char)NULL	/* end of string */
#define ERR		-1		/* general error */
#define ERREXIT		1		/* error exit */
#define NO		0		/* false/no */
#define OK		0		/* ok exit */
#define YES		1		/* true/yes */

#define ISSTR(ch)	(iswprint(ch) || ch == '\t')

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

static long	foff;			/* offset in the file */
static int	head_len,		/* length of header */
		read_len;		/* length to read */
#if mips
static u_char	hbfr[FILHSZ+AOUTHSZ];	/* buffer for filehdr, aouthdr */
FILHDR		*filhdr = (FILHDR *) hbfr;
AOUTHDR		*aouthdr = (AOUTHDR *) &hbfr[FILHSZ]; 
#else
static u_char	hbfr[sizeof(EXEC)];	/* buffer for struct exec */
#endif

int	minlen = DEF_LEN;		/* minimum string length */
short oflg;					/* print octal location */
wchar_t ch;					/* character */
int	cnt;					/* general counter */
wchar_t *C;					/* bfr pointer */
wchar_t	*bfr;				/* collection buffer */

static int end;
extern char *gettxt();

main(argc,argv)
int	argc;
char	**argv;
{
#ifndef	mips
	EXEC	*head;			/* exec header pointer */
#endif
	short	asdata = NO;		/* look in everything */
	char	*file,			/* file name for error */
		*malloc();
	static wchar_t	getch();

	setlocale(LC_ALL, "");
	/*
	 * for backward compatibility, allow '-' to specify 'a' flag; no
	 * longer documented in the man page or usage string.
	 */
	for (++argv;*argv && **argv ==  '-';++argv) {
		for (cnt = 1;(*argv)[cnt];++cnt)
			switch ((*argv)[cnt]) {
				case 'a':
					asdata = YES;
					break;
				case 'o':
					oflg = YES;
					break;
				default:	/* getopt message compatible */
					if (!isdigit((*argv)[cnt])) {
						fprintf(stderr,gettxt("strings:1", "strings: illegal option -- %c\nusage: strings [-ao] [-#] [file ... ]\n"),(*argv)[cnt]);
						exit(ERREXIT);
					}
					minlen = atoi(*argv + 1);
					break;
			}
		if (cnt == 1)
			asdata = YES;
	}

	if (!(bfr = (wchar_t *)malloc((u_int)minlen))) {
		fputs(gettxt("strings:2", "strings: unable to allocate space.\n"),stderr);
		exit(ERREXIT);
	}
	bfr[minlen] = EOS;
	file = "stdin";
	do {
		if (*argv) {
			if (!freopen(*argv,"r",stdin)) {
				perror(*argv);
				exit(ERREXIT);
			}
			file = *argv++;
		}
		foff = 0;
		read_len = ERR;
		if (asdata) {
/*
			if (fseek(stdin, 0, 0) == ERR) {
				perror(file);
				exit(ERREXIT);
			}
*/
			process();
		}
		else {
#if mips
			/* check for good header */
			if ((head_len = read(fileno(stdin),(char *)filhdr, 
				 FILHSZ)) == ERR) {
				perror(file);
				exit(ERREXIT);
			}

			if ISCOFF(filhdr->f_magic) {	/* process COFF format */
				if ((head_len += read(fileno(stdin),(char *)aouthdr,
				     AOUTHSZ)) == ERR) {
					perror(file);
					exit(ERREXIT);
				}
				if (head_len == FILHSZ+AOUTHSZ && !N_BADMAG(*aouthdr)){
					foff = N_TXTOFF(*filhdr, *aouthdr) + aouthdr->tsize;
					if (fseek(stdin,foff,L_SET) == ERR) {
						perror(file);
						exit(ERREXIT);
					}
					read_len = aouthdr->dsize;
					head_len = 0;
					process();
				} else {
				    if (fseek(stdin, 0, 0) == ERR) {
					   perror(file);
					   exit(ERREXIT);
					}
				}
			} else {	/* process ELF format */
				Elf32_Ehdr	ehdr;

				fseek(stdin, 0, 0);
				if (read(fileno(stdin), &ehdr, sizeof(ehdr)) != sizeof(ehdr)) {
					perror(file);
					exit(ERREXIT);
				}
			
				if (IS_ELF(ehdr)) {
					Elf32_Shdr secthdr;
					unsigned foff = ehdr.e_shoff; /* section hdr tbl offset */
					char *strtab;
					int s_num;

					/* get strings table header */
			        fseek(stdin,
						  foff+(ehdr.e_shstrndx*ehdr.e_shentsize),0);
        			if (read(fileno(stdin),
							 &secthdr,sizeof(secthdr)) != sizeof(secthdr)) {
          				perror(file);
          				exit(ERREXIT);
        			}

					/* read string table section */
			        strtab = malloc(secthdr.sh_size);
        			fseek(stdin,secthdr.sh_offset,0);
        			if (read(fileno(stdin),
							 strtab,secthdr.sh_size) != secthdr.sh_size) {
          				perror(file);
          				exit(ERREXIT);
        			}

					/* for each section in the section header table, 
					 * read the section header and determine the section name.
					 * process .data, and .rodata sections
					 */ 
					for (s_num = 0; s_num < ehdr.e_shnum; s_num = ++s_num) {
						fseek(stdin, foff, 0);
						/* read section header */
			        	if (read(fileno(stdin),&secthdr,sizeof(secthdr)) != 
							sizeof(secthdr)) {
							perror(file);
	          				exit(1);
	        			}
						/* check name of section */
						if ((strcmp(strtab+secthdr.sh_name,ELF_DATA) == 0) ||
							(strcmp(strtab+secthdr.sh_name,ELF_RODATA) == 0)) {
							read_len = secthdr.sh_size;
							head_len = 0;
							if (fseek(stdin, secthdr.sh_offset, L_SET) == ERR) {
								perror(file);
								exit(ERREXIT);
							} /* if */
							/* process section */
							process();
						} /* if */
						/* update pointer to next header */
						foff += ehdr.e_shentsize;
                    } /* for */
				} else {
					/* If not COFF or ELF, do whole file */
					if (fseek(stdin, 0, 0) == ERR) {
						perror(file);
						exit(ERREXIT);
					}
					process();
				}  /* if (IS_ELF(ehdr)) */
			}

#else
			head = (EXEC *)hbfr;
			if ((head_len = 
			 	 read(fileno(stdin),(char *)head,sizeof(EXEC))) == ERR) {
				perror(file);
				exit(ERREXIT);
			}
			if (head_len == sizeof(EXEC) && !N_BADMAG(*head)) {
				foff = N_TXTOFF(*head) + head->a_text;
				if (fseek(stdin,foff,L_SET) == ERR) {
					perror(file);
					exit(ERREXIT);
				}
				read_len = head->a_data;
				head_len = 0;
				process();
			} else {
			   	if (fseek(stdin, 0, 0) == ERR) {
			    	perror(file);
			    	exit(ERREXIT);
				}
			}
#endif
		}
	} while (*argv);
	exit(OK);
} /* main */


/* 
 * process --
 *	find strings in section
 */
process()
{
	register long cnt;

		for (cnt = 0, end = 0; !end; ) {
		    ch = getch(); 
		    if (end)
			break;
			if (ISSTR(ch)) {
				if (!cnt)
					C = bfr;
				*C++ = ch;
				if (++cnt < minlen)
					continue;
				if (oflg)
					printf("%07ld %ws",foff - minlen,bfr);
				else
					fputws(bfr,stdout);

				while (1) {
					ch = getch();
					if (end || !ISSTR(ch))
					    break;
					putwchar(ch);
				}

				putchar('\n');
			}
			cnt = 0;
		}
}


/*
 * getch --
 *	get next character from wherever
 */
static wchar_t
getch()
{
	wchar_t wc;

	if (read_len != ERR && read_len-- == 0) {
	    end = 1;
	    return;
	}
	wc = getwchar();
	if (feof(stdin) || ferror(stdin)) {
	    end = 1;
	    return;
	}
	foff = ftell(stdin);
	return(wc);
}
