root/browse/browse.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. main
  2. clear_all
  3. clone
  4. newname
  5. readent
  6. readent
  7. getdir
  8. at_current
  9. at_file
  10. disp_flags
  11. show_flags
  12. tag
  13. tagged
  14. delete_from_display
  15. delete_entry
  16. ins_at
  17. ins_entry
  18. domove
  19. addfile
  20. pname
  21. sortdir
  22. entcmp
  23. dump
  24. statout
  25. u_name
  26. g_name
  27. printime
  28. header
  29. triad
  30. pwd
  31. browse
  32. clearin
  33. name_of
  34. cmd
  35. macro
  36. newdir
  37. reload
  38. topline
  39. set_quickmode
  40. redraw
  41. dumpdata
  42. here_i_am
  43. bell
  44. system
  45. addstring
  46. inps
  47. ctlouts
  48. ctloutc
  49. ctlout
  50. get_index
  51. file_name
  52. enter
  53. isdir
  54. getch
  55. ungetch

/*             -- Just what the hell am I ??? ---                            */
/* (If this is made from MAKEFILE, BSD or USG should be set.) */

#include <stdio.h>

#include "system.h"

/*              -- Miscellaneous include files --                            */

#include <sys/param.h>                  /* NCARGS, and others */
#ifndef M_XENIX
# include <sys/types.h>                       /* data types for various files */
#endif
#include <sys/stat.h>         /* stat data structure for getdir(), statout() */
#include <sys/dir.h>                      /* dir data structure for getdir() */
#include <pwd.h>                       /* passwd data structure for u_name() */
#include <grp.h>                        /* group data structure for g_name() */
#ifdef BSD
# include <sys/time.h>                  /* time data structure for printime() */
#else
# include <time.h>                     /* time data structure for printime() */
#endif
#include <signal.h>

#ifndef MAXNAMLEN
# ifdef BSD
#  define MAXNAMLEN 256
# else
#  define MAXNAMLEN DIRSIZ
# endif
#endif

/*              -- make information --
BUILD
browse: browse.c
        cc browse.c -O -o browse -ltermlib
END
*/

/*              -- Miscellaneous defines --                                  */
#define FALSE 0
#define TRUE 1

#define MAXENTS 1024
#define MAXID 16
#define MAXLINE 81
#define NCOL 64
#define MAXNAME 14
#define FILENAME 256

/*              -- Extended directory entry format --                        */
struct entry {
        char *e_name;
        int e_flags;
#define FTAGGED (1<<0)
#define FPERMANENT (1<<1)
        struct stat e_stat;                             /* file status field */
        char e_uname[9];                                        /* user name */
        char e_gname[9];                                /* user's group name */
} 
*xentries[MAXENTS], **entries=xentries;
int nentries;

/*              -- Look-up cache for user names --                           */
struct idtab {
        int  id_id;                                    /* user (or group) id */
        char id_name[9];                                 /* name[8] + filler */
} 
u_list[MAXID],                                       /* Matched user id's. */
g_list[MAXID];                                              /* ditto group */
int u_ptr=0, g_ptr=0;                                     /* current entries */

/*              -- Global variables --                                       */
int ccol=NCOL;                      /* Width of used display, current column */
int quickmode;                    /* short display mode (files only) */

int     top, curr;               /* Positions of screen in directory */
#define bottom ((top+nlines>nentries)?nentries:(top+nlines))
char    *dot;                                   /* name of current directory */
int     nlines;                         /* number of lines displayed on page */
char    display_up;                               /* Does the display exist? */
int     todump=1;                                /* Do we want to dump data? */
int     ended;                                    /* Have we quite finished? */
int     intrup;                                 /* Have we been interrupted? */
char    *HOME;                                    /* Where did I start from? */
char    *SHELL;                                    /* How do I run programs? */

/*              -- types of functions !!!                                    */
char *getenv();
char *malloc();

#define NEW(t) (t *)malloc(sizeof (t))
#define NIL(t) ((t) 0)

/*              -- Code starts here: dummy main --                           */
main(ac, av)
int ac;
char **av;
{
        if(ac>1) chdir(av[1]);

        HOME=getenv("HOME");
        SHELL=getenv("SHELL");

        intrup=0;
        if(tcl_init()) {
                tinit(getenv("TERM"));
                clear_all();
                browse();
                tend();
                tcl_end();
        }
        exit(0);
}

clear_all()
{
        int i;

        for(i = 0; i < MAXENTS; i++)
                entries[i] = 0;
}

char *clone(name)
char *name;
{
        char *hold;
        
        hold = (char *)malloc(strlen(name)+1);

        if(hold==0)
                return 0;
        strcpy(hold, name);
        return hold;
}

newname(e, name)
struct entry *e;
char *name;
{
        if(e->e_name)
                free(e->e_name);
        e->e_name = clone(name);
}

#if BSD
readent(dp, buffer)
DIR *dp;
char *buffer;
{
        struct direct *ptr;

        do {
                ptr = readdir(dp);
                if(!ptr) return 0;
        } while(ptr->d_ino == 0);

        strcpy(buffer, ptr->d_name);
        return 1;
}
#else
#define opendir(n) fopen(n, "r")
#define DIR FILE
readent(dp, buffer)
DIR *dp;
char *buffer;
{
        struct direct db;
        while(fread(&db, sizeof(struct direct), 1, dp)) {
                if(db.d_ino) {
                        strncpy(buffer, db.d_name, MAXNAMLEN);
                        buffer[MAXNAMLEN] = 0;
                        return 1;
                }
        }
        return 0;
}
#define closedir fclose
#endif

getdir()
{
        char *u_name(), *g_name();
        DIR *dp;
        static char buffer[MAXNAMLEN+1];
        int i, p;

        if(!(dp = opendir("."))) {
                save_errno(".");
                return FALSE;
        }

        p = 0;
        for(i = 0; i < nentries; i++) {
                if(entries[i]->e_flags & FPERMANENT) {
                        if(p != i) {
                                struct entry *hold;
                                hold = entries[p];
                                entries[p] = entries[i];
                                entries[i] = hold;
                        }
                        p++;
                }
        }

        for(nentries = p; !intrup && nentries < MAXENTS; nentries++) {
                if(!entries[nentries]) {
                        entries[nentries] = NEW(struct entry);
                        if(!entries[nentries])
                                break;
                        entries[nentries]->e_name = NIL(char *);
                }

                if(!readent(dp, buffer))
                        break;

                if(stat(buffer, &entries[nentries]->e_stat)==-1) {
                        closedir(dp);
                        save_errno(buffer);
                        return FALSE;
                }

                newname(entries[nentries], buffer);

                entries[nentries]->e_flags = 0;

                strcpy(entries[nentries]->e_uname,
                        u_name(entries[nentries]->e_stat.st_uid));

                strcpy(entries[nentries]->e_gname,
                        g_name(entries[nentries]->e_stat.st_gid));
        }

        closedir(dp);

        if(intrup)
                return FALSE;

        if(nentries>=MAXENTS || entries[nentries]==NIL(struct entry *)) {
                save_errmsg(".: Directory too large");
                return FALSE;
        }

        sortdir();

        if(intrup) {
                save_errmsg("Interrupted");
                return FALSE;
        }
        return TRUE;
}

at_current()
{
        at_file(curr);
}

at_file(file)
int file;
{
        if(display_up) {
                if(file < top || file >= top+nlines)
                        return 0;
                at(0, curr-top+2);
        } else {
                if(file != curr)
                        return 0;
                cmdline();
        }
        return 1;
}

disp_flags(flags)
{
        outc((flags&FTAGGED)?'+':((flags&FPERMANENT)?'!':' '));
}

show_flags(ent)
{
        if(!display_up) return;
        if(ent < top || ent >= top+nlines) return;
        at(quickmode?0:(ccol-1), ent-top+2);
        disp_flags(entries[ent]->e_flags);
}

tag(ent, mode, bit)
{
        switch(bit) {
                case 'P': bit = FPERMANENT; break;
                case 'T': bit = FTAGGED; break;
        }
        switch(mode) {
                case -1: entries[ent]->e_flags &= ~bit; break;
                case 0: entries[ent]->e_flags ^= bit; break;
                case 1: entries[ent]->e_flags |= bit; break;
        }
        show_flags(ent);
}

tagged(ent)
{
        return (entries[ent]->e_flags & FTAGGED) ? 1 : 0;
}

delete_from_display(file)
int file;
{
        if(!display_up) return;

        if(file>=top+nlines) return;

        if(file < top) file = top;

        scroll(2+file-top, 2+nlines, 1);
        at(0, 2+nlines-1);
        if(top+nlines >= nentries)
                outc('~');
        else
                dump(bottom, bottom+1);
}
                
delete_entry(file)
int file;
{
        struct entry *hold;
        int     i;

        delete_from_display(file);

        hold = entries[file];
        for(i=file; i<nentries-1; i++)
                entries[i]=entries[i+1];
        entries[nentries-1]=hold;
        nentries--;

        if(file < curr || curr >= nentries) {
                curr--;
                if(top >= nentries) {
                        top--;
                        display_up = 0;
                        todump = 1;
                }
        }
} 

ins_at(ent, i)
struct entry *ent;
int i;
{
        struct entry *hold;
        int j;

        /* Allocate slot at end */
        if(!entries[nentries]) {
                entries[nentries] = NEW(struct entry);
                if(!entries[nentries]) {
                        save_errno(ent->e_name);
                        return 0;
                }
                entries[nentries]->e_name = NIL(char *);
        } else if(entries[nentries]->e_name) {
                free(entries[nentries]->e_name);
                entries[nentries]->e_name = NIL(char *);
        }

        /* Copy data into slot */
        *entries[nentries] = *ent;
        entries[nentries]->e_name = clone(ent->e_name);
        if(!entries[nentries]->e_name) {
                save_errno(ent->e_name);
                return 0;
        }

        if(i != nentries) {
                /* Rotate slot to middle */
                hold = entries[nentries];
                for(j = nentries; j > i; j--)
                        entries[j] = entries[j-1];
                entries[i] = hold;
        }
        nentries++;

        if(display_up) {
                if(i < top)
                        i = top;
                if(i >= bottom)
                        return 1;
                scroll(2+i-top, 2+nlines, -1);
                at(0, 2+i-top);
                dump(i, i+1);
        }
        return 1;
}

ins_entry(ent)
struct entry *ent;
{
        int i;

        if(nentries >= MAXENTS) {
                save_errmsg("Too many files");
                return 0;
        }

        for(i = 0; i < nentries; i++)
                if(entcmp(&ent, &entries[i]) < 0)
                        break;

        return ins_at(ent, i);
}

domove(ent, new_name)
int ent;
char *new_name;
{
        struct entry hold;
        hold = *entries[ent];

        if(link(entries[ent]->e_name, new_name)!=0) {
                save_errno(entries[ent]->e_name);
                return 0;
        }
        if(unlink(entries[ent]->e_name)!=0) {
                save_errno(entries[ent]->e_name);
                return 0;
        }

        hold.e_name = new_name;
        hold.e_flags &= ~FTAGGED;

        delete_entry(ent);
        return ins_entry(&hold);
}

addfile(name)
char *name;
{
        struct entry hold;

        if(stat(name, &hold.e_stat)==-1) {
                save_errno(name);
                return FALSE;
        }

        hold.e_name = name;

        hold.e_flags = 0;

        strcpy(hold.e_uname, u_name(hold.e_stat.st_uid));

        strcpy(hold.e_gname, g_name(hold.e_stat.st_gid));

        return ins_entry(&hold);
}

pname(name, mode)
char *name;
int mode;
{
        int i;
        char *slash, *rindex();
        int max=quickmode?MAXLINE:(MAXNAME+1);

        if((mode&S_IFMT)==S_IFDIR)
                max--;
        else if((mode&S_IFMT)==S_IFREG && (mode&0111))
                max--;
        else if((mode&S_IFMT)!=S_IFREG)
                max--;

        if(!quickmode && (slash=rindex(name, '/'))) {
                name=slash;
                outc('<');
                max--;
        }
        for(i=0; i<max && *name; name++)
                i += ctlout(*name);
        standend();
        if(i==max && *name) {
                outc('\b');
                outc('>');
        }
        if((mode&S_IFMT)==S_IFDIR) {
                outc('/');
        }
        else if((mode&S_IFMT)==S_IFREG && (mode&0111)) {
                outc('*');
        }
        else if((mode&S_IFMT)!=S_IFREG) {
                outc('?');
        }
}

sortdir()
{
        int   entcmp();
        qsort(entries, nentries, sizeof(struct entry *), entcmp);
}

entcmp(e1, e2)
struct entry **e1, **e2;
{
        if((*e1)->e_name[0] == '/') {
                if((*e2)->e_name[0] != '/')
                        return 1;
        } else if((*e2)->e_name[0] == '/')
                return -1;

        return strcmp((*e1)->e_name, (*e2)->e_name);
}

dump(start, end)
int start;
int end;
{
        int  i;
        int  lo = (start<nentries)?start:nentries;
        int  hi = (end<nentries)?end:nentries;

        for(i=lo; i<hi; i++) {
                statout(entries[i]->e_name,
                &entries[i]->e_stat,
                entries[i]->e_uname,
                entries[i]->e_gname,
                entries[i]->e_flags);
                nl();
        }
        return TRUE;
}

statout(name, sbuf, user, group, flags)
char *name;
struct stat *sbuf;
char *user, *group;
int flags;
{
        int mode = sbuf->st_mode;

        if(!quickmode) {
                printf("%5u ", sbuf->st_ino);

                if((mode&S_IFMT)==S_IFCHR) outc('c');
                else if((mode&S_IFMT)==S_IFBLK) outc('b');
                else if((mode&S_IFMT)==S_IFDIR) outc('d');
                else if((mode&S_IFMT)==S_IFREG) outc('-');
                else outc('?');
                triad((mode>>6)&7, mode&S_ISUID, 's');
                triad((mode>>3)&7, mode&S_ISGID, 's');
                triad(mode&7, mode&S_ISVTX, 't');
                outc(' ');

                printf("%3u ", sbuf->st_nlink);
                printf("%-8s ", user);
                printf("%-8s ", group);

                if((mode&S_IFMT)==S_IFREG || (mode&S_IFMT)==S_IFDIR)
                        printf("%7ld ", sbuf->st_size);
                else
                        printf("%3d,%3d ",
                        major(sbuf->st_rdev),
                        minor(sbuf->st_rdev));

                outc(' ');
                printime(&sbuf->st_mtime);
        }

        disp_flags(flags);
        pname(name, sbuf->st_mode);
}

char *
u_name(uid)
int uid;
{
        int  i;
        struct passwd *pwptr, *getpwuid();

        for(i=0; i<u_ptr; i++)
                if(u_list[i].id_id==uid)
                        return u_list[i].id_name;

        if(u_ptr>=MAXID)                                       /* cache full */
                u_ptr = 0;        /* simplistic algorithm, wrap to beginning */
        /* with MAXID >> # common id's it's good enough */

        u_list[u_ptr].id_id=uid;

        if(pwptr=getpwuid(uid)) { /* Copy name */
                for(i=0; pwptr->pw_name[i]>' '; i++)
                        u_list[u_ptr].id_name[i]=pwptr->pw_name[i];
                u_list[u_ptr].id_name[i]=0;
        } 
        else /* Default to UID */
                sprintf(u_list[u_ptr].id_name, "%d", uid);

        return u_list[u_ptr++].id_name;
}

char *
g_name(gid)
int gid;
{
        int  i;
        struct group *grptr, *getgrgid();

        for(i=0; i<g_ptr; i++)
                if(g_list[i].id_id==gid)
                        return g_list[i].id_name;

        if(g_ptr>=MAXID)                                       /* cache full */
                g_ptr = 0;        /* simplistic algorithm, wrap to beginning */
        /* with MAXID >> # common id's it's good enough */

        g_list[g_ptr].id_id=gid;

        if(grptr=getgrgid(gid)) { /* Copy name */
                for(i=0; grptr->gr_name[i]>' '; i++)
                        g_list[g_ptr].id_name[i]=grptr->gr_name[i];
                g_list[g_ptr].id_name[i]=0;
        } 
        else /* Default to UID */
                sprintf(g_list[g_ptr].id_name, "%d", gid);

        return g_list[g_ptr++].id_name;
}

printime(clock)
long *clock;
{
        struct tm *tmbuf, *localtime();
        static char *months[12]= {
                "Jan","Feb","Mar","Apr","May","Jun",
                "Jul","Aug","Sep","Oct","Nov","Dec"
        };

        tmbuf=localtime(clock);
        printf("%2d %3s %02d %2d:%02d",
        tmbuf->tm_mday,
        months[tmbuf->tm_mon],
        tmbuf->tm_year,
        tmbuf->tm_hour,
        tmbuf->tm_min);
}

header()
{
        if(quickmode)
                printf(" File Name");
        else
                printf(
"Inode Long mode  LNX User     Group   Size/Dev  Modify Time     File name"
                    );
        nl();
}

triad(bits, special, code)
int bits, special;
char code;
{
        if(bits&4) outc('r');
        else outc('-');

        if(bits&2) outc('w');
        else outc('-');

        if(special) outc(code);
        else if(bits&1) outc('x');
        else outc('-');
}

char *
pwd()
#ifdef GETCWD
{
        static char mydir[FILENAME+1] = "";
        char *getcwd();

        return getcwd(mydir, FILENAME);
}
#else
{
        static char buffer[FILENAME+1];

        strcpy(buffer, "exec pwd");
        if(tcl_call(buffer, sizeof buffer)) {
                int len = strlen(buffer);
                while(len > 0 && buffer[len-1] <= ' ')
                        len--;
                buffer[len] = 0;
                return buffer;
        }
        save_errmsg(buffer);
        return 0;
}
#endif

browse()
{
        int  c;

        newdir();
        redraw();
        ended=0;

        do {
                intrup=0;       /* clear interrupt */
                here_i_am();
                fflush(stdout);
                c = getch();
                cmd(c);
        } 
        while(!ended);
}

clearin()
{
        fseek(stdin, 0L, 1); /* stay here messily */
}

char *name_of(c)
int c;
{
        static char *p, b[80];
        int ctrl = 0;

        p = b;
        if(c & 0x80) {
                c &= 0x7F;
                strcpy(b, "meta_");
                p += 5;
        }

        switch(c) {
                case '\033':
                        strcpy(p, "escape");
                        return b;
                case '\034':
                        strcpy(p, "ctrl_backslash");
                        return b;
                case '\035':
                        strcpy(p, "ctrl_close_bracket");
                        return b;
        }

        if(c < ' ') {
                c += '@';
                ctrl = 1;
        }

        switch(c) {
                case ' ': strcpy(p, "space"); break;
                case '$': strcpy(p, "dollar_sign"); break;
                case '\'': strcpy(p, "quote"); break;
                case '"': strcpy(p, "double_quote"); break;
                case ';': strcpy(p, "semicolon"); break;
                case '{': strcpy(p, "open_brace"); break;
                case '}': strcpy(p, "close_brace"); break;
                case '\177': strcpy(p, "delete"); break;
                case '\\': strcpy(p, "backslash"); break;
                case '[': strcpy(p, "open_bracket"); break;
                case ']': strcpy(p, "close_bracket"); break;
                default: 
                        if(ctrl)
                                sprintf(p, "'^%c'", c);
                        else
                                sprintf(p, "'%c'", c);
                        break;
        }
        return b;
}

cmd(c)
int c;
{
        char buffer[80];

        if(intrup) {
                cmdline();
                outs("Interrupt");
                return;
        }

        sprintf(buffer, "key_%s\n", name_of(c));
        if(!tcl_call(buffer, 80)) {
                cmdline();
                ctlouts(buffer);
                if(!display_up) outc('\n');
                bell();
        }
}

char *macro(c)
int c;
{
        static char buffer[80];

        sprintf(buffer, "macro_%s\n", name_of(c));
        if(tcl_call(buffer, 80))
                return buffer;
        else
                return 0;
}

newdir()
{
        int result = 1;

        if(display_up)
                at(0,0);
        fflush(stdout);
        dot=pwd();

        if(!getdir())
                result = 0;

        curr=0;
        top=0;
        topline();
        if(display_up)
                todump=TRUE;

        return result;
}

reload()
{
        if(getdir()) {
                curr=(curr>=bottom)?bottom-1:curr;
                if(display_up) {
                        topline();
                        todump=TRUE;
                }
                return 1;
        }
        return 0;
}

topline()
{
        if(display_up)
                at(0,0);
        printf("%s: %d files", dot, nentries);
        nl();
}

set_quickmode(mode)
int mode;
{
        if(quickmode != mode) {
                quickmode = mode;
                if(display_up) {
                        at(0,1);
                        header();
                        todump = 1;
                }
        }
}

redraw()
{
        clear_screen();
        display_up=0;
        topline();
        at(0,1);
        header();
        todump=1;
        display_up=1;
}

dumpdata()
{
        at(0,2);
        dump(top,bottom);
        if(top+nlines>nentries) {
                int i;
                at(0, bottom-top+2);
                for(i=bottom-top; i<nlines; i++) {
                        outc('~');
                        nl();
                }
        }
}

here_i_am()
{
        if(todump)
                display_up = 1;
        if(!display_up) {
                cmdline();
                statout(entries[curr]->e_name,
                        &entries[curr]->e_stat,
                        entries[curr]->e_uname,
                        entries[curr]->e_gname,
                        entries[curr]->e_flags);
                at(quickmode?1:ccol, nlines+2);
                fflush(stdout);
        } else if(todump) {
                if(!(curr-top > 0 && curr-top < nlines)) {
                        top = curr-nlines/2;
                        if(top<0) top=0;
                }
                dumpdata();
                at(quickmode?1:ccol, curr-top+2);
                todump=0;
        } else {
                int lines_to_scroll = curr-top;
                if(lines_to_scroll > 0)
                        if((lines_to_scroll -= nlines-1) < 0)
                                lines_to_scroll = 0;
                if(lines_to_scroll < 1-nlines) {
                        top=curr;
                        at(0,2);
                        dump(top, bottom);
                } else if(lines_to_scroll > nlines-1) {
                        top=curr-nlines+1;
                        at(0,2);
                        dump(top, bottom);
                } else if(lines_to_scroll) {
                        scroll(2, nlines+2, lines_to_scroll);
                        top += lines_to_scroll;
                        if(lines_to_scroll < 0) {
                                at(0, 2);
                                dump(top, top-lines_to_scroll);
                        } else {
                                at(0, 2+nlines-lines_to_scroll);
                                dump(bottom-lines_to_scroll, bottom);
                        }
                }
                at(quickmode?1:ccol, curr-top+2);
        }
}

bell()
{
        outc(7);
}

system(command)
char *command;
{
        int status;
        int pid;
        SIGNAL (*sigint)(), (*sigquit)();
#ifdef BSD
        SIGNAL  (*sigtstp)();
#endif

        sigint=signal(SIGINT, SIG_IGN);
        sigquit=signal(SIGQUIT, SIG_IGN);
#ifdef BSD
        sigtstp = signal(SIGTSTP, SIG_IGN);
#endif
        end_screenmode();
        cooktty();
        fflush(stdout);
        pid = fork();
        if(pid == 0) {
                signal(SIGINT, SIG_DFL);
                signal(SIGQUIT, SIG_DFL);
#ifdef BSD
                signal(SIGTSTP, SIG_DFL);
#endif
                fflush(stdout);
                execl(SHELL, SHELL, "-c", command, (char *)0);
                execl("/bin/sh", "sh", "-c", command, (char *)0);
                perror("/bin/sh");
                exit(-77);
        }
        if(pid==-1) {
                save_errno("browse");
                status = -1;
        } else
                wait(&status);
        signal(SIGINT, sigint);
        signal(SIGQUIT, sigquit);
#ifdef BSD
        signal(SIGTSTP, sigtstp);
#endif
        rawtty();

        if(status & 0xFF)
                return -(status&0xFF);
        else
                return (status&0xFF00)>>8;
}

addstring(ptr, buf, ip)
char *ptr, *buf;
int *ip;
{
        while(*ptr)
                ctlout(buf[(*ip)++] = *ptr++);
        standend();
}

char
inps(buf, text, termin)
char *buf;
char *text;
char termin;
{
        int i = 0;
        char c, *txp;
        char *s;

        txp=text?text:"!";

        while(!intrup &&
            (fflush(stdout), c=getch()) != '\033' &&
            c != '\n' &&
            c != termin)
                switch(c) {
                case '\b': 
                case '\177':
                        if(i>0) {
                                printf("\b \b");
                                i--;
                        } 
                        else
                                bell();
                        break;
                case 'U'-'@':
                        txp=text?text:"!";
                case 'X'-'@':
                        if(i>0)
                                while(i>0) {
                                        printf("\b \b");
                                        i--;
                                }
                        else
                                bell();
                        break;
                case 'K'-'@':
                        if(*txp)
                                ctloutc(buf[i++] = *txp++);
                        break;
                case '\\':
                        outc(c);
                        fflush(stdout);
                        c=getch();
                        if( c=='\\' || c=='\b' || c=='\177' ||
                            c=='K'-'@' || c=='U'-'@' || c=='X'-'@' ||
                            c==termin || c=='\033' || c=='\n') {
                                outc('\b');
                                ctloutc(buf[i++]=c);
                                break;
                        } 
                        else if(c>='0' && c<='7') {
                                int n, val=0;
                                for(n=0; n<3 && c>='0' && c<='7'; n++) {
                                        val = val*8 + c-'0';
                                        outc('\b');
                                        ctloutc(val);
                                        c=getch();
                                }
                                ungetch(c);
                                c=buf[i++]=val;
                                break;
                        } 
                        else if(c=='^') {
                                outc('\b');
                                outc('^');
                                fflush(stdout);
                                c=getch();
                                if(c>='?'&&c<='_') {
                                        outc('\b');
                                        ctloutc(buf[i++]=(c-'@')&'\177');
                                        break;
                                } 
                                else if(c>='`'&&c<='~') {
                                        outc('\b');
                                        ctloutc(buf[i++]=c-'`');
                                        break;
                                } /* otherwise default */
                                else buf[i++]='^'; /* after adding caret */
                        }
                        else buf[i++]='\\';
                default:
                        if(c=='V'-'@')
                                ctloutc(buf[i++] = getch());
                        if(s = macro(c))
                                addstring(s, buf, &i);
                        else
                                ctloutc(buf[i++] = c);
                        break;
                }

        buf[i] = 0;
        return intrup?'\033':c;
}

ctlouts(s)
char *s;
{
        int cnt = 0;

        while(*s)
                cnt += ctlout(*s++);
        cnt += standend();

        return cnt;
}

ctloutc(c)
char c;
{
        int cnt;

        cnt = ctlout(c);
        cnt += standend();

        return cnt;
}

ctlout(c)
char c;
{
        int cnt = 0;
        if(c&'\200') {
                cnt += underline();
                cnt += ctlout(c&'\177');
                return cnt;
        } 
        else if(c<' ' || c=='\177') {
                cnt += standout();
                outc((c+'@')&'\177');
                cnt++;
                return cnt;
        } 
        else {
                cnt += standend();
                outc(c);
                cnt++;
                return cnt;
        }
}

get_index(file)
char *file;
{
        int i;

        for(i = 0; i<nentries; i++)
                if(strcmp(entries[i]->e_name, file) == 0)
                        return i;
        return -1;
}

char *file_name(i)
int i;
{
        if(i < 0 || i >= nentries)
                return 0;
        else
                return entries[i]->e_name;
}

enter(dir)
char *dir;
{
        if(access(dir, 5)==0 && chdir(dir)==0)
                return newdir();
        save_errno(dir);
        return 0;
}

isdir(entry)
struct entry *entry;
{
        return((entry->e_stat.st_mode&S_IFMT)==S_IFDIR);
}

getch()
{
        return getchar();
}

ungetch(c)
char c;
{
        ungetc(c, stdin);
}


/* [<][>][^][v][top][bottom][index][help] */