/* * 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 #include #include #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); }