Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Posting-Version: version B 2.10.2 9/5/84; site umich.UUCP Path: utzoo!watmath!clyde!burl!ulysses!gamma!epsilon!mb2c!umich!doug From: doug@umich.UUCP (Douglas Orr) Newsgroups: net.sources Subject: shell for MS-DOS (part 2) Message-ID: <203@umich.UUCP> Date: Sun, 4-Aug-85 14:14:26 EDT Article-I.D.: umich.203 Posted: Sun Aug 4 14:14:26 1985 Date-Received: Tue, 6-Aug-85 12:18:52 EDT Distribution: net Organization: University of Michigan, EECS Dept., Ann Arbor, MI Lines: 2440 Here is part 2 of the Unix-like shell for DOS. This part contains the actual Microsoft C sources for the program. -Doug {ihnp4,mb2c,pur-ee}!umich!textset!doug doug%textset@umich.csnet # This is a shell archive. # Remove everything above and including the cut line. # Then run the rest of the file through sh. -----cut here-----cut here-----cut here-----cut here----- #!/bin/sh # shar: Shell Archiver # Run the following text with /bin/sh to create: # sh.c # cmds.c # builtin.c # wildcard.c # gen.h # sh.h # This archive created: Sun Aug 4 14:03:53 1985 cat << \SHAR_EOF > sh.c #include #include #include #include #include #include "gen.h" #include "sh.h" Public char * * dissect(); Public char * smalloc(); Local bool escflg = True; /* interpret \ as "escape" character */ /* * sh: a Unix(tm) like command interpreter for MS-DOS. * * by Douglas Orr * Textset Inc. * Ann Arbor, Michigan * * Copyright (c) 1985 Textset. All rights reserved. * * This program may be freely distributed, but not sold for profit. * */ main(argc,argv,envp) int argc; char * argv[]; char * envp[]; { /* arguments processing */ while( --argc ) if( (*++argv)[0] == '-' ) { switch( (*argv)[1] ) { case 'e': escflg = !escflg; break; default: fprintf( stderr, "sh: invalid argument %s\n", *argv ); break; } } /* real stuff */ init(); sh(); } /* Environment Stuff */ /* * for now, just use the uSoft environment stuff. This could probably * be improved */ char * l_getenv( name ) char * name; { return( getenv(name) ); } l_setenv( name, val ) char * name; char * val; { char * str; str = smalloc( strlen(name) + strlen(val) + 2 ); strcpy( str, name ); strcat( str, "=" ); strcat( str, val ); return( putenv( str ) ); } l_printenv() { char * * ep; putchar( '\n' ); for( ep=environ; *ep; ep++ ) printf( "%s\n", *ep ); putchar( '\n' ); return( 0 ); } init() { char * path; /* ignore interrupts */ signal( SIGINT, SIG_IGN ); /* default values */ if( (path = l_getenv( "PATH" )) == NULL ) { l_setenv( "PATH", "\;\bin" ); path = l_getenv( "PATH" ); } setbuf( stdin, NULL ); setbuf( stdout, NULL ); setbuf( stderr, NULL ); rehash( path ); /* read in the profile */ if( access( "/profile", F_ROK ) == 0 ) source( "/profile" ); /* set important variables */ if( l_getenv( "PROMPT" ) == NULL ) l_setenv( "PROMPT", "% " ); if( l_getenv( "PS1" ) == NULL ) l_setenv( "PS1", "> " ); } /* * iiiiit's SHOWTIME */ sh() { char buf[256]; bool intr; while( !feof(stdin) ) { /* prompt */ if( Interactive() ) fprintf( stderr, "%s", l_getenv("PROMPT") ); /* input */ if( getbuf( buf, sizeof buf, stdin ) ) { /* execute */ (void)sys( buf, sizeof buf ); } } } /* * Do initial input processing. Turn the high bit on to * prevent escaped characters and special characters within single * quotes from being interpreted */ #define ChNorm (0) #define ChDquote (1) #define ChSquote (2) #define Escape(x) (x|0x80) getbuf( buf, bufsize, fd ) char * buf; int bufsize; FILE * fd; { char * ptr = buf; int ch_state; Reg int ch; ch_state = ChNorm; while( (ch = getc(fd)) != EOF ) { if( (ch == '\\') && escflg ) ch = Escape(getc(fd)); else if( ch == '\'' ) { if( ch_state == ChSquote ) ch_state = ChNorm; else if( ch_state == ChNorm ) ch_state = ChSquote; else /* state == dquote */ ch = Escape(ch); } else if( ch == '"' ) { if( ch_state == ChDquote ) ch_state = ChNorm; else if( ch_state == ChNorm ) ch_state = ChDquote; else /* state == squote */ ch = Escape(ch); } /* ?! include other wildcards here, as added */ if( ((ch_state == ChSquote) && ((ch == '$') || (ch == '*'))) || ((ch_state == ChDquote) && (ch == '*')) ) ch = Escape(ch); *ptr++ = ch; if( ptr-buf >= bufsize-1 ) break; /* * !! This isn't exactly how Unix does quote processing ... * my current opinion is that it's not worth the trouble */ if( ch == '\n' ) { if( ch_state == ChNorm ) break; else if( Interactive() ) fprintf( stderr, "%s ", l_getenv( "PS1" ) ); } } *ptr = '\0'; /* eof */ if( ptr == buf ) return( False ); /* screw up */ if( ch_state != ChNorm ) { fprintf( stderr, "error: mismatched quotes\n" ); return( False ); } return( True ); } /* * Keep a stack of fds. Push our current I/O environment before * executing commands so that we have someplace to go back to after * I/O redirections */ #define FMax (20) Local int fdstk[FMax]; Local int ftop = 0; push_fd(fd) int fd; { if( ftop >= FMax ) return( -1 ); else fdstk[ftop++] = fd; } pop_fd() { if( ftop == 0 ) return( -1 ); else return( fdstk[--ftop] ); } save_fds() { push_fd( dup(0) ); push_fd( dup(1) ); push_fd( dup(2) ); } restore_fds() { int fd; fflush(stderr); fflush(stdout); dup2( (fd = pop_fd()), 2 ); close(fd); dup2( (fd = pop_fd()), 1 ); close(fd); dup2( (fd = pop_fd()), 0 ); close(fd); setbuf( stdin, NULL ); clearerr(stdin); clearerr(stdout); clearerr(stderr); } /* * Use as source input the indicated file */ source( file ) char * file; { int fd; if( (fd = open( file, 0 )) >= 0 ) { /* save current input */ if( push_fd( dup(0) ) == -1 ) { fprintf( stderr, "source files nested too deeply\n" ); return; } dup2( fd, 0 ); close(fd); /* loop on new input */ sh(); /* restore old input */ dup2( (fd = pop_fd()), 0 ); clearerr(stdin); close(fd); } else perror( file ); } /* * execute the given command */ sys( pgm, pgmlen ) char * pgm; int pgmlen; { int rc; char * * argv; /* set up I/O environment */ save_fds(); /* slice into an argv */ if( (argv = dissect( pgm, pgmlen )) ) { /* doit */ rc = cmd( argv, environ ); /* !! Assertion: all args are malloc'd */ while( *argv ) free( *argv++ ); } /* restore */ restore_fds(); return( rc ); } SHAR_EOF cat << \SHAR_EOF > cmds.c /* yow */ #include #include #include #include #include #include #include #include #include #include #include #include "gen.h" #include "sh.h" /* * sh: a Unix(tm) like command interpreter for MS-DOS. * * by Douglas Orr * Textset Inc. * Ann Arbor, Michigan * * Copyright (c) 1985 Textset. All rights reserved. * * This program may be freely distributed, but not sold for profit. * */ /* declare built-in functions */ Public b_ls(), b_echo(), b_pushd(), b_popd(); Public b_pwd(), b_cd(), b_mkdir(), b_rmdir(), b_rm(); Public b_history(), b_exit(), b_dirs(), b_rehash(); Public b_set(), b_fgrep(), b_source(); Public int fcmp(); /* disk transfer area */ typedef union dta { struct { char dt_dos[21]; #define DosSub (0x10) #define DosHid (0x02) byte dt_attr; short dt_time; short dt_date; unsigned short dt_size_low; unsigned short dt_size_high; char dt_name[13]; } d; short buf[128]; } Dta; Local Dta dta; /* * give us an easily addressable disk transfer area */ set_dta() { union REGS regs; union SREGS segregs; Dta far * dta_addr = &dta; regs.h.ah = 0x1a; /* set dta */ regs.x.dx = FP_OFF(dta_addr); segregs.ds = FP_SEG(dta_addr); intdosx( ®s, ®s, &segregs ); } /* * set our disk transfer area and match the first file in the given * path. * !! Warning: uSoft routines that do disk accesses reset the * disk transfer area. Don't call in between directory reads * examples: open, stat, etc. * * ?! add flags to allow optionally getting hidden/volume file names */ char * open_dir( ptr ) char * ptr; { char far * fptr; union REGS regs; union SREGS segregs; set_dta(); fptr = ptr; regs.h.ah = 0x4e; /* find first entry */ regs.x.dx = FP_OFF(fptr); segregs.ds = FP_SEG(fptr); regs.x.cx = DosSub; /* look for normal files & subdirectories*/ intdosx( ®s, ®s, &segregs ); if( regs.x.cflag ) return( NULL ); else /* name of the current file in the dta */ return( dta.d.dt_name ); } char * nxt_entry( ptr ) char * ptr; { union REGS regs; regs.h.ah = 0x4f; /* find next entry */ regs.x.cx = DosSub; /* look for normal files & subdirs */ intdos( ®s, ®s ); if( regs.x.cflag ) return( NULL ); else return( dta.d.dt_name ); } /* extract fields from dta */ dta_mode() { char * ptr; int mode = 0; if( (ptr = strrchr( dta.d.dt_name, '.' )) ) { if( (strcmpi( ptr, ".bat" ) == 0) || (strcmpi( ptr, ".exe" ) == 0) || (strcmpi( ptr, ".com" ) == 0) ) mode |= S_IEXEC; } if( dta.d.dt_attr & DosSub ) mode |= S_IFDIR; return( mode ); } /* * return most of the time info provided in struct tm */ struct tm * dta_time() { Local struct tm dta_time; dta_time.tm_hour = (dta.d.dt_time >> 11) & 0x1f; dta_time.tm_min = (dta.d.dt_time >> 5) & 0x3f; dta_time.tm_sec = (dta.d.dt_time & 0x1f) * 2; dta_time.tm_year = ((dta.d.dt_date >> 9) & 0x7f) + 80; dta_time.tm_mon = ((dta.d.dt_date >> 5) & 0xf); dta_time.tm_mday = (dta.d.dt_date & 0x1f); if( dta_time.tm_mon > 12 || dta_time.tm_mon < 1 ) dta_time.tm_mon = 0; return( &dta_time ); } long dta_size() { return( (((long)dta.d.dt_size_high) << 16) + dta.d.dt_size_low ); } /* "disk" operations */ /* * change the current disk */ chdsk( disk ) int disk; { union REGS regs; if( isupper(disk) ) disk -= 'A'; else disk -= 'a'; if( disk < 0 || disk > 25 ) { fprintf( stderr, "invalid drive\n" ); return( -1 ); } regs.h.ah = 0x0e; /* change disk */ regs.h.dl = disk; /* to this value */ intdos( ®s, ®s ); return( 0 ); } bool isdev( disk ) char * disk; { return( (strlen(disk) == 2) && (disk[1] == ':') ); } getcdsk() { union REGS regs; regs.h.ah = 0x19; /* get current disk */ intdos( ®s, ®s ); return( regs.h.al + 'a' ); } #define MaxCmdLen (15) #define Cm_Builtin (0x01) #define Cm_Batch (0x02) typedef struct cmds { char cm_name[MaxCmdLen]; char * cm_path; short cm_flags; int (* cm_rtne)(); } Cmds; #define MaxCmds (256) Local Cmds cmds[MaxCmds]; /* built-in commands */ Local struct bi { char * bi_name; int (* bi_rtne)(); } builtins[] = { "ls", b_ls, "echo", b_echo, "cd", b_cd, "pwd", b_pwd, "pushd", b_pushd, "pd", b_pushd, "popd", b_popd, "rm", b_rm, "mkdir", b_mkdir, "rmdir", b_rmdir, "history", b_history, "exit", b_exit, "dirs", b_dirs, "rehash", b_rehash, "set", b_set, "fgrep", b_fgrep, /* msdos sucks */ "source", b_source, /* just like on MTS */ NULL, NULL, }; Local int cmdcnt = 0; /* Command Parsing Stuff */ add_cmd( cmd, path, flags, rtne ) char * cmd; char * path; short flags; int (* rtne)(); { if( cmdcnt >= MaxCmds ) { fprintf( stderr, "error: hash table overflow\n" ); return; } /* ?! make this into a real hash table, eventually */ strcpy( cmds[cmdcnt].cm_name, cmd ); if( path ) cmds[cmdcnt].cm_path = strdup(path); else cmds[cmdcnt].cm_path = NULL; cmds[cmdcnt].cm_flags = flags; cmds[cmdcnt].cm_rtne = rtne; cmdcnt++; } /* * ... ok ... so we're not really hashing anything */ rehash( path ) char * path; { char mypath[128]; char buf[128]; char cbuf[128]; Reg char * pptr; char * ptr; char * extptr; char * cur_path; int flags; int i; /* ?! make this into a real hash table, eventually */ for( i=0; i= MaxArgs ) { fprintf( stderr, "%d: too many arguments\n", av-argv ); goto bad_end; } if( ptr[0] == '.' && pat[0] != '.' ) continue; strcpy( endpat, ptr ); *av++ = strdup(tmp); } } *av = NULL; if( cwd ) { chdir( cwd ); free( cwd ); } /* sort the wildcarded arguments arguments */ /* !! this can overflow your stack if you are unlucky */ wargc = av - (argv+argc); if( wargc > 1 ) qsort( (char *)(argv+argc), wargc, sizeof(char *), fcmp ); if( dev != -1 ) chdsk(dev); return( wargc ); /* he was a bad boy, and he came to a bad end */ bad_end: if( dev != -1 ) chdsk(dev); return( 0 ); } /* perform I/O redirections */ do_redirect( argc, argv ) int argc; char * * argv; { int i, j; int skip; char * file; char * mode; FILE * fd; int direct; int mod; /* * moderate UGLY ALERT */ for( i=2; i') || (*file == '&') ) mod = *file++; /* is file name in the second argument? */ if( *file == '\0' ) { if( i == argc-1 ) return( -1 ); skip=2; file = argv[i+1]; } if( direct == '<' ) { mode = "r"; fd = stdin; } else { mode = "w"; fd = stdout; if( mod == '>' ) mode = "a"; } if( freopen( file, mode, fd ) == NULL ) return( -1 ); /* >& gets stderr & stdout */ if( mod == '&' ) dup2( 1, 2 ); /* UGLY part */ for( j=i; j+skip <= argc ; j++ ) argv[j] = argv[j+skip]; argc -= skip; } else i++; } return( argc ); } /* * perform history substitutions */ /* ?! add :modifiers, and ^^^ substitutions */ #define HistMax (50) short hindex[HistMax]; char * history[HistMax] = { NULL, }; short hind = 0; short hmax = HistMax; short hcount = 0; sub_hist( buf, bufsize ) char * buf; int bufsize; { char * hptr; char * hend; char * sub = NULL; int ind; int hval; char tmpchr; int i; char * to, * from; int sublen; int cmdlen; for( hptr=buf; (hptr = strchr( hptr, '!' )); ) { sub = NULL; ind = hind-1; if( ind < 0 ) ind = hmax-1; if( *(hptr+1) == '!' ) hend = hptr+2; else { for( hend=hptr+1; *hend && !(isspace(*hend) || ispunct(*hend)); hend++) /* find end of history spec */; } /* case !! */ if( *(hptr+1) == '!' ) { /* !! -> insert previous command */ if( (sub = history[ind]) == NULL ) goto noevent; } else /* case !num */ if( isdigit(*(hptr+1)) ) { /* !## -> insert command ## */ tmpchr = *hend; *hend = '\0'; hval = atoi( hptr+1 ); *hend = tmpchr; for( i=0; i insert history string starting with "str" */ { for( i=0; i bufsize ) { fprintf( stderr, "event too large\n" ); return( False ); } /* make a null terminated string out of this */ tmpchr = *hend; *hend = '\0'; if( cmdlen - (hend-hptr) + sublen > bufsize ) { fprintf( stderr, "substitution too large\n" ); return( False ); } /* do the substitution */ insert(hptr,hend-hptr,(buf+cmdlen)-hptr,sub,sublen); /* replace the original character */ *(hptr+sublen) = tmpchr; } } /* retire old buffer if we have wrapped around */ if( history[hind] ) free( history[hind] ); /* record this buffer for future histories */ hindex[hind] = ++hcount; history[hind++] = astrdup( buf ); if( hind >= hmax ) hind = 0; /* echo substituted string */ if( sub ) fprintf( stderr, "%s\n", buf ); return( True ); } /* UGLY ALERT */ insert( old, oldlen, totlen, new, newlen ) char * old; /* history string */ int oldlen; /* size of history string */ int totlen; /* length from start of history to end of command */ char * new; /* replacement string */ int newlen; /* size of replacement string */ { Reg char * from, * to; int i; /* do the substitution */ if( newlen < oldlen ) { /* shift left */ to = old+newlen; from = old+oldlen; while( (*to++ = *from++) ) /* shift, shift, shift, shift */; } else if( newlen > oldlen ) { /* shift right */ from = old+totlen; /* start with trailing null */ to = from + (newlen - oldlen); for( i = (newlen-oldlen); i-- >= 0; ) *to-- = *from--; } /* stick in the new command */ from = new; to = old; for( i=0; i builtin.c #include #include #include #include #include #include #include #include #include #include #include "gen.h" #include "sh.h" /* * sh: a Unix(tm) like command interpreter for MS-DOS. * * by Douglas Orr * Textset Inc. * Ann Arbor, Michigan * * Copyright (c) 1985 Textset. All rights reserved. * * This program may be freely distributed, but not sold for profit. * */ Public char * history[]; Public short hindex[]; Public short hind, hmax; Local char * mons[] = { "Inv", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", }; char * smalloc( size ) unsigned size; { char * addr; if( !(addr = malloc(size)) ) { /* death with honor */ fprintf( stderr, "out of space\n" ); exit( 1 ); } return( addr ); } char * strdup( str ) Reg char * str; { return( strcpy( smalloc( strlen(str)+1 ), str ) ); } fcmp( file1, file2 ) char * * file1; char * * file2; { return( strcmp( *file1, *file2 ) ); } /* Built-in commands */ /* toy ls */ struct fs { char * fs_name; struct tm fs_time; unsigned short fs_mode; off_t fs_size; }; #define Set(flag) (flags |= (1 << flag)) #define Reset(flag) (flags &= ~(1 << flag)) #define Test(flag) (flags & (1 << flag)) #define Recurse (0) #define Lflag (1) #define Sflag (2) #define Rflag (3) #define Aflag (4) #define Cflag (5) Local fscmp( file1, file2 ) struct fs * file1; struct fs * file2; { return( strcmp( file1->fs_name, file2->fs_name ) ); } Local long ls_size; Local int ls_cnt; b_ls( argv, envp ) char * * argv; char * * envp; { char * ptr; short flags = 0; int rc; #ifdef correct /* * !! as near as I can tell, ls is the only built-in that allocates * storage. If you interrupt, that storage doesn't get freed, * currently. One way around this is the following, which isn't * very satisfactory. Another is to just ignore it and hope you * don't run out of storage. Yet another would be to set another * interrupt which frees the storage before leaving which is more * of a hassle than I have time for today. Caveat user. */ signal( SIGINT, SIG_IGN ); #endif correct ls_size = 0; ls_cnt = 0; Set(Recurse); /* list subdirectories */ if( !Interactive() ) Set(Cflag); /* print out one column */ while( (*++argv)[0] == '-' ) { for( ptr=(*argv)+1; *ptr; ptr++ ) switch( *ptr ) { case 'l': Set(Lflag); Reset(Cflag); break; case 's': Set(Sflag); break; case 'R': Set(Rflag); break; case 'a': Set(Aflag); break; default: fprintf( stderr, "ls: illegal option %s\n", *argv ); return( -1 ); } } rc = ls( argv, flags ); if( Test(Sflag) ) fprintf( stdout, "%ld total. %d file%s.\n", ls_size, ls_cnt, ls_cnt == 1 ? "" : "s" ); return( rc ); } /* * UGLY ALERT ... since I couldn't find any good way to do what I * would do on Unix, were I to be building a program to recursively * search directories - that being open the damn things like files, * I debased myself (a frequent occurance in dosville) and issue "cd"s * to move around. My apologies. I do know better, in the global * sense. */ Local lstatus( file, flags, maxlen, nl ) struct fs * file; short flags; int maxlen; bool nl; { /* UGLY ALERT recurse on directories if appropriate */ /* (Jaayynnnee ... how do you stop this crazy thing?) */ if( (file->fs_mode & S_IFDIR) && Test(Recurse) ) { int dev = -1; char * auxlist[1]; char * cwd = NULL; char * newd; char * dir = file->fs_name; /* cheat */ if( !Test(Rflag) ) Reset(Recurse); /* only recurse one level, by default */ auxlist[0] = NULL; if( dir[1] == ':' ) { dev = getcdsk(); if( chdsk( dir[0] ) == -1) { fprintf( stderr, "couldn't access %c:\n", dir[0] ); return( -1 ); } dir += 2; if( dir[0] == '\0' ) dir = "/"; } cwd = getcwd(NULL,128); if( (cwd == NULL) || chdir(dir) == -1) { fprintf( stderr, "! couldn't cd to %s\n", file->fs_name ); return( -1 ); } if( (newd = getcwd(NULL,128)) != NULL ) { fprintf( stdout, "%s\n%s:\n", (nl) ? "" : "\n", strlwr(newd) ); free(newd); } /* do a list of the place to which we just attached */ ls( auxlist, flags ); putc( '\n', stdout ); if( dev != -1 ) chdsk( dev ); if( cwd ) { chdir( cwd ); free( cwd ); } nl = True; } else { if( Test(Lflag) ) { /* ?! doesn't always line up right */ fprintf( stdout, "%-8ld %2d:%02d %3s-%02d-%02d %-13s\n", file->fs_size, file->fs_time.tm_hour, file->fs_time.tm_min, mons[file->fs_time.tm_mon], file->fs_time.tm_mday, file->fs_time.tm_year, file->fs_name ); nl = True; } else { fprintf( stdout, "%s", file->fs_name ); if( Test(Aflag) ) { if( file->fs_mode & S_IFDIR ) fprintf( stdout, "\\" ); else if( file->fs_mode & S_IEXEC ) fprintf( stdout, "*" ); else printf( " " ); } fprintf( stdout, "%*s", maxlen-strlen(file->fs_name), "" ); nl = False; } } return( nl ); } Local ls( list, flags ) char * * list; short flags; { int argc; struct fs * files; char * * filelist; int cnt; int maxlen; Reg int i, j; Reg int r; int cols; char * ptr; Local struct stat statb; int nl; /* initialize */ for( argc=0, filelist=list ; *filelist; filelist++ ) argc++; maxlen = 0; files = NULL; cnt = 0; /* implicitly ls of current directory */ if( argc == 0 ) { int ac; files = (struct fs *)smalloc( (ac=20) * sizeof(struct fs) ); if( !Test(Rflag) ) Reset(Recurse); for( ptr=open_dir("*.*"); ptr; ptr=nxt_entry() ) { if( cnt == ac ) { ac += 20; files = (struct fs *)realloc( files, ac*sizeof(struct fs) ); if( files == NULL ) { /* cut your losses */ fprintf( stderr, "ls: out of space\n" ); return(-1); } } /* degenerate */ if( strcmp( ptr, "." ) == 0 || strcmp( ptr, ".." ) == 0 ) continue; files[cnt].fs_mode = dta_mode(); files[cnt].fs_time = *dta_time(); files[cnt].fs_size = dta_size(); files[cnt++].fs_name = strlwr(strdup(ptr)); if( strlen(ptr) > maxlen ) maxlen = strlen( ptr ); } } else { files = (struct fs *)smalloc( (argc+1) * sizeof(struct fs) ); filelist = list; while( *filelist ) { if( (stat( *filelist, &statb ) == -1) ) { if( isdev(*filelist) || (strcmp(*filelist, "/")==0 || strcmp(*filelist,"\\")==0)) { statb.st_mode = S_IFDIR; statb.st_size = 0; statb.st_mtime = 0; } else { fprintf( stderr, "%s: no such file\n", *filelist ); return( -1 ); } } files[cnt].fs_name = strlwr(strdup(*filelist)); files[cnt].fs_mode = statb.st_mode; files[cnt].fs_time = *localtime( &statb.st_mtime ); files[cnt].fs_time.tm_mon += 1; /* relative to 1 */ files[cnt++].fs_size = statb.st_size; if( strlen(*filelist) > maxlen ) maxlen = strlen( *filelist ); filelist++; /* very important */ } } /* !! this can overflow stack if the list is too large */ if( cnt ) qsort( (char *)files, cnt, sizeof(struct fs), fscmp ); /* formatting considerations */ maxlen++; if( Test(Aflag) ) cols = 79 / (maxlen+1); else cols = 79 / maxlen; if( cols < 0 ) cols = 1; if( Test(Lflag) ) { for( i=0; i= cnt ) break; nl = lstatus( &files[ind], flags, maxlen, nl ); /* add the size of a column */ ind += cnt/cols; if( i < (cnt % cols) ) ind++; } /* nl flag indicates if a new line is desired */ if( !nl ) { putc( '\n', stdout ); nl = True; } } } /* free up the storage you have used, run some totals */ ls_cnt += cnt; for( i=0; i= 0; i-- ) fprintf( stdout, "%s ", dirs[i] ); putc( '\n', stdout ); return( 0 ); } b_pushd( argv, envp ) char * * argv; char * * envp; { char * dirp; char * cwd; if( *++argv == NULL ) { if( dircnt == 0 ) { fprintf( stderr, "no directory specified\n" ); return( -1 ); } else dirp = dirs[--dircnt]; } else dirp = *argv; if( dircnt < MaxDirs ) { if( (cwd = getcwd( NULL, 128 )) == NULL ) { perror( "chdir" ); return( -1 ); } if( dirp[1] == ':' ) { if( chdsk(dirp[0]) == -1 ) perror( "chdisk" ); dirp += 2; if( dirp[0] == '\0' ) dirp = "/"; } if ( chdir( dirp ) == -1 ) { perror( "chdir" ); free(cwd); } else dirs[dircnt++] = strlwr(cwd); b_dirs( NULL, NULL ); return( 0 ); } else fprintf( stderr, "stack overflow\n" ); return( -1 ); } b_popd( argv, envp ) char * * argv; char * * envp; { char * cwd; char * dir; if( dircnt == 0 ) { fprintf( stderr, "stack empty\n" ); return( -1 ); } else { dir = cwd = dirs[--dircnt]; if( dir[1] == ':' ) { if( chdsk(dir[0]) == -1 ) perror( "chdisk" ); dir += 2; } if( chdir( dir ) == -1) perror( "chdir:" ); else b_dirs( NULL, NULL ); free( cwd ); } return( 0 ); } b_mkdir( argv, envp ) char * * argv; char * * envp; { int error = 0; int rc; while( *++argv ) { if( (rc = mkdir( *argv )) != 0 ) perror( "mkdir" ); error |= rc; } return( error ); } b_rmdir( argv, envp ) char * * argv; char * * envp; { int error = 0; int rc; while( *++argv ) { if( (rc = rmdir( *argv )) != 0 ) perror( "rmdir" ); error |= rc; } return( error ); } b_rm( argv, envp ) char * * argv; char * * envp; { int error = 0; int rc; while( *++argv ) { if( (rc = unlink( *argv )) != 0 ) perror( "rm" ); error |= rc; } return( error ); } b_history( argv, envp ) char * * argv; char * * envp; { int count; int i; int ind; if( *++argv == NULL ) count = hmax; else { count = atoi( *argv ); if( count > hmax ) count = hmax; } ind = hind-1; for( i=0; i llen ) return( 0 ); end = line+(llen-plen); for( ptr=line; ptr < end; ptr++ ) { /* ... actually, pretty mediocre */ if( strncmp( pattern, ptr, plen ) == 0 ) return( 1 ); } return( 0 ); } b_fgrep( argv, envp ) char * argv[]; char * * envp; { FILE * fd; int rc; char * pattern; pattern = *++argv; argv++; rc = 0; if( *argv == NULL ) { rc = fgrep( NULL, pattern, stdin ); return( 0 ); } else while( *argv ) { if( (fd = fopen( *argv, "r" )) == NULL ) fprintf( stderr, "couldn't open %s\n", *argv ); else { rc |= fgrep( *argv, pattern, fd ); fclose( fd ); } argv++; } return( !rc ); } SHAR_EOF cat << \SHAR_EOF > wildcard.c #include "gen.h" /* toy pattern matching ... recognize '*' only, for now */ /* * sh: a Unix(tm) like command interpreter for MS-DOS. * * by Douglas Orr * Textset Inc. * Ann Arbor, Michigan * * Copyright (c) 1985 Textset. All rights reserved. * * This program may be freely distributed, but not sold for profit. * */ ismatch( pattern, filename ) char * pattern; char * filename; { char * pat; char * fn; pat = pattern; fn = filename; while( *pat ) { if( *pat == '*' ) { ++pat; while( *fn ) { if( ismatch( pat, fn ) ) return( True ); else ++fn; } break; } else if( *pat == *fn ) { pat++; fn++; } else return( False ); } return( *pat == *fn ); } SHAR_EOF cat << \SHAR_EOF > gen.h #define Reg register #define Local static #define Public extern #define True (1) #define False (!True) /* access types */ #define F_OK (0) #define F_ROK (4) typedef unsigned char byte; typedef byte bool; Public char * l_getenv(); Public l_setenv(); SHAR_EOF cat << \SHAR_EOF > sh.h /* sh definitions */ #define Interactive() (isatty(fileno(stdin)) && isatty(fileno(stdout))) Public char * open_dir(); Public char * nxt_entry(); Public char * l_getenv(); Public struct tm * dta_time(); Public long dta_size(); SHAR_EOF # End of shell archive exit 0