Metropoli BBS
VIEWER: tfb.c MODE: TEXT (ASCII)
/*
 * A TSR file browser
 *
 * Copyright 1993-1995 Dave Dunfield
 * All rights reserved.
 *
 * Permission granted for personal (non-commercial) use only.
 *
 * Compile command: cc tfb -fop
 */
#include <stdio.h>
#include <window.h>
#include <tsr.h>

#define SCREEN_SIZE	80		/* Width of screen */
#define	LINE_SIZE	250		/* Maximum width of input line */
#define	NUM_TAGS	10		/* Maximum number of tag locations */
#define	NUM_FILES	10		/* Maximum number of pick files */

int tab_size = 8, line, lcount, offset, tag, file, fnext = 0,
	file_info[NUM_FILES][NUM_TAGS+4], Msel, Mval[NUM_FILES];

unsigned sizeh, sizel, maxline, lines[2048][2], tags[NUM_TAGS];

char filename[NUM_FILES][51], search_string[NUM_FILES][51], *Mptr[NUM_FILES+1],
	*argptr = 0;

struct WINDOW *mwin;

FILE *fp = 0;

/* Window video attributes */
int attrs[] = {
	WSAVE|WCOPEN|0x70,				/* 0: Message window */
	WSAVE|WCOPEN|0x07,				/* 1: Main screen */
	WSAVE|WBOX1|WCOPEN|0x70			/* 2: Info entry */
	};

/* Form for entering filename */
char *form1[] = {
	64<<8|3,
	"\x00\x00\x32Filename:",
	0 };

/* Form for setting tab size */
char *form2[] = {
	30<<8|3,
	"\x00\x00\x85Tab size:",
	0 };

/* Form for selecting line number */
char *form3[] = {
	30<<8|3,
	"\x00\x00\x85Line number:",
	0 };

/* Form for string searches */
char *form4[] = {
	64<<8|3,
	"\x00\x00\x32Search for:",
	0 };

/* Welcome message */
char hello[] =
	"TFB [/ACLRS] [file] - Copyright 1993-1995 Dave Dunfield. All rights reserved.";
/*
 * Main file browser program
 */
browser()
{
	int i, x;
	char buffer[LINE_SIZE+1];

	mwin = wopen(0, 0, 80, 1, attrs[0]);
	wputs(hello);
	wopen(0, 1, 80, 24, attrs[1]);
	wcursor_off();

	/* If file already active... reopen */
	if(fnext) {
		if(!(fp = fopen(filename[file], "rb")))
			fnext = 0; }

	/* If no file open... prompt for one. */
	if(!fnext) {
		*buffer = 0;
		do {
			if(argptr) {
				strcpy(buffer, argptr);
				argptr = 0; }
			else if(wform(7, 10, attrs[2], form1, buffer))
				*buffer = 0;
			if(!*buffer) {
				wclose();
				wclose();
				return; } }
		while(!open_file(buffer));
		strcpy(filename[file = fnext++], buffer); }

/* Display screen from file */
display:
	if(line > (lcount - 23)) line = lcount - 23;
	if(line < 0) line = 0;
	if(offset < 0) offset = 0;
	update_status();
	gotoline(line);
	for(i=0; i < 24; ++i) {
		wgotoxy(0, i);
		if(fgets(buffer, sizeof(buffer)-1, fp))
			display_line(buffer);
		if((line + i) == lcount) {
			*W_OPEN = 0x70;
			wputs(" End of file ");
			*W_OPEN = attrs[1];
			wcleow();
			break; } }

/* Get and process command character */
command:
	switch(x = wgetc()) {
		case _KUA :			/* Backup one line */
			--line;
			goto display;
		case _KDA :			/* Advance one line */
			++line;
			goto display;
		case _KPU :			/* Backup one page */
			line -= 23;
			goto display;
		case _KPD :			/* Advance one page */
			line += 23;
			goto display;
		case _CPU :			/* Go to start of file */
			line = 0;
			goto display;
		case _CPD :			/* Go to end of file */
			line = 32767;
			goto display;
		case _KRA :			/* Advance one column */
			++offset;
			goto display;
		case _KLA :			/* Backup one column */
			--offset;
			goto display;
		case _KEN :			/* Advance 20 columns */
			offset += 20;
			goto display;
		case _KHO :			/* Backup 20 columns */
			offset -= 20;
			goto display;
		case _CHO :			/* Reset to 1st column */
			offset = 0;
			goto display;
		case _K1 :			/* Display Fkey help */
			w_clwin(mwin);
			w_puts("1:Help 2:Goto 3:Search 4:Again 5:Gotag 6:Setag 7:Tabsize 8:Colors 9:File 10:Pick", mwin);
			goto command;
		case _K2 :			/* Goto line number */
			x = line+1;
			if(!wform(20, 10, attrs[2], form3, &x))
				line = x - 1;
			goto display;
		case _K3 :			/* Initial Search */
			if(!wform(7, 10, attrs[2], form4, search_string[file])
				&& search(search_string[file], line))
				goto display;
			goto command;
		case _K4 :			/* Repeat Search */
			if(search(search_string[file], line+1))
				goto display;
			goto command;
		case _K5 :			/* Move to tag */
			if((!tag_menu(15, 8)) && tags[tag])
				line = tags[tag] - 1;
			goto display;
		case _K6 :			/* Set tag */
			if(!tag_menu(55, 8))
				tags[tag] = line + 1;
			goto command;
		case _K7 :			/* Set TAB size */
			x = tab_size;
			if(!wform(20, 10, attrs[2], form2, &x))
				tab_size = x ? x : 1;
			goto display;
		case _K8 :
			colors();
			goto display;
		case _K9 :			/* Browse another file */
			*buffer = 0;
			if(!wform(7, 10, attrs[2], form1, buffer)) {
				save_info();
				if(open_file(buffer)) {
					strcpy(filename[file = fnext++ % NUM_FILES], buffer);
					goto display; } }
			goto command;
		case _K10 :			/* Pick a previously opened file */
			for(i=Msel=x=0; i < NUM_FILES; ++i) {
				if(*filename[i]) {
					if(i == file)
						Msel = x;
					Mptr[x] = filename[i];
					Mval[x++] = i; } }
			Mptr[x] = 0;
			if(!wmenu(15, 8, attrs[2], Mptr, &Msel)) {
				save_info();
				if(open_file(filename[Msel])) {
					file = Msel;
					restore_info();
					goto display; } }
			goto command;
		case 0x1B :			/* exit */
			save_info();
			fclose(fp);
			wclose();
			wclose();
			return; }

/* Command key not recognized... */
	wputc(7);
	goto command;
}

/*
 * Open and index a file
 */
open_file(char *name)
{
	int c;

	/* Attempt to open the file - report if failure */
	if(!(c = fopen(name, "rb"))) {
		w_clwin(mwin);
		w_printf(mwin,"Unable to access: '%s'", name);
		return 0; }

	w_clwin(mwin);
	w_printf(mwin,"Indexing '%s'...", name);

	/* Close previously open file (if any) */
	if(fp)
		fclose(fp);
	fp = c;

	/* Reset viewer file control variables */
	sizeh = sizel = maxline = lcount = line = offset = tag = 0;
	for(c=0; c < NUM_TAGS; ++c)
		tags[c] = 0;

	/* Build table of line numbers .vs. file offsets */
	while((c = getc(fp)) != EOF) {
		if(!++sizel)
			++sizeh;
		if(c == '\n') {
		/*	if(!++sizel)
				++sizeh; */
			if(!(++lcount & 0x1F)) {
				lines[maxline][0] = sizel;
				lines[maxline++][1] = sizeh; } } }

	return -1;
}

/*
 * Display status in the message window
 */
update_status()
{
	w_gotoxy(0, 0, mwin);
	w_printf(mwin,"%-50s Line: %u of %u, Col: %u", filename[file], line+1, lcount, offset+1);
	w_cleow(mwin);
}

/*
 * Position file to line 'n'
 */
gotoline(unsigned line)
{
	int c, i;

	rewind(fp);

	/* If more than 32 lines... seek to it */
	if(i = line >> 5) {
		--i;
		fseek(fp, lines[i][1], lines[i][0], 0); }

	/* Read till we get to exact line */
	i = line & 0x1F;
	while(i) {
		if((c = getc(fp)) == EOF)
			return -1;
		if(c == '\n')
			--i; }

	return 0;
}

/*
 * Display a line with tabs expanded
 */
display_line(char *text)
{
	int p, o;
	unsigned char c;

	o = offset + SCREEN_SIZE;
	p = 0;
	while((c = *text++) && (p < o)) {
		if(c == '\t') {				/* tab */
			do
				if(p >= offset)
					wputc(' ');
			while(++p % tab_size); }
		else if(c != '\r') {						/* not a tab */
			if(++p > offset) {
				if(c < ' ') {		/* Control character */
					*W_OPEN = 0x70;
					wputc(c + 0x40);
					*W_OPEN = attrs[1];
					continue; }
				wputc((c <= '~') ? c : 0xFE); } } }
	if(p < o)
		wcleol();
}

/*
 * Search for string in file
 */
search(char *string, int l)
{
	char buffer[LINE_SIZE+1];

	w_clwin(mwin);
	w_printf(mwin, "Searching from line %u for '%s'... ", l+1, string);
	gotoline(l);
	while(fgets(buffer, sizeof(buffer)-1, fp)) {
		if(inline(buffer, string)) {
			line = l;
			return -1; }
		++l; }
	w_printf(mwin, "Not found!", string);
	return 0;
}

/*
 * Test for string occuring within a line.
 * We could easily do this in 'C', however its a good chance to show
 * off inline assembly language, and get a slight speed improvement.
 */
inline(line, string) asm
{
		MOV		SI,6[BP]		; Get line
inl1:	MOV		DI,4[BP]		; Get string
inl2:	MOV		AL,[DI]			; Get char from string
		MOV		AH,[SI]			; Get char from line
		AND		AL,AL			; End of string?
		JZ		inl3			; Yes, we have match
		INC		SI				; Advance line
		INC		DI				; Advance string
		CMP		AL,AH			; *line == *string?
		JZ		inl2			; Yes, keep looking
		AND		AH,AH			; End of string?
		JNZ		inl1			; No, keep trying
; End of line... string was not found
		XOR		AX,AX			; 0 = Not found
		JMP SHORT inl4			; And exit
; Found string
inl3:	MOV		AX,-1			; 1 = Success
inl4:
}

/*
 * Save viewer information for current file.
 */
save_info()
{
	int i, *ptr;
	ptr = file_info[file];
	*ptr = line;
	*++ptr = offset;
	*++ptr = tab_size;
	*++ptr = tag;
	for(i=0; i < NUM_TAGS; ++i)
		*++ptr = tags[i];
}

/*
 * Reload viewer information for current file.
 */
restore_info()
{
	int i, *ptr;
	ptr = file_info[file];
	line = *ptr;
	offset = *++ptr;
	tab_size = *++ptr;
	tag = *++ptr;
	for(i=0; i < NUM_TAGS; ++i)
		tags[i] = *++ptr;
}

/*
 * Build and perform a menu of tag line numbers.
 */
tag_menu(int x, int y)
{
	int i;
	char *tagnames[NUM_TAGS+1], text[NUM_TAGS][6];
	for(i=0; i < NUM_TAGS; ++i) {
		if(tags[i])
			sprintf(tagnames[i] = text[i], "%u", tags[i]);
		else
			tagnames[i] = "-----"; }
	tagnames[NUM_TAGS] = 0;
	return wmenu(x, y, attrs[2], tagnames, &tag);
}

/*
 * Set window colors
 */
colors()
{
	int num, color, f, b;
	static char *cnames[] = {
		"Message line",
		"Main screen",
		"Pop up windows" };

	num = 0;
	for(;;) {
		color = attrs[num];
		f = color & 0x0F;
		b = color & 0xF0;
		wopen(20, 9, 32, 7, (WSAVE|WCOPEN|WBOX2)|(color & 0xFF));
		wcursor_off();
		*W_OPEN = (f << 4) | (b >> 4);
		wputs(cnames[num]);
		*W_OPEN = color;
		wputs("\n\nUp/Down    = Change forground\n");
		wputs("Left/Right = Change background\n");
		wputs("PgUp/PgDn  = Change screen");
		switch(wgetc()) {
			case 0x1B :
				wclose();
				*mwin = attrs[0];
				*W_OPEN = attrs[1];
				return;
			case _KPU : num = (num + 1) % 3;	break;
			case _KPD : num = (num + 2) % 3;	break;
			case _KUA : ++f;					goto setcolor;
			case _KDA : --f;					goto setcolor;
			case _KRA : b += 16;				goto setcolor;
			case _KLA : b -= 16;
			setcolor:
				attrs[num] = (color & 0xFF00) | (f & 0x0f) | (b & 0xF0);
			default: }
		wclose(); }
}

/*
 * Main program - parse arguments, and start browser (if not TSR).
 */
main(int argc, char *argv[])
{
	int i;

	/* At startup, zero filenames & search strings */
	for(i=0; i < NUM_FILES; ++i)
		*filename[i] = *search_string[i] = 0;

	/* If not TSR, activate browser now */
	if((argc < 2) || (*(argptr = argv[1]) != '/')) {
		browser();
		return; }

	/* TSR... parse hotkeys & set TSR vectors */
	i = 0;
	while(*++argptr) switch(toupper(*argptr)) {
		case 'A' : i |= ALT;		break;
		case 'C' : i |= CONTROL;	break;
		case 'L' : i |= L_SHIFT;	break;
		case 'R' : i |= R_SHIFT;	break;
		case 'S' : i |= SYS_REQ;	break;
		default: abort("\nInvalid HOTKEY"); }
	argptr = (argc > 2) ? argv[2] : 0;
	tsr(&browser, i, 10000);
}
[ RETURN TO DIRECTORY ]