Newsgroups: comp.sources.unix From: alexj@equinox.unr.edu (Jack Alexander) Subject: v25i140: ils - interactive "ls" and browser Sender: unix-sources-moderator@pa.dec.com Approved: vixie@pa.dec.com Submitted-By: alexj@equinox.unr.edu (Jack Alexander) Posting-Number: Volume 25, Issue 140 Archive-Name: ils #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh 'MANIFEST' <<'END_OF_FILE' MANIFEST Makefile README get.c get.h ils.1 ils.c ils.h input.c main.c parse.c wildmat.c END_OF_FILE if test 88 -ne `wc -c <'MANIFEST'`; then echo shar: \"'MANIFEST'\" unpacked with wrong size! fi # end of 'MANIFEST' fi if test -f 'Makefile' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Makefile'\" else echo shar: Extracting \"'Makefile'\" \(524 characters\) sed "s/^X//" >'Makefile' <<'END_OF_FILE' X# Makefile for ils X# CFLAGS = -D$(SYSTEM) -DNODEBUG -g X# for SYSTEM, make it SYSTEMV for System V and BSD for Berkely. SYSTEM = SYSTEMV X CC = cc X SRCS = main.c ils.c parse.c get.c input.c wildmat.c ils.h get.h X MANIFEST = Makefile README $(SRCS) X OBJS = main.o ils.o parse.o get.o input.o wildmat.o X all: ils X clean: X rm -f *.o X rm -f ils X ils: $(OBJS) X $(CC) $(OBJS) -o ils -lcurses -ltermcap X main.o: main.c ils.h X ils.o: ils.c ils.h X parse.o: parse.c X get.o: get.c get.h X input.o: input.c ils.h X wildmat.o: wildmat.c END_OF_FILE if test 524 -ne `wc -c <'Makefile'`; then echo shar: \"'Makefile'\" unpacked with wrong size! fi # end of 'Makefile' fi if test -f 'README' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'README'\" else echo shar: Extracting \"'README'\" \(3079 characters\) sed "s/^X//" >'README' <<'END_OF_FILE' AUTHOR: Jack Alexander DATE OF LAST MODS: 1/11/91 ANY COMMENTS OR REQUESTS FOR CHANGES WILL BE GREATFULLY ACCEPTED. X---------------------------------------------------------------------- X(c) 1991 by Jack Alexander NO WARRANTY OF ANY KIND IS GIVEN WITH THIS PROGRAM, EVEN FOR XFITNESS OF PURPOSE X---------------------------------------------------------------------- X ILS - Interactive "ls" and browser for UNIX systems. X This has been tested on BSD-ish and System V machines from several vendors including Sun, NCR, AT&T, and NeXT (previous version). X ils is "interactive ls", a utility to users to easily move around a directory tree and perform operations on objects in the directories. XEach user can define their own actions based on key-sequences. X The number of built-in commands has been kept to a minimum intentionally. The idea is to allow total flexibility by allowing people to use the same tools they've used for years (little shell scripts, etc), and to just offer them a more efficient, user-friendly way to do it. I know that this has been done before, but I didn't like the others ones that much because they were to specific and limiting. With ils, you can have as powerful or as "safe" a tool as you like, varying by user. X The operations available are defined by the user in $HOME/.ils in a manner as described in the file ils.1 file which is part of this release. X ils can really replace the use of the shell almost entirely for many users (I use it myself quite a bit -- I guess that's why I wrote it). X NOTE: I recommend that you always use the -F option, as it certainly makes identification of directories easy (puts '/' after each directory). This can be automatically done by linking ilf to ils, then just using the name ilf instead of ils. X INPUT: string input is done using a getstring I wrote a few years back. It's a little odd, as it was written for people that don't have any vi experience, as each key has a specific function, and you are always in insert mode. Editing instructions can be found in ils.1. X OVERVIEW: X The user (or administrator) has a file called .ils in their $HOME directory. A sample file might be: X X# This is a sampel .ils file help: man ils X m: more $file X V: vi $file X M: make $input(Make what (RETURN for default)? ) X mv: mv $file $input(Rename '$file' to: ) X XF: file $file X L: if(latex $prefix);then if(dvijep $prefix);then lpr -B $prefix.jep;fi;fi X rm: rm $file X X---------------------- end of example file ------------------ X In the above file, key sequences "help", "m", "M", "mv", "F", "L", "rm", and "V" are given actions. X X$file is a built-in ils variable (see ils.1) that always contains the currently selected file (the one pointed at by the cursor). $prefix, and $input are also special variables, and there are others. X Anyway, if any of the key sequences are eneterd through the keyboard, and then ENTER or RETURN is pressed, the action defined for the key sequences in executed, a line at a time, via system(3) calls. You can multiple action lines for each key sequence, but the example doesn't contain any. END_OF_FILE if test 3079 -ne `wc -c <'README'`; then echo shar: \"'README'\" unpacked with wrong size! fi # end of 'README' fi if test -f 'get.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'get.c'\" else echo shar: Extracting \"'get.c'\" \(4715 characters\) sed "s/^X//" >'get.c' <<'END_OF_FILE' X/* %W% general getstring routine */ X/* author: Jack Alexander - this routine may be freely distibuted */ X#include X#include "get.h" X get_num(x,y,digits,low,high,number) int x,y,digits,low,high,*number; X{ X char buf[80]; X int retval; X X buf[0]='\0'; X noecho(); X raw(); X while(1) X switch(getst(digits,x,y,buf,digits+1,NULL,NUM_ONLY,NULL)) { X case GET_ESCAPE: X return(GET_ESCAPE); X case GET_DOWN: X case GET_RETURN: X retval=atoi(buf); X if(retval < low || retval>high) X break; X *number = retval; X echo(); X noraw(); X return(0); X default: X break; X } X} X X/* a few hints... */ X/* idx = current index into string */ X/* cx = current screen x position */ X/* cy = current screen y position */ X/* ix = initial screen x (upon call, where cursor is requested to be placed) */ X/* iy = initial screen y " " " ... */ X/* fw = field width */ X/* mx = max length of string */ X/* sl = string length */ X/* valid = string of acceptable characters within the type passed in gettype */ X/* if NULL, then all type of characters passed in gettype are allowed */ X int idx, cx, cy, ix, iy, fw, mx, sl, rcode; X getst(len,sx,sy,str,fl,retlen,gettype,valid) int len, sx, sy, fl, *retlen, gettype; char *str, *valid; X{ X int c; X X X rcode = INITIAL; X idx=0; X cx=ix=sx; X cy=iy=sy; X mx = len; X fw = fl; X str[mx]='\0'; X sl=strlen(str); X if(sl!=0) { X idx =sl; /* place cursor at string's end */ X cx +=sl; X } X#ifdef undef X init(); X#endif X while(1) { X if(gettype==SHOW) { /* want only to display the string */ X drawstr(str,0); /* draw the string */ X#ifdef undef X de_init(); X#endif X return; X } X drawstr(str,1); /* draw the string */ X c=getch()&0x7f; X switch(c) { X case GET_CLEAR: X clearall(str); X break; X case GET_ESCAPE: X rcode=GET_ESCAPE; X break; X case GET_UP: X rcode=dec_y_pos(); X break; X case GET_DOWN: X rcode=inc_y_pos(); X break; X case GET_LEFT: X rcode=dec_x_pos(); X break; X case GET_RIGHT: X rcode=inc_x_pos(1); X break; X case GET_DELETE: X case GET_DELETE2: X remove_char(str); X break; X case GET_TAB: X case GET_RETURN: X rcode = GET_RETURN; X break; X default: X switch(gettype) { X case NUM_ONLY: /* numbers only (0-9) */ X if(c>='0' && c<='9') X if(in(c,valid)) X add_char(str,c); X break; X case LETTER_ONLY: X /* letters a-z and A-Z, and ' ' only */ X if(c==' ' || (c>='a' && c<='z') || (c>='A' && c<='Z')) X if(in(c,valid)) X add_char(str,c); X break; X case ALL_ALPHA:/* any printable chars */ X if(c>=' ' && c<=0x7f) X if(in(c,valid)) X add_char(str,c); X break; X default: X if(c>=' ' && c<=0x7f) X add_char(str,c); X break; X } X break; X } X if(rcode != INITIAL) { X#ifdef undef X de_init(); X#endif X if(retlen != NULL) X *retlen = sl; X return(rcode); X } X } X} X dec_y_pos() X{ X if(!idx) X return(GET_UP); X if(iy >= cy) { X idx = 0; X cx = ix; X return(INITIAL); X } X idx -= fw; X cy--; X return(INITIAL); X} X inc_y_pos() X{ X if(idx>=mx-1 || idx==sl) X return(GET_DOWN); X if(idx+fw >= mx) { X cx += mx-idx - (mx-sl); X idx += mx-idx - (mx-sl); X#ifdef undef X cx += (mx - idx -fw); X idx += (mx - idx - fw); X#endif X return(INITIAL); X } X if(idx+fw > sl) { X cx += sl - idx; X idx += sl - idx; X return(INITIAL); X } X idx += fw; X cy++; X return(INITIAL); X} X inc_x_pos(flag) int flag; X{ X if(flag && idx==sl) X return(GET_RIGHT); X if(idx == mx) X return(GET_RIGHT); X cx++; X idx++; X if(cx >= (ix+fw)) { X cx = ix; X cy++; X } X return(INITIAL); X} X dec_x_pos() X{ X if(idx==0) X return(GET_LEFT); X idx--; X cx--; X if(cx < ix) { X cx = fw+ix-1; X cy--; X } X return(INITIAL); X} X add_char(str, c) char *str; char c; X{ X register int i; X X if(inc_x_pos(0)!=INITIAL) X return; X for(i=mx-1;i>=idx;i--) X str[i]=str[i-1]; X str[idx - 1] = c; X if(sl!=mx) X sl++; X} X remove_char(str) char *str; X{ X register int i; X X if(idx >= sl) { /* remove char behind cursor */ X if(dec_x_pos()!=INITIAL) X return; X str[sl-1]='\0'; X } X else X for(i=idx;i=s) { X addch('.'); X i++; X } X else if((s-i) < fw) { X printw("%s",&str[i]); X i += s-i; X } X else { X for(j=0;j'get.h' <<'END_OF_FILE' X/* defines for my getstring routine */ X/* %W% */ X X#define INITIAL 0 X#define GET_CLEAR 0x03 /* control c */ X#define GET_UP 0x0b /* control k */ X#define GET_DOWN 0x0a /* control j */ X#define GET_LEFT 0x08 /* control h */ X#define GET_RIGHT 0x0c /* control l */ X#define GET_TAB 0x09 /* control i */ X#define GET_RETURN 0x0d /* control m */ X#define GET_DELETE 24 /* control x */ X#define GET_DELETE2 0x7f /* delete key on most terminals */ X#define GET_ESCAPE 0x1b /* escape */ X X/* types of characters that can be specified for getstring input */ X#define SHOW 0 /* only show the string, don't ask for input */ X#define NUM_ONLY 1 /* only numeric characters 0-9 */ X#define LETTER_ONLY 2 /* only letters a-z, A-Z and space (' ') */ X#define ALL_ALPHA 3 /* all printable ascii characters are permitted */ END_OF_FILE if test 797 -ne `wc -c <'get.h'`; then echo shar: \"'get.h'\" unpacked with wrong size! fi # end of 'get.h' fi if test -f 'ils.1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'ils.1'\" else echo shar: Extracting \"'ils.1'\" \(7789 characters\) sed "s/^X//" >'ils.1' <<'END_OF_FILE' X.PU X.TH ILS 1 local X.SH NAME ils \- interactive directory browser and visual shell X.SH SYNOPSIS X.ll +8 X.B ils \[-aFeTrc \[title\]\] \[ pattern1 pattern2 \.\.\. patternN \] X.ll -8 X.br X.SH DESCRIPTION X.B ils was orginally just a driectory browsing utility to allow users to move around the system, descending into directories through a visually-oriented interface. X Since the original implementation, the functionality of X.B ils has been expanded to allow operations to be performed on the files in the directories currently being examined. Each user can define their own set of operations using built-in variables, environment variables, as well as keyboard input. By creating a file, ``.ils" in the $HOME directory, each user can define what sequence of keys invokes a set of shell operations. X There are several options, some of which corresponds to the option of the same name in the X.B ls(1) command: X.TP X.BI \-a List all entries; usually `.', `..' and all files that start with a `.' are suppresed. This is the default if the last character of the name of the program is an `a' (i.e., linking ila to ils). X.TP X.BI \-F Cause directories to be marked with a trailing `/' and executable files to be marked with a trailing `*'. This is the default if the last character of the name of the program is a `f' (i.e., linking ilf to ils). X.TP X.BI \-e XEcho all commands as they are executed. X.TP X.BI \-T\ \ Print a title on top of the window or screen. X.TP X.BI \-r Change the vertical size of the ils window from the default. The default is the size of the screen upon calling ils. If using this option, the number of rows must immediately follow, with no white space (i.e., ils -r10). X.TP X.BI \c Change the horizontal size of the ils window from the default. The default is the size of the screen upon calling ils. If using this option, the number of columns must immediately follow, with no white space (i.e., ils -r60). X X.SH WILDCARDING Patterns can be used in to match files, just as in ls(1). To pass a pattern to ils, and to avoid the shell from expanding it prior to invoking ils, the user can surround the pattern with quotes (i.e., 'ils "*.c"' instead of ils *.c). This will insure that the pattern match is done on every directory descended into during the execution of ils. Otherwise, if no quotes are used, the shell will expand the wildarding and pass ils all filenames that match the pattern in the current working directory. Any directory descended into that do not contain any files of the same name will appear to the user as an empty directory. X X.B ils wildcarding works exactly the same as sh(1), csh(1), and ksh(1) wildcarding (thanks to Rich Salz for the wildcarding routine). X X.SH THE .ils FILE XEach user can set up their own .ils file in their home ($HOME) directory to define their own key sequences and corresponding actions related to those key sequences. The .ils file has the following format: X X.nf key_sequence: command line 1 X command line 2 X ... X command line n X.fi X This tells X.B ils that when the key sequence is entered, execute the shell commands in command lines 1 through n. One current limitation X.B ils has is that each command line is executed seperately, so that no interdependent lines can be used. A call to system(3) is made on each line, so a new shell comes into existance for each line. Any environmental changes made such as shell variable assignment or change of working directory are lost after each command. You can, however, have multiple shell commands on one line by using semicolons delimiting seperate commands. X All lines that have a `#' character as the first character are assumed to be comments and are skipped. X.SH USING ils upon execution, the user will see a listing of the directory, very similar to the output of the X.B ls(1) command. On the top line is the path of the current directory. If possible, X.B ils will display the full path of the current working directory. If the path exceeds the width of the screen or window, it will be truncated. Since the right-most characters of a pathname are usually of the most interest, the truncation is done at the left end. As many characters as will fit are shown, and the path is preceeded by "...". X One file is always the "current file", it is pointed at by the X.B ils cursor `>'. To change the current file, there are cursor movement keys. The defaults are h,j,k and l (as in vi(1)). If a directory has been made the current file, the user can descend into the directory by pressing the RETURN or ENTER key. Once the user has descended into a directory the directory becomes the current working directory. The current path on the top line will be updated, and the contents of the directory will be shown on the screen. X The user can also ``force" a command from the keyboard by pressing the ':' key. This will prompt the user for a line of input which will be scaned for variable substitution, then executed via a call to system(3). This can be useful in that it makes it so that the user rarely has to leave ils to perform a command (i.e, ":rm *.o" will invoke a shell and remove all file ending in ".o"; ":more $file" will invoke a shell and display the cuurently selected file via the more(1) command). X To go back up one level (i.e., cd ..), press ESCAPE. X To exit ils, press control-D. X X.SH KEYBOARD INPUT and STRING EDITING When entering string from the keyboard, there is one important thing to remember: you are always in insert mode. The string editing routines in X.B ils use the following keys: X.TP X.BI ESCAPE Abort editing this string X.TP X.BI control-x Delete a character X.TP .BI X.BI control-c Clear entire string X.TP X.BI left-arrow Go left one character within the string X.TP X.BI ENTER (or RETURN) The first ENTER puts you at the end of the string, the second ENTER will pass the string back to ils for processing, and terminate the string edit. If you are already on the last character in the string, then the first press of ENTER will enter the string. X All of the command keys used in editing can be easily changed by modifying the file "ils.h", then re-making ils. In a later release, it is hoped that each user can define their editing key within their ".ils" file. X X.SH VARIABLES AND BUILT-IN COMMANDS X.B ils has several variables and commands built-in: X.TP X.BI $file the name of the file currently selected X.TP X.BI $prefix the prefix (all characters up to the first `.') of the file currently selected X.TP X.BI $path the current directory being viewed X.TP X.BI $all_files all files in the directory being viewed X.TP X.BI $input(prompt) value typed in from keyboard after user is prompted with the string contained in prompt (variable names can be used in contructing prompt) X.TP X.BI $ENVIRONMENT_VARIABLE the value of any environment variable can be used (i.e., $HOME will be substituted with the value of $HOME in the user's environment). X.B ils will use it's own variable values before checking for any existing environment variables. This means that is the user wants to use the value of the variable $file from his environment, X.B ils will use the name of the current file instead, since $file is a built-in variable. X X.SH SAMPLE .ils FILE X X X.nf X# This is a sample .ils file X# cc: cc $file X RM: rm $file X V: vi $file X X# make, can change default by entering an alernative X# (i.e., "make install") M: make $input(Make what (RETURN is default)? ) X X# rename the file mv: mv $file (Rename $file to: ) X X# LateX a file and print it L: latex $prefix X dvijep $prefix X lpr -b $prefix.jep X X# look at my definition file defs: more $HOME/.ils X.fi X X.SH RELEASE NOTES X X.PP X.SH "AUTHOR" Jack Alexander, (jack@jimi.cs.unlv.edu), (jack@equinox.unr.edu) X.SH "BUGS" No known bugs END_OF_FILE if test 7789 -ne `wc -c <'ils.1'`; then echo shar: \"'ils.1'\" unpacked with wrong size! fi # end of 'ils.1' fi if test -f 'ils.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'ils.c'\" else echo shar: Extracting \"'ils.c'\" \(25940 characters\) sed "s/^X//" >'ils.c' <<'END_OF_FILE' X/************************************************************************** X * ils - interactive "ls" library for ascii terminals. X * X * Subroutine ils expects variable "rows", and "cols" to be set prior to X * calling. rows should contain the verticle size of the window to X * be used, and cols should contain horizontal size (in characters). X * X * X * Author: Jack Alexander X * jack@marley X * X */ X#include "ils.h" X X#include X#include X#include X#include X#include X X#include "get.h" /* need key definitions from getstring routine */ X extern char *pnam; /* program name */ X int a_num, a_ind; /* work variables for argument processing */ int syms=0; /* number of symbols in table */ struct symbol *symtab[MAX_SYMBOLS]; /* symbol table */ char *args[ILS_MAX_ARGS]; /* argument list */ int modes; /* hold behavioral data */ X char cur_path[MAXPATH]; /* current path */ X X X/* interactive directory browser -- main routine X** X** variable pattern in a two dimensional array containing all of the patterns X** to use for pattern matching. If pattern[0] == NULL, then no X** patterns are used, and all files are shown. X** X** variable path is a string containing the path of the directory from which X** browsing will begin (typically, same as pwd(1)). X** X** ulx, uly, lrx, and lry are the coordinates (in screen coordinates where X** units are characters. X** X** m is an integer which may contain any behavioral charactaristics. X** various bits, as described in ils.h can be ored into this variable to X** change the behavior of ils. X** X** title is a string which is displayed on the top line of the window. If X** title is NULL, then no title is shown, and you get an extra line for X** editing. X**/ ils(pattern,path,ulx,uly,lrx,lry,m,title) char *pattern[],*path,*title; int ulx, uly, lrx, lry, m; X{ X struct d_entry *top; /* the very top of the known directory tree */ X X modes = m; /* makes user defined modes global */ X top = (struct d_entry *) NULL; /* haven't read any directories yet */ X clear(); X read_ils_defines(); /* read this user's definitions */ X read_directory(pattern,path,&top); /* read in current directory */ X edit_directory(pattern,path,&top,ulx,uly,lrx,lry,title); /* do it */ X} X X/* read_ils_defines() looks and the ".ils" file for the user. This file X contains all user-specific key sequences and correspoding actions for X the key sequences. */ read_ils_defines() X{ X char *getenv(), *a, dummy[MAXPATH]; X FILE *fopen(), *fp; X int lnum=0; X X a=getenv("HOME"); /* get path to user's home directory */ X sprintf(dummy,"%s/.ils",a); /* set up path to the .ils file */ X if((fp=fopen(dummy,"r"))==NULL) X return; /* no ".ils" file in home directory */ X while(fgets(dummy,MAXPATH,fp)!=NULL) { X lnum++; /* line number counter */ X if(dummy[0]=='#') X continue; /* a comment line */ X parse_line(dummy,lnum); /* enter into symbol table */ X } X fclose(fp); X} X X#ifdef BSD X/****** X * Berkeley UNIX version. read directory reads all files in the current X * directory. X */ read_directory(pattern,path,dptr) char *pattern[],path[]; struct d_entry **dptr; X{ X DIR *dp, *opendir(); X struct direct *dirptr, *readdir(); X X if(chdir(path)<0) X return; /* can't change to desired directory */ X if((dp=opendir("."))==NULL) X return; /* can't open the directory file itself */ X while((dirptr=readdir(dp))!=NULL) /* end of directory */ X add_entry(pattern,dirptr->d_fileno,dirptr->d_name,dptr); X closedir(dp); X} X#endif BSD X X#ifdef SYSTEMV X/* SYSTEM V Unix version of the read_directory routine. */ read_directory(pattern,path,dptr) char *pattern[],path[]; struct d_entry **dptr; X{ X struct direct d; X int fd; X X if(chdir(path)<0) /* can't change to the desired directory */ X return; X if((fd=open(".",O_RDONLY))<0) /* can't open the directory file */ X return; X while(read(fd,&d,sizeof(struct direct)) == sizeof(struct direct)) X add_entry(pattern,d.d_ino,d.d_name,dptr); /* add file */ X close(fd); X} X#endif SYSTEMV X X/* showpath displays the string in "path" on the top line of the window. X The top line is defined in ulx, ily. Only as much of the path as X can be seen is displayed, the rest is cust out. */ showpath(path,ulx, uly, lrx, lry) char path[]; int ulx, uly, lrx, lry; X{ X int l; X char dummy[80]; X X standout(); /* invert the line */ X move(uly,ulx); /* move to top line of the window */ X if((l=strlen(path))>(lrx-uly-1)) { /* is string too long? */ X /* if string is too long, truncate it. preceed all truncated X paths with "..." to show user that the path was indeed X truncated. */ X sprintf(dummy,"...%c%d.%ds",'%',(lrx-ulx-3),(lrx-ulx-3)); X printw(dummy,path+(l-(lrx-ulx-3))); /* print path */ X } X else /* string is not too long, just print it */ X printw(path); X standend(); /* end inverted mode */ X clrtoeol(); /* get rid of standend char on some types of terminals */ X} X X/* display an error message on the top line of the window, wait for RETURN X key to be pressed so that user can acknowledge the error */ ils_error(msg,ulx,uly,lrx,lry) char msg[]; int ulx, uly, lrx, lry; X{ X int l; X char dummy[80]; X X standout(); /* all errors in inverse mode */ X move(uly,ulx); /* go to top line */ X sprintf(dummy,">> %s << Press RETURN...",msg); X if((l=strlen(dummy))>(lrx-uly-1)) { /* does message need truncation? */ X if(strlen(msg) > (lrx-uly-1)) { X /* truncate the message. Preceed truncated with "..." */ X sprintf(dummy,"...%c%d.%ds",'%',(lrx-ulx-3),(lrx-ulx-3)); X printw(dummy,msg+(l-(lrx-ulx-3))); X X } X else X strcpy(dummy,msg); /* no truncation needed */ X } X printw(dummy); X standend(); /* end inverse mode */ X refresh(); X ils_wait_key(ILS_ENTER); /* wait for RETURN key to be pressed */ X} X X/* wait for key (passed in variable 'c') to be pressed. Keep asking X for more keys until the one waited for is pressed. */ ils_wait_key(c) int c; X{ X while((getch()&0x7f)!=c); X} X X/* edit a directory, using a rectangle in the screen with (ulx, uly) as X the coordinates of the upper-left corner of the rectangle, and X (lrx, lry) as the lower-right corner of the rectangle. title is X a character string that will be printed in the top line of the rectangle, X unless a NULL is passed, in which case nothing is printed, and you X have another line to use as part of the directory. */ X edit_directory(pattern,path,dptr,ulx,uly,lrx,lry,title) char *pattern[],path[]; struct d_entry **dptr; int ulx, uly, lrx, lry; char *title; X{ X static int level=0; X char newpath[80], trailer, *p, line[MAXPATH], this_dir[MAXPATH], X last_active[MAXPATH]; X struct d_entry *cur, *t, *active_list[ILS_MAX_ENTRIES]; X int height, width, widest, entries, i, j, horiz_fit, vert_fit, X x, y, more_line, first_visible, orig_uly, X column_height, cur_entry, state, next_state, c, X last_x, last_y, z, up_x, up_y, down_x, down_y, X done, new_entries, low, high, middle; X X clear(); X raw(); /* need characters AS THEY ARE PRESSED */ X noecho(); /* don't show user's input */ X X orig_uly = uly; X if(title != (char *)NULL) /* is there a title? */ X uly++; /* account for title line */ X uly++; /* account for "current directory" line */ X X height = lry - uly; /* height of window */ X width = lrx - ulx; /* width of window */ X X strcpy(cur_path,path); /* make path globally known */ X X level++; /* count the levels down */ X X state = ILS_INITIAL; X next_state = ILS_INITIAL2; X cur_entry = 0; X X while(1) { /* loop will end with an exit() call */ X switch(state) { X case ILS_INITIAL: X cur = *dptr; /* get all entries in directory into a sorted list */ X widest = 6; /* default longest string length */ X entries = first_visible = 0; X /* loop through and find the longest filename in the list */ X while(cur != (struct d_entry *)NULL) { X if((i=strlen(cur->name)) > widest) X widest = i; /* found a longer string */ X active_list[entries++] = cur; X cur = cur->next; /* get next in list */ X } X /* get number of strings that can fit => how many columns */ X horiz_fit = width / (widest+2); X column_height = entries / horiz_fit + ((entries%horiz_fit)==0? 0:1); X vert_fit = lry-uly; /* how many can fit verticall into window */ X if(vert_fit > column_height) X vert_fit = column_height; /* safeguard */ X X state = next_state; X break; X X case ILS_INITIAL2: X if(!entries) { /* is this an empty directory? */ X showpath(path,ulx,orig_uly+(title==NULL? 0:1),lrx,lry); X ils_error("EMPTY DIRECTORY",ulx,orig_uly,lrx,lry); X level--; X return; /* empty: allow no editing */ X } X state = ILS_TOTAL_DRAW; /* redraw screen */ X break; X X case ILS_FIND_CURSOR: /* locate best place for cursor */ X done = 0; X low = 0; /* base value for binary search */ X high = entries; /* high value for binary search */ X /* binary search to position where the cursor should be */ X while(!done) { /* binary search to the place to put cursor */ X middle = (low+high)/2; X i = strcmp(last_active,active_list[middle]->name); X if(i==0) X done = 1; /* found entry! */ X if(i<0) { X if(middle==high) /* found where to put cursor */ X done=1; X else X high = middle; /* try lower half */ X } X else { X if(middle==low) /* found where to put cursor */ X done=1; X else X low = middle; /* try upper half */ X } X } X /* middle is now equal to entry number where cursor should be */ X cur_entry = middle; X first_visible = 0; /* for now, top line shown is line 0 */ X i = middle % column_height; /* get offset from top of column */ X if(i>=vert_fit) /* is the active entry off the screen? */ X first_visible = i-vert_fit+1;/* YES make it visible */ X if(first_visible<0 || first_visible>=entries || first_visible>=vert_fit) X first_visible = i; /* safeguard */ X if(first_visible >= entries) /* another safeguard */ X first_visible; X state = ILS_INITIAL2; X break; X X case ILS_TOTAL_DRAW: /* redraw the whole window */ X clear(); X if(title != NULL) { /* is there a title to display? */ X move(orig_uly,ulx); X clrtoeol(); X printw("%s",title); /* display the title */ X } X /* display the path either just below the title, or on top */ X showpath(path,ulx,orig_uly+(title==NULL? 0:1),lrx,lry); X case ILS_DRAW: /* show files in directory */ X up_x=up_y=down_x=down_y= -1; X for(i=0,z=first_visible;istat.st_mode & 040000) X trailer='/'; X else if(active_list[z]->stat.st_mode & 0100) X trailer='*'; X } X printw("%s%c",active_list[z]->name,trailer); X X } X } X z+=column_height-vert_fit; X } X if((first_visible+vert_fit) < column_height) { X y=uly+vert_fit-1; X x=1+ulx+(width/horiz_fit)*(horiz_fit-1); X move(y,x); X standout(); X printw("more >"); X standend(); X clrtoeol(); X down_x = x; X down_y = y; X } X X refresh(); X state = ILS_SHOW_CURSOR; X next_state = ILS_KEYBOARD; X break; X case ILS_SHOW_PATH: X showpath(path,ulx,orig_uly+(title==NULL? 0:1),lrx,lry); X case ILS_MOVE_CURSOR: X move(last_y,last_x); X addch(' '); X case ILS_SHOW_CURSOR: X last_y=y=uly + cur_entry%column_height-first_visible; X last_x=x=ulx + (width/horiz_fit) * (cur_entry / column_height); X if((x+1)==up_x && y==up_y) { X first_visible--; X state = ILS_DRAW; X break; X } X if((x+1)==down_x && y==down_y) { X first_visible++; X state = ILS_DRAW; X break; X } X move(y,x); X addch('>'); X move(y,x); X refresh(); X state = next_state; X break; X case ILS_KEYBOARD: X switch((c=(getch()&0x7f))) { X case ILS_ESCAPE: /* go back up or QUIT! */ X if(level==1) X state=ILS_ASCEND; X else X state = ILS_EXIT; X break; X case ILS_EXIT_KEY: /* QUIT NOW! */ X ils_exit(1); X break; X case ILS_ENTER: X case ILS_RETURN: /* descend into a directory */ X if(active_list[cur_entry]->stat.st_mode & 040000) { X if(strcmp(active_list[cur_entry]->name,".")==0) { X flash(); /* change to current directory */ X break; X } X if(strcmp(active_list[cur_entry]->name,"..")==0) { X if(level==1) X state = ILS_ASCEND; X else X state = ILS_EXIT; X break; X } X if(path[1]=='\0') X sprintf(newpath,"/%s",active_list[cur_entry]->name); X else X sprintf(newpath,"%s/%s",path,active_list[cur_entry]->name); X strcpy(last_active,active_list[cur_entry]->name); X if(active_list[cur_entry]->contents == (struct d_entry *)NULL) { X read_directory(pattern,newpath,&(active_list[cur_entry]->contents)); X } X else { X chdir(newpath); X strcpy(cur_path,newpath); X re_read_dir(pattern,&(active_list[cur_entry]->contents)); X } X refresh(); X edit_directory(pattern,newpath,&(active_list[cur_entry]->contents),ulx,orig_uly,lrx,lry,title); X chdir(path); /* back up */ X strcpy(cur_path,path); X state = ILS_INITIAL; X if((new_entries=re_read_dir(pattern,dptr))!=entries) X next_state = ILS_FIND_CURSOR; X else X next_state = ILS_INITIAL2; X } X break; X case ILS_UP: /* move cursor up */ X if(cur_entry==0 || (cur_entry%column_height)==0) X break; /* can't move up one */ X cur_entry--; X y=uly+cur_entry%column_height-first_visible; X if(y=lry) { X state = ILS_DRAW; X first_visible++; X } X else { X state = ILS_MOVE_CURSOR; X next_state = ILS_KEYBOARD; X } X break; X case ILS_RIGHT: /* move cursor right */ X if(cur_entry+column_height >= entries) X break; /* can't move right */ X else { X cur_entry+=column_height; X state = ILS_MOVE_CURSOR; X next_state = ILS_KEYBOARD; X } X break; X case ILS_LEFT: /* move cursor left */ X if(cur_entry-column_height < 0) X break; /* can't move left */ X else { X cur_entry-=column_height; X state = ILS_MOVE_CURSOR; X next_state = ILS_KEYBOARD; X } X break; X case ILS_REDRAW: /* redraw the screen */ X case ILS_REDRAW_ALT: /* alternate character */ X state = ILS_TOTAL_DRAW; X break; X case ILS_ENV: /* display symbol table */ X display_symtab(); X state = ILS_TOTAL_DRAW; X break; X case ILS_FORCE_KEYBOARD: /* force us into keyboard mode */ X default: X if(keybd(c,ulx,orig_uly+(title==NULL? 0:1),lrx,lry,line)) { X strcpy(last_active,active_list[cur_entry]->name); X process_input(c,active_list,cur_entry,entries,line,ulx,orig_uly+(title==NULL? 0:1),lrx); X state = ILS_INITIAL; X if((new_entries=re_read_dir(pattern,dptr))!=entries) X next_state = ILS_FIND_CURSOR; X else X next_state = ILS_INITIAL2; X showpath(path,ulx,orig_uly+(title==NULL? 0:1),lrx,lry); X } X else X state = ILS_SHOW_PATH; X break; X } X break; X case ILS_ASCEND: /* up into a new directory */ X if(path[1]=='\0') { /* at root (/) ? */ X flash(); /* can't go up any higher! */ X state = ILS_KEYBOARD; /* back to input mode */ X break; X } X strcpy(newpath,path); X /* find the first '/' character from the right, then chop the X path from that point on. This is to make a path like: X /usr/local/bin X into X /usr/local X */ X for(i=strlen(path)-1;i && newpath[i]!='/';i--); X if(i==0) { /* i==0 if we are one level down from root */ X strcpy(newpath,"/"); /* make root the new path */ X strcpy(this_dir,&(path[1])); X } X else { X newpath[i]='\0'; /* up one level in the path */ X strcpy(this_dir,&(newpath[i+1])); X } X if(this_dir[0]=='\0') /* make sure not a null string */ X break; /* a little safeguard */ X cur = (struct d_entry *) NULL; /* ready to read a new dir */ X read_directory(pattern,newpath,&cur); /* read it */ X t = cur; /* find the entry in the new directory that X corresponds to this directory's name. */ X strcpy(last_active,this_dir); /* for finding cursor pos */ X for(cur = (*dptr);cur!=(struct d_entry *)NULL;) { X if(strcmp(cur->name,this_dir)==0) { X /* found it. assign contents to this entry */ X cur->contents = (*dptr); X cur = (struct d_entry *) NULL; X } X else /* try next one */ X cur = cur->next; X } X (*dptr) = t; /* make this the new "top of tree" pointer */ X strcpy(cur_path,newpath); /* let globals know */ X strcpy(path,newpath); X cur_entry = first_visible = 0; X state = ILS_INITIAL; /* make this the active one */ X next_state = ILS_FIND_CURSOR; X break; X case ILS_EXIT: /* QUIT! */ X move(last_y,last_x); X addch(' '); /* erase cursor */ X level--; /* back up one level */ X if(level==0) { X noraw(); X echo(); X } X return(1); X break; X } X } X} X X/* add an entry to a directory list. X** pattern is the array of search patterns as passed to ils() X** i is the inode number of this entry X** p is the name of this entry X** place the the pointer to the directory list (of d_entry's) X*/ add_entry(pattern,i,p,place) long i; char *pattern[],*p; struct d_entry **place; X{ X struct d_entry *t, *cur, *last; X int l, match; X struct stat st; X char *ps, *pd; X X if(i==0) /* if inode is 0, file was removed */ X return; X if(stat(p,&st)<0) /* can't stat file, don't add it */ X return; X if(p[0]=='.' && (modes&ILS_ALL)==0) /* is this a "." file? */ X return; /* starts with '.', not in list all mode */ X if(pattern[0]!=(char *)NULL) { /* if patterns are used, see if match */ X for(l=match=0;pattern[l]!=(char *)NULL && match==0;l++) { X if(wildmat(p,pattern[l])) X match++; /* matches a pattern */ X } X if(!match) X return; /* doesn't match any patterns */ X } X X new_entry(&t,p,&st); /* get memory and assign values */ X X if(*place == (struct d_entry *)NULL) /* empty list */ X *place=t; X else { X /* loop through and decide where to place the new entry. X the list is a sorted doubly-linked list. */ X cur = last = *place; X while(cur != (struct d_entry *)NULL) { X if(strcmp(p,cur->name) < 0) { /* insert before */ X if(cur == *place) { /* at top of list */ X t->next = *place; X cur->prev = t; X *place = t; X return; X } X else { /* in middle */ X cur->prev->next = t; X t->prev = cur->prev; X cur->prev = t; X t->next = cur; X return; X } X } X else { /* check next name */ X last = cur; X cur = cur->next; X } X } X if( cur == (struct d_entry *)NULL) { /* insert at end of list */ X last->next = t; X t->prev = last; X } X } X} X X/* get a string from the keyboard, and process it based on defined keysequences. X** c is the first character (as entered in the mailine ils code above. X** if c is not a FORCE_KEYBOARD character, thenn it is inserted into X** the string passed to the string editing routine, as the first character X** of the user's string. X** leftx,upy,rightx,downy are screen coordinates of the window size, X** regarding the location of the upper-left and lower-right corners. X** line is a pointer to a string. This is where the keyboard input will X** be placed and thus passed back to the calling routine. X*/ keybd(c,leftx,upy,rightx,downy,line) int leftx, upy, rightx, downy; char c, line[]; X{ X int l; X X if(c==ILS_FORCE_KEYBOARD) /* is this a FORCED KEY SEQUENCE? */ X line[0]='\0'; X else { /* if legal character, insert into line[] */ X if(c<' ' || c >0x7f) /* out of range */ X return; X line[0]=c; X line[1]='\0'; X } X X if(rightx-leftx-7 < 2) /* see if we can fit the prompt */ X return(0); /* no space on screen for input */ X move(upy,leftx); X printw("Input: "); /* print the prompt */ X l=getst(rightx-leftx-8,leftx+7,upy,line,rightx-leftx-7,&l,ALL_ALPHA,NULL); X if(l==GET_RETURN || l==GET_DOWN) X return(1); X return(0); /* probably ESCAPED out */ X} X X/* free_contents() de-allocates the contents of the directory pointed at X** by p. X*/ free_contents(p) struct d_entry *p; X{ X if(p->next != (struct d_entry *)NULL) /* depth-first deallaocation */ X free_contents(p->next); X if(p->contents != (struct d_entry *)NULL) /* get rid of contents */ X free_contents(p->contents); X free(p); /* get rid of top node */ X} X X/* re_read_dir() takes another look at the contents of a directory for X** changes in the contents or mode bits of the directory members. X** This is used when a directory is re-entered into, and we need to see X** if files were remove, added, or changed in any way. X** X** pattern is the array of search patterns as passed to ils() initially. X** X** dptr is the pointer to the contents of the directory last time it was X** examined. X*/ re_read_dir(pattern,dptr) char *pattern[]; struct d_entry **dptr; X{ X char name[MAXPATH], *pd, *ps; X struct d_entry *tdptr,*t; X int i,inode,r,done,mxloop; X struct stat st; X long touch, time(); X#ifdef BSD /* berkeley directories are different */ X DIR *dp, *opendir(); X struct direct *dirptr, *readdir(); X X if(chdir(cur_path)<0) X return; X if((dp=opendir("."))==NULL) X return; X tdptr = *dptr; X touch = time((long *)0); X while((dirptr=readdir(dp))!=NULL) { X strcpy(name,dirptr->d_name); X inode = dirptr->d_fileno; X#endif BSD X X#ifdef SYSTEMV X struct direct d; X int fd; X X if(chdir(cur_path)<0) X return; X if((fd=open(".",O_RDONLY))<0) X return; X tdptr = *dptr; X touch = time((long *)0); X while(read(fd,&d,sizeof(struct direct)) == sizeof(struct direct)) { X strncpy(name,d.d_name,DIRSIZ); X name[DIRSIZ]='\0'; X inode = d.d_ino; X#endif SYSTEMV X /* common entry-handling code */ X X if(inode) /* if this file wasn't removed, stat it */ X if(stat(name,&st)<0) X continue; /* can't stat? forget this one! */ X if(name[0]=='.' && (modes&ILS_ALL)==0) X continue; /* skip this, not in 'list all' mode */ X done=0; X mxloop=0;/* mxloop is a safeguard. It was used for X initial debugging, and can probably be removed */ X while(!done && mxloop++ < 10000) { X if((r=strcmp(name,tdptr->name))==0) { X if(inode) { /* copy over new stat struct */ X for(i=0, ps=(char *)&st,pd=(char *)&(tdptr->stat);itouched = touch; X } X done=1; /* file found, move on to next */ X } X else if(r<0 && inode) { /* name < tdptr->name insert before? */ X if((tdptr->prev) == (struct d_entry *)NULL) { X /* insert at top of list */ X new_entry(&t,name,&st); X t->touched = touch; X t->next = tdptr; X tdptr->prev = t; X *dptr = t; X done=1; X } X else if(strcmp(name,tdptr->prev->name)>0) { X /* insert above tdptr */ X new_entry(&t,name,&st); X t->touched = touch; X tdptr->prev->next = t; X t->next = tdptr; X t->prev = tdptr->prev; X tdptr->prev = t; X done=1; X } X else X tdptr = tdptr->prev; X } X else if(inode) { /* name > tdptr->name insert after? */ X if((tdptr->next) == (struct d_entry *)NULL) { X /* insert at end of list */ X new_entry(&t,name,&st); X t->touched = touch; X tdptr->next = t; X t->prev = tdptr; X done=1; X } X else if(strcmp(name,tdptr->next->name)<0) { X /* insert just below tdptr */ X new_entry(&t,name,&st); X t->touched = touch; X t->next = tdptr->next; X tdptr->next = t; X t->prev = tdptr; X t->next->prev = t; X done=1; X } X else X tdptr = tdptr->next; X } X else X done=1; X } X } X X /* loop through and remove all entries that have a different touch X if the touch value doesn't match, then file was removed and the X entry in the directory was used by a new filename. */ X tdptr = *dptr; /* start at top of directory */ X while(tdptr != (struct d_entry *) NULL) { X if((tdptr->touched) != touch) { X /* need to remove this entry, see where it's at */ X if(tdptr == (*dptr)) { X /* at the top */ X *dptr = (*dptr)->next; X (*dptr)->prev = (struct d_entry *)NULL; X tdptr->next = (struct d_entry *)NULL; X free_contents(tdptr); X tdptr = *dptr; X } X else if(tdptr->next == (struct d_entry *)NULL) { X /* at the bottom */ X tdptr->prev->next = (struct d_entry *)NULL; X free_contents(tdptr); X tdptr = (struct d_entry *) NULL; /* end */ X } X else { X /* in the middle somewhere */ X t = tdptr->next; X tdptr->prev->next = tdptr->next; X tdptr->next->prev = tdptr->prev; X tdptr->next = (struct d_entry *) NULL; X free_contents(tdptr); X tdptr = t; X } X } X else /* try next entry */ X tdptr = tdptr->next; X } X X#ifdef BSD X closedir(dp); X#endif BSD X#ifdef SYSTEMV X close(fd); X#endif SYSTEMV X} X X/* new_entry allocates memory for a new addition to a directory, then X** copies in the name and stat structure into the newly allocated structure. X** It copies nulls into all of the structure's pointers. X** X** tex is to be assigned to the address of the new memory. X** name is the string to copy into the name field. X** st is a pointer to the stat structure to copy over. X*/ new_entry(tex,name,st) struct d_entry **tex; char *name; struct stat *st; X{ X struct d_entry *t; X register int i; X char *pd, *ps; X X if((*tex=t= (struct d_entry *) malloc(sizeof(struct d_entry)))==NULL) { X clear(); X printw("OUT OF MEMORY!\n"); X refresh(); X ils_exit(1); X } X t->name = (char *)malloc(strlen(name)+1); /* alloc space for name */ X strcpy(t->name,name); /* copy name over */ X X for(i=0, ps=(char *)st,pd=(char *)&(t->stat);inext = (struct d_entry *) NULL; /* null out all pointers */ X t->prev = (struct d_entry *) NULL; X t->contents = (struct d_entry *) NULL; X} X X/* exit ils gracefully with 'code' as the exit code */ ils_exit(code) int code; X{ X noraw(); /* out of raw mode */ X echo(); /* echo back on */ X endwin(); /* exit curses */ X exit(code); X} END_OF_FILE if test 25940 -ne `wc -c <'ils.c'`; then echo shar: \"'ils.c'\" unpacked with wrong size! fi # end of 'ils.c' fi if test -f 'ils.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'ils.h'\" else echo shar: Extracting \"'ils.h'\" \(3354 characters\) sed "s/^X//" >'ils.h' <<'END_OF_FILE' X/* includes for ils program */ X#ifndef ushort X#include X#endif X X#ifndef stat_h X#include X#endif X X#define ILS_MAX_ENTRIES 1000 X#define ILS_MAX_PATS 256 /* max search patterns */ X#define MAX_SYMBOLS 256 /* max symbol table size */ X X/* types of display modes (can be ored if appropriate) */ X#define ILS_LONG 1 /* long format */ X#define ILS_ALL 8 /* list ALL files (even . and ..) */ X#define ILS_F_TYPE 0x10 /* directories end with '/', executables '*' */ X#define ILS_ECHO_COMS 0x20 /* echo commands as executed */ X X#define ILS_ESCAPE 0x1b /* hard-coded ESCAPE key */ X#define ILS_ENTER '\n' /* hard-coded RETURN oe ENTER key */ X#define ILS_RETURN '\r' /* hard-coded RETURN or ENTER key */ X#define ILS_DOWN 'j' /* hard-coded DOWN */ X#define ILS_UP 'k' /* hard-coded UP */ X#define ILS_LEFT 'h' /* hard-coded LEFT */ X#define ILS_RIGHT 'l' /* hard-coded RIGHT */ X#define ILS_REDRAW 18 /* control-r */ X#define ILS_REDRAW_ALT 12 /* control-l */ X#define ILS_ENV '?' /* display symbol table */ X#define ILS_FORCE_KEYBOARD ':' /* force keyboard input */ X#define ILS_EXIT_KEY 4 /* control-d */ X X#define ILS_FILE ".ils" /* ils definition file (per user) */ X X#define ILS_INITIAL -3 /* initial state (only done once) */ X#define ILS_INITIAL2 -2 X#define ILS_TOTAL_DRAW -1 /* redraw path, title, and contents */ X#define ILS_DRAW 0 /* display directory content */ X#define ILS_KEYBOARD 1 /* get input from keyboard */ X#define ILS_MOVE_CURSOR 2 /* erase last cursor, show new */ X#define ILS_SHOW_CURSOR 3 /* redraw the cursor */ X#define ILS_EXIT 4 /* exit this level of edit_directory */ X#define ILS_SHOW_PATH 5 /* print the path on the top line */ X#define ILS_ASCEND 6 /* go up into a new directory */ X#define ILS_FIND_CURSOR 7 /* figure out best place to put cursor */ X X#ifdef BSD X#define flash() addch('\007') X#endif X X#define ILS_METACHAR '$' /* Preceeds all ils variables */ X#define META1 '/' /* List of sh metacharacters that can be used */ X#define META2 '<' /* as delimeters. */ X#define META3 '>' X#define META4 '(' X#define META5 ')' X#define META6 '[' X#define META7 ']' X#define META8 '&' X#define META9 '|' X#define META10 '`' X#define META11 '\"' X#define META12 '!' X#define META13 '~' X#define META14 '$' X#define META15 '\'' X#define META16 '?' X#define META17 ';' X#define META18 '.' X X#define ILS_MAX_ARGS 256 /* max arguments per single command line */ X#define MAXPATH 128 X#define PWD "/bin/pwd" X X#define BOTTOM 0 X#define TOP 1 X typedef struct d_entry { X char *name; X int inode; X struct stat stat; /* stat for this file touched */ X struct d_entry *next; /* next entry in this directory */ X struct d_entry *prev; /* previous entry in this directory */ X struct d_entry *contents; /* if a directory, it's contents */ X long touched; /* flag for last time touched */ X}; X typedef struct value { X char *v; X struct value *next; X}; X X#define ILS_KEY_SEQUENCE 1 X#define ILS_VARIABLE 2 X typedef struct symbol { X char *name; /* name of this symbol */ X int type; /* symbol type */ X struct value *val; /* pointer to symbol value */ X}; X X#define CURRENT_FILE "file" /* insert this file */ X#define ALL_FILES "all_files" /* insert all files in directory */ X#define INPUT "input" /* input from keyboard command */ X#define PREFIX "prefix" /* insert the prefix (before .) */ X#define PWD_PATH "path" /* insert the path (ala `pwd`) */ END_OF_FILE if test 3354 -ne `wc -c <'ils.h'`; then echo shar: \"'ils.h'\" unpacked with wrong size! fi # end of 'ils.h' fi if test -f 'input.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'input.c'\" else echo shar: Extracting \"'input.c'\" \(5006 characters\) sed "s/^X//" >'input.c' <<'END_OF_FILE' X#include X#include "ils.h" X#include "get.h" X extern int syms; /* number of symbols on symbol table */ extern struct symbol *symtab[]; extern char *pnam; extern int a_num; /* number of arguments in list */ extern int a_ind; /* index into current argument */ extern char *args[ILS_MAX_ARGS]; extern char cur_path[]; /* pointer to active path */ extern int modes; /* what special flags might be set */ X process_input(c,list,entry,entries,p,x,y,rx) struct d_entry *list[]; int entry, entries,x,y,rx; char p[],c; X{ X struct value *v; X char line[256]; X X int i=0; X X if(c==ILS_FORCE_KEYBOARD) { X evaluate_line(line,p,list,entry,entries,x,y,rx); X ils_doit(line); /* execute the statement */ X } X else { X for(i=0;iname,p)==0){/* found the command */ X perform_command(!c,symtab[i]->val,list,entry,entries,x,y,rx); X break; /* get out of loop */ X } X if(i==syms) X flash(); X } X} X perform_command(c,v,list,entry,entries,x,y,rx) struct value *v; struct d_entry *list[]; int entry, entries,x,y,rx; X{ X char line[256]; X int i; X X while(v != (struct value *) NULL) { X evaluate_line(line,v->v,list,entry,entries,x,y,rx); X ils_doit(line); /* execute the statement */ X v = v->next; X } X dealloc(args); X} X ils_doit(line) char *line; X{ X noraw(); X echo(); X clear(); X if(modes & ILS_ECHO_COMS) X printw("%s\n",line); X refresh(); X#ifdef BSD X endwin(); X#endif X X system(line); /* execute the statement */ X#ifdef BSD X printf("PRESS RETURN TO RETURN TO %s\n",pnam); X getchar(); X initscr(); X scrollok(stdscr,TRUE); X raw(); X noecho(); X#endif X X#ifdef SYSTEMV X raw(); X noecho(); X standout(); X printw("Press any key to return to %s",pnam); X standend(); X refresh(); X getch(); X#endif SYSTEMV X} X evaluate_line(line,s,list,entry,entries,x,y,rx) char s[], line[]; struct d_entry *list[]; int entry, entries,x,y,rx; X{ X char c, lable[160], arg[160], new_line[256]; X register int k; X int l, a=0, j, white=0, d, ln=0, i, line_ind=0; X X l = strlen(s); X for(i=0;iname); X else if(strcmp(lable,PREFIX)==0) { X for(i=0;iname) && list[entry]->name[i]!='.';i++) X line[i] = list[entry]->name[i]; X line[i]='\0'; X } X else if(strcmp(lable,PWD_PATH)==0) { X for(i=0;cur_path[i]!='\0';i++) X line[i] = cur_path[i]; X line[i]='\0'; X } X else if(strcmp(lable,INPUT)==0) { X (*ii)++; X for(parens=1,k=0;parens && s[*ii]!='\0';(*ii)++) X switch(s[*ii]) { X case ')': X parens--; X if(parens) X prompt[k++]=s[*ii]; X break; X case '(': X parens++; X default: X prompt[k++]=s[*ii]; X break; X } X if(parens) X return(1); X prompt[k]='\0'; X X move(y,x); X X evaluate_line(new_prompt,prompt,list,entry,entries,x,y,rx); X l=strlen(new_prompt); X printw(new_prompt); X line[0]='\0'; X if(getst(rx-x-l-1,x+l,y,line,rx-x-l,&l,ALL_ALPHA,NULL)!=GET_RETURN) X return(1); X } X#ifdef SYSTEMV X else if((c=(char *)getenv(lable))!=NULL) X strcpy(line,c); X#endif SYSTEMV X} X X/* add to the argument list */ X/* c is for passing a single character, str is for passing a string, X * type==0 for char, 1 for string */ add_to_args(c,str,type) char c, str[]; int type; X{ X int l,i; X X if(type) { X l=strlen(str); X for(i=0;i254) X line_ind=254; X break; X } X} END_OF_FILE if test 5006 -ne `wc -c <'input.c'`; then echo shar: \"'input.c'\" unpacked with wrong size! fi # end of 'input.c' fi if test -f 'main.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'main.c'\" else echo shar: Extracting \"'main.c'\" \(2923 characters\) sed "s/^X//" >'main.c' <<'END_OF_FILE' X/************************************************************************** X** main() routine for ils. X** X** ils (c) copyright 1991 by Jack Alexander X** X** NO WARRANTY OF ANY KIND IS ASSOCIATED WITH THIS PROGRAM, NOT EVEN X** FOR FITNESS OF PURPOSE X** X**/ X#include X#include X#include "ils.h" X char *pnam; char *patterns[ILS_MAX_PATS]; X main(argc,argv) int argc; char *argv[]; X{ X int rows=0, cols=0, i, j, pats, modes=0,errs=0,skip,l; X char curdir[MAXPATH], *title, c; X X pnam = argv[0]; /* globnal pointer to program name */ X title = (char *) NULL; /* no title, yet */ X X /* extract any modes that may be part of a default dure to filename */ X switch(argv[0][strlen(argv[0])-1]) { X case 'a': X modes |= ILS_ALL; X break; X case 'f': X modes |= ILS_F_TYPE; X break; X case 'l': X modes |= ILS_LONG; X break; X } X for(i=1,pats=0;i='0' && c<='9' && j='0' && c<='9' && j=argc) { X fprintf(stderr,"%s: -T option must be followed with a title\n",pnam); X fprintf(stderr," example: %s -T \"This is a title\"\n",pnam); X errs++; X } X else { X title = argv[i+1]; X skip=1; X } X break; X } X } X break; X default: X patterns[pats++] = argv[i]; X } X i+=skip; X skip=0; X } X if(errs) { X fprintf(stderr,"%s: errors in parameters\n",pnam); X exit(0); X } X patterns[pats] = (char *) NULL; X X initscr(); /* init curses */ X scrollok(stdscr,TRUE); X if(!rows) X rows = LINES; X if(!cols) X cols = COLS; X X if(get_curdir(curdir)) { X endwin(); X exit(0); X } X ils(patterns,curdir,0,0,cols,rows,modes,title); X#ifdef BSD X clear(); X printw("Leaving %s...\n",pnam); X refresh(); X#endif X endwin(); /* end curses */ X} X get_curdir(path) char path[]; X{ X FILE *fp; X X if((fp=popen(PWD,"r"))==NULL) { X fprintf(stderr,"%s: popen returns error\n",pnam); X return(1); X }; X fscanf(fp,"%s",path); X pclose(fp); X return(0); X} END_OF_FILE if test 2923 -ne `wc -c <'main.c'`; then echo shar: \"'main.c'\" unpacked with wrong size! fi # end of 'main.c' fi if test -f 'parse.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'parse.c'\" else echo shar: Extracting \"'parse.c'\" \(3638 characters\) sed "s/^X//" >'parse.c' <<'END_OF_FILE' X#include X#include X#include "ils.h" X X#define NUETRAL 0 /* states used in parse */ X#define IN_OP 1 X extern char *pnam; /* program name */ extern struct symbol *symtab[]; /* symbol table */ extern int syms; /* number of symbols on table */ X parse_line(line,lnum) char line[]; int lnum; X{ X static int state=NUETRAL,o,no; X char ops[2][160]; X X o=get_opts(ops,line); /* Get the 2 possible parts */ X X if(!o) X return(0); /* line was just white space */ X switch(state) { X case NUETRAL: X if(o==1) { X printw("%s: error on line %d of '%s' file\n",pnam,lnum,ILS_FILE); X refresh(); X return(1); X } X no=new_op(ops,ILS_KEY_SEQUENCE); X state=IN_OP; X break; X case IN_OP: X if(o==2) X no=new_op(ops,ILS_KEY_SEQUENCE); X else X add_op(no,ops); X break; X default: X printw("%s: bad state in parse_line()\n",pnam); X refresh(); X return(1); X break; X } X return(0); X} X X#define ESC 1 X#define COLON 2 X#define NEWLINE 3 X#define SKIP_WHITE 4 X get_opts(s,line) char s[2][160], line[]; X{ X int i,j,k,l,nonwhite=0,state=0,colons=0; X char c; X X l=strlen(line); X s[0][0]=s[1][0]='\0'; X X for(i=j=k=0;iname = (char *)malloc(strlen(s[0])+1); X strcpy(t->name,s[0]); X t->type = type; X X if(s[1][0]=='\0') X t->val = (struct value *)NULL; X else { X v = t->val = (struct value *) malloc(sizeof(struct value)); X v->v = (char *)malloc(strlen(s[1])+1); X strcpy(v->v,s[1]); X v->next =(struct value *) NULL; X } X X return(syms++); X} X add_op(op,s) int op; char s[2][160]; X{ X struct value *v; X X v = symtab[op]->val; X X if(v==(struct value *)NULL) { X v = symtab[op]->val = (struct value *) malloc(sizeof(struct value)); X v->next =(struct value *) NULL; X } X else { X while(v->next != (struct value *)NULL) X v = v->next; X v->next = (struct value *) malloc(sizeof(struct value)); X v->next->next = (struct value *) NULL; X v = v->next; X } X v->v = (char *)malloc(strlen(s[0])+1); X strcpy(v->v,s[0]); X} X display_symtab() X{ X int i, over=0; X struct value *v; X X for(i=0;iname); X standend(); X v = symtab[i]->val; X while(v!=NULL && over++ != MAX_SYMBOLS) { X printw(" %s\n",v->v); X v = v->next; X } X standout(); X printw("Press RETURN next symbol, %c for previous, ESC to exit", X ILS_LEFT); X standend(); X refresh(); X switch(getch()&0x7f) { X case ILS_ENTER: X case ILS_RETURN: X break; X case ILS_LEFT: X if(i!=0) X i -= 2; X else X i--; X break; X case ILS_ESCAPE: X return; X } X } X} END_OF_FILE if test 3638 -ne `wc -c <'parse.c'`; then echo shar: \"'parse.c'\" unpacked with wrong size! fi # end of 'parse.c' fi if test -f 'wildmat.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'wildmat.c'\" else echo shar: Extracting \"'wildmat.c'\" \(2677 characters\) sed "s/^X//" >'wildmat.c' <<'END_OF_FILE' X/* X** Do shell-style pattern matching for ?, \, [], and * characters. X** Might not be robust in face of malformed patterns; e.g., "foo[a-" X** could cause a segmentation violation. It is 8bit clean. X** X** Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986. X** Special thanks to Lars Mathiesen for the ABORT code. This can greatly X** speed up failing wildcard patterns. For example: X** pattern: -*-*-*-*-*-*-12-*-*-*-m-*-*-* X** text 1: -adobe-courier-bold-o-normal--12-120-75-75-m-70-iso8859-1 X** text 2: -adobe-courier-bold-o-normal--12-120-75-75-p-70-iso8859-1 X** Text 1 matches with 51 calls, while text 2 fails with 54 calls. Without X** the ABORT, then it takes 22310 calls to fail. Ugh. X*/ X X#define TRUE 1 X#define FALSE 0 X#define ABORT -1 X static int Star(s, p) X register char *s; X register char *p; X{ X while (wildmat(s, p) == FALSE) X if (*++s == '\0') X return ABORT; X return TRUE; X} X X static int DoMatch(s, p) X register char *s; X register char *p; X{ X register int last; X register int matched; X register int reverse; X X for ( ; *p; s++, p++) { X if (*s == '\0') X return ABORT; X switch (*p) { X case '\\': X /* Literal match with following character. */ X p++; X /* FALLTHROUGH */ X default: X if (*s != *p) X return FALSE; X continue; X case '?': X /* Match anything. */ X continue; X case '*': X /* Trailing star matches everything. */ X return *++p ? Star(s, p) : TRUE; X case '[': X /* [^....] means inverse character class. */ X if (reverse = p[1] == '^') X p++; X for (last = 0400, matched = FALSE; *++p && *p != ']'; last = *p) X /* This next line requires a good C compiler. */ X if (*p == '-' ? *s <= *++p && *s >= last : *s == *p) X matched = TRUE; X if (matched == reverse) X return FALSE; X continue; X } X } X X return *s == '\0'; X} X X int wildmat(s, p) X char *s; X char *p; X{ X return DoMatch(s, p) == TRUE; X} X X X X#ifdef TEST X#include X X/* Yes, we use gets not fgets. Sue me. */ extern char *gets(); X X main() X{ X char pattern[80]; X char text[80]; X X printf("Wildmat tester. Enter pattern, then strings to test.\n"); X printf("A blank line gets prompts for a new pattern; a blank pattern\n"); X printf("exits the program.\n\n"); X X for ( ; ; ) { X printf("Enter pattern: "); X if (gets(pattern) == NULL) X break; X for ( ; ; ) { X printf("Enter text: "); X if (gets(text) == NULL) X exit(0); X if (text[0] == '\0') X /* Blank line; go back and get a new pattern. */ X break; X printf(" %s\n", wildmat(text, pattern) ? "YES" : "NO"); X } X } X X exit(0); X /* NOTREACHED */ X} X#endif /* TEST */ END_OF_FILE if test 2677 -ne `wc -c <'wildmat.c'`; then echo shar: \"'wildmat.c'\" unpacked with wrong size! fi # end of 'wildmat.c' fi echo shar: End of shell archive. exit 0