doc@pucc-j.UUCP (09/02/86)
Reply-To: dillon%cory.Berkeley.EDU@BERKELEY.EDU (Matt Dillon) This shar file contains the latest version of my shell. See the README and instructions.txt file within for more details. -Matt #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create: # README # comm1.c # comm2.c # execom.c # globals.c # instructions.txt # main.c # run.c # set.c # shell.h # sub.c # This archive created: Wed Aug 27 19:51:38 1986 export PATH; PATH=/bin:/usr/bin:$PATH echo shar: "extracting 'README'" '(464 characters)' if test -f 'README' then echo shar: "will not over-write existing file 'README'" else cat << \!Funky!Stuff! > 'README' SHELL V 2.01 Matthew Dillon COMPILATION -Use a 32 bit compiler -Use Astartup.obj (or equivalent) as your startup object file. Do NOT use Lstartup.obj. -Use the libraries (in this order): MY.LIB AMIGA.LIB LC.LIB MY.LIB is my own library which you should have. If not, mail via UUCP or ARPAnet and I'll give you a copy. LC.LIB is only in there because lattice uses a function in LC.LIB for long integer multiplies. !Funky!Stuff! fi # end of overwriting check echo shar: "extracting 'comm1.c'" '(7826 characters)' if test -f 'comm1.c' then echo shar: "will not over-write existing file 'comm1.c'" else cat << \!Funky!Stuff! > 'comm1.c' /* * COMM1.C * * Matthew Dillon, August 1986 * * * */ #include <exec/types.h> #include <exec/exec.h> #include <libraries/dos.h> #include <libraries/dosextens.h> #include <lattice/fcntl.h> #include "shell.h" extern struct FileLock *CreateDir(), *CurrentDir(), *ParentDir(); extern struct FileLock *Lock(), *DupLock(); extern struct FileLock *cdgo(); extern char *AllocMem(); extern long disp_entry(); struct FileLock *Clock; do_sleep() { register int i; if (ac == 2) { i = atoi(av[1]); while (i > 0) { Delay (50*2); i -= 2; if (CHECKBREAK()) break; } } return (1); } do_number() { return (1); } do_cat() { int fi, i; char buf[256]; if (ac == 1) { while (Ogets(buf)) Oputs(buf); return (1); } for (i = 1; i < ac; ++i) { if (fi = xopen (av[i], "r", 512)) { while (xgets (fi, buf, 256)) { Oputs(buf); if (CHECKBREAK()) break; } xclose (fi); } else { printf ("could not open %s\n", av[i]); } } return (1); } do_dir(garbage, com) char *garbage; { struct DPTR *dp; struct InfoData *info; char *name; int i, stat; long total = 0; if (ac == 1) { ++ac; av[1] = ""; } for (i = 1; i < ac; ++i) { if ((dp = dopen (av[i], &stat)) == NULL) continue; if (com < 0) { info = (struct InfoData *)AllocMem(sizeof(struct InfoData), MEMF_PUBLIC); if (Info (dp->lock, info)) { int bpb = info->id_BytesPerBlock; fprintf (Cout, "Unit:%2ld Errs:%3ld Bytes: %-7ld Free: %-7ld\n", info->id_UnitNumber, info->id_NumSoftErrors, bpb * info->id_NumBlocks, bpb * (info->id_NumBlocks - info->id_NumBlocksUsed)); } else { perror (av[i]); } FreeMem (info, sizeof(*info)); } else { if (stat) { while (dnext (dp, &name, &stat)) { total += disp_entry (dp->fib); if (CHECKBREAK()) break; } } else { total += disp_entry(dp->fib); } } dclose (dp); } fprintf (Cout, "TOTAL: %ld\n", total); return (1); } long disp_entry(fib) register struct FileInfoBlock *fib; { char str[5]; register char *dirstr; str[4] = '\0'; str[0] = (fib->fib_Protection & FIBF_READ) ? '-' : 'r'; str[1] = (fib->fib_Protection & FIBF_WRITE) ? '-' : 'w'; str[2] = (fib->fib_Protection & FIBF_EXECUTE) ? '-' : 'x'; str[3] = (fib->fib_Protection & FIBF_DELETE) ? '-' : 'd'; dirstr = (fib->fib_DirEntryType < 0) ? " " : "DIR"; fprintf (Cout, "%s %6ld %s %s\n", str, (long)fib->fib_Size, dirstr, fib->fib_FileName); return ((long)fib->fib_Size); } do_quit() { if (Src_stack) { Quit = 1; return(do_return()); } main_exit (0); } do_echo(str) char *str; { register char *ptr; char nl = 1; for (ptr = str; *ptr && *ptr != ' '; ++ptr); if (*ptr == ' ') ++ptr; if (av[1] && strcmp (av[1], "-n") == 0) { nl = 0; ptr += 2; if (*ptr == ' ') ++ptr; } Write(Cout, ptr, strlen(ptr)); if (nl) Oputs(""); return (1); } do_source(str) char *str; { register int fi; char buf[256]; if (Src_stack == MAXSRC) { puts ("Too many source levels"); return(-1); } fi = xopen (next_word(str), "r", 512); if (fi == 0) { printf ("Cannot open %s\n", next_word(str)); return(-1); } ++H_stack; Src_pos[Src_stack] = 0; Src_base[Src_stack] = fi; ++Src_stack; while (xgets (fi, buf, 256)) { Src_pos[Src_stack - 1] += 1+strlen(buf); if (Verbose) puts(buf); exec_command (buf); if (CHECKBREAK()) break; } --H_stack; --Src_stack; unset_level(LEVEL_LABEL + Src_stack); xclose (fi); return (1); } /* * cd. Additionally, allow '..' to get you back one directory in the path. */ do_cd() { static char root[32]; register struct FileLock *lock; register char *str, *ptr; register int notdone; if (ac != 2) { Oputs (root); Clock = cdgo (root, NULL); return (1); } str = av[1]; while (*str == '/') { ++str; if (Clock) Clock = cdgo (NULL, Clock); } notdone = 1; while (notdone) { ptr = str; while (*str && *str != '/' && *str != ':') ++str; notdone = *str; *str++ = '\0'; if (ptr == str - 1) continue; if (notdone == ':') { *(str-1) = notdone; notdone = *str; *str = '\0'; strcpy (root, ptr); Clock = cdgo (root, NULL); *str = notdone; } else if (strcmp (ptr, "..") == 0) { if (Clock) Clock = cdgo (NULL, Clock); } else { if ((lock = cdgo (ptr, NULL)) == NULL) puts ("not found"); else Clock = lock; } } return (1); } struct FileLock * cdgo(ptr, lock) char *ptr; struct FileLock *lock; { register struct FileLock *newlock, *oldlock; if (lock) newlock = ParentDir (lock); else newlock = Lock (ptr, ACCESS_READ); if (newlock) { if (oldlock = CurrentDir (newlock)) UnLock (oldlock); } return (newlock); } do_mkdir() { register int i; register struct FileLock *lock; for (i = 1; i < ac; ++i) { if (lock = CreateDir (av[i])) { UnLock (lock); continue; } perror (av[i]); } return (1); } do_mv() { if (Rename (av[1], av[2])) return (1); perror ("rm:"); return (-1); } do_rm() { register int i; for (i = 1; i < ac; ++i) { if (!DeleteFile(av[i])) perror (av[i]); } return (1); } do_history() { register struct HIST *hist; register int i = H_tail_base; register int len = (av[1]) ? strlen(av[1]) : 0; for (hist = H_tail; hist; hist = hist->prev) { if (len == 0 || strncmp(av[1], hist->line, len) == 0) { fprintf (Cout, "%3ld ", i); Oputs (hist->line); } ++i; if (CHECKBREAK()) break; } return (1); } do_mem() { register long cfree, ffree; extern long AvailMem(); Forbid(); cfree = AvailMem (MEMF_CHIP); ffree = AvailMem (MEMF_FAST); Permit(); if (ffree) fprintf (Cout, "FAST memory:%10ld\n", ffree); fprintf (Cout, "CHIP memory:%10ld\n", cfree); fprintf (Cout, "Total -----:%5ld K\n", (ffree + cfree) >> 10); return (1); } /* * foreach var_name ( str str str str... str ) commands * spacing is important (unfortunetly) * * ac=0 1 2 3 4 5 6 7 * foreach i ( a b c ) echo $i * foreach i ( *.c ) "echo -n "file ->";echo $i" */ do_foreach() { register int i, cstart, cend, old; register char *cstr, *vname, *ptr, *scr, *args; cstart = i = (*av[2] == '(') ? 3 : 2; while (i < ac) { if (*av[i] == ')') break; ++i; } if (i == ac) { puts ("')' expected"); return (-1); } ++H_stack; cend = i; vname = strcpy(malloc(strlen(av[1])+1), av[1]); cstr = compile_av (av, cend + 1, ac); ptr = args = compile_av (av, cstart, cend); while (*ptr) { while (*ptr == ' ' || *ptr == 9) ++ptr; scr = ptr; if (*scr == '\0') break; while (*ptr && *ptr != ' ' && *ptr != 9) ++ptr; old = *ptr; *ptr = '\0'; set_var (LEVEL_SET, vname, scr); if (CHECKBREAK()) break; exec_command (cstr); *ptr = old; } --H_stack; free (args); free (cstr); unset_var (LEVEL_SET, vname); free (vname); return (1); } !Funky!Stuff! fi # end of overwriting check echo shar: "extracting 'comm2.c'" '(3867 characters)' if test -f 'comm2.c' then echo shar: "will not over-write existing file 'comm2.c'" else cat << \!Funky!Stuff! > 'comm2.c' /* * MORE commands */ #include "shell.h" do_return() { if (Src_stack) { xseek (Src_base[Src_stack - 1], 0, 1); Rval = (ac < 2) ? 1 : atoi(av[1]); return (-1); } else { main_exit ((ac < 2) ? 1 : atoi(av[1])); } } /* * STRHEAD * * place a string into a variable removing everything after and including * the 'break' character or until a space is found in the string. * * strhead varname breakchar string * */ do_strhead() { register char *str = av[3]; char bc = *av[2]; while (*str && *str != bc) ++str; *str = '\0'; set_var (LEVEL_SET, av[1], av[3]); } do_strtail() { register char *str = av[3]; char bc = *av[2]; while (*str && *str != bc) ++str; if (*str) ++str; set_var (LEVEL_SET, av[1], str); } /* * if A < B <, >, =, <=, >=, !=, where A and B are either: * nothing * a string * a value (begins w/ number) */ do_if(garbage, com) char *garbage; { char *v1, *v2, *v3, result, num; int n1, n2; switch (com) { case 0: if (If_stack && If_base[If_stack - 1]) { If_base[If_stack++] = 1; break; } result = num = 0; if (ac <= 2) { /* if $var; */ if (ac == 1 || strlen(av[1]) == 0 || (strlen(av[1]) == 1 && *av[1] == ' ')) goto do_result; result = 1; goto do_result; } if (ac != 4) { ierror(NULL, 500); break; } v1 = av[1]; v2 = av[2]; v3 = av[3]; while (*v1 == ' ') ++v1; while (*v2 == ' ') ++v2; while (*v3 == ' ') ++v3; if (*v1 >= '0' && *v1 <= '9') { num = 1; n1 = atoi(v1); n2 = atoi(v3); } while (*v2) { switch (*v2++) { case '>': result |= (num) ? (n1 > n2) : (strcmp(v1, v3) > 0); break; case '<': result |= (num) ? (n1 < n2) : (strcmp(v1, v3) < 0); break; case '=': result |= (num) ? (n1 == n2) : (strcmp(v1, v3) ==0); break; default: ierror (NULL, 503); break; } } do_result: If_base[If_stack++] = !result; break; case 1: if (If_stack > 1 && If_base[If_stack - 2]) break; if (If_stack) If_base[If_stack - 1] ^= 1; break; case 2: if (If_stack) --If_stack; break; } Disable = (If_stack) ? If_base[If_stack - 1] : 0; return (1); } do_label() { char aseek[32]; if (Src_stack == 0) { ierror (NULL, 502); return (-1); } sprintf (aseek, "%ld %ld", Src_pos[Src_stack-1], If_stack); set_var (LEVEL_LABEL + Src_stack - 1, av[1], aseek); return (1); } do_goto() { int new; long pos; char *lab; if (Src_stack == 0) { ierror (NULL, 502); } else { lab = get_var (LEVEL_LABEL + Src_stack - 1, av[1]); if (lab == NULL) { ierror (NULL, 501); } else { pos = atoi(lab); xseek (Src_base[Src_stack - 1], pos, -1); Src_pos[Src_stack - 1] = pos; new = atoi(next_word(lab)); for (; If_stack < new; ++If_stack) If_base[If_stack] = 0; If_stack = new; } } return (-1); /* Don't execute reset of this line */ } do_failat() { Faillevel = 1; Oputs ("NOT IMPLIMENTED YET"); } do_checkbrk() { Oputs ("NOT IMPLIMENTED YET"); } do_inc(garbage, com) char *garbage; { char *var; char num[32]; if (ac == 3) com = atoi(av[2]); var = get_var (LEVEL_SET, av[1]); if (var) { sprintf (num, "%ld", atoi(var)+com); set_var (LEVEL_SET, av[1], num); } } do_input() { char in[256]; if (Ogets(in)) set_var (LEVEL_SET, av[1], in); return (1); } do_ver() { Oputs (VERSION); return (1); } !Funky!Stuff! fi # end of overwriting check echo shar: "extracting 'execom.c'" '(14496 characters)' if test -f 'execom.c' then echo shar: "will not over-write existing file 'execom.c'" else cat << \!Funky!Stuff! > 'execom.c' /* * EXECOM.C * * Matthew Dillon, 10 August 1986 * * Finally re-written. * */ #include "shell.h" #define F_EXACT 0 #define F_ABBR 1 #define ST_COND 0x01 #define ST_NAME 0x02 struct COMMAND { int (*func)(); short minargs; short stat; int val; char *name; }; extern char *format_insert_string(); extern char *mpush(), *exarg(); extern int do_run(), do_number(); extern int do_quit(), do_set_var(), do_unset_var(); extern int do_echo(), do_source(), do_mv(); extern int do_cd(), do_rm(), do_mkdir(), do_history(); extern int do_mem(), do_cat(), do_dir(); extern int do_foreach(), do_return(), do_if(), do_label(), do_goto(); extern int do_failat(), do_checkbrk(), do_inc(); extern int do_input(), do_ver(), do_sleep(), do_help(); extern int do_strhead(), do_strtail(); static struct COMMAND Command[] = { do_run , 0, ST_NAME, 0 , "\001", do_number , 0, 0, 0 , "\001", do_quit , 0, 0, 0 , "quit", do_set_var , 0, 0, LEVEL_SET , "set", do_unset_var, 0, 0, LEVEL_SET , "unset", do_set_var , 0, 0, LEVEL_ALIAS, "alias", do_unset_var, 0, 0, LEVEL_ALIAS, "unalias", do_echo , 0, 0, 0 , "echo", do_source , 0, 0, 0 , "source", do_mv , 2, 0, 0 , "mv", do_cd , 0, 0, 0 , "cd", do_rm , 0, 0, 0 , "rm", do_mkdir , 0, 0, 0 , "mkdir", do_history , 0, 0, 0 , "history", do_mem , 0, 0, 0 , "mem", do_cat , 0, 0, 0 , "cat", do_dir , 0, 0, 0 , "dir", do_dir , 0, 0, -1 , "devinfo", do_foreach , 3, 0, 0 , "foreach", do_return , 0, 0, 0 , "return", do_if , 1, ST_COND, 0 , "if", do_if , 0, ST_COND, 1 , "else", do_if , 0, ST_COND, 2 , "endif", do_label , 1, ST_COND, 0 , "label", do_goto , 1, 0, 0 , "goto", do_failat , 1, 0, 0 , "failat", do_checkbrk , 0, 0, 0 , "checkbreak", do_strhead , 3, 0, 0 , "strhead", do_strtail , 3, 0, 0 , "strtail", do_inc , 1, 0, 1 , "inc", do_inc , 1, 0, -1, "dec", do_input , 1, 0, 0, "input", do_ver , 0, 0, 0, "version", do_sleep , 0, 0, 0, "sleep", do_help , 0, 0, 0, "help", NULL , 0, 0, 0 , NULL }; static unsigned char elast; /* last end delimeter */ static char Cin_ispipe, Cout_ispipe; exec_command(base) char *base; { register char *scr; register int i; if (!H_stack) add_history(base); scr = malloc((strlen(base) << 2) + 2); /* 4X */ preformat(base, scr); i = fcomm(scr, 1); return ((i) ? -1 : 1); } isalphanum(c) { if (c >= '0' && c <= '9') return (1); if (c >= 'a' && c <= 'z') return (1); if (c >= 'A' && c <= 'Z') return (1); if (c == '_') return (1); return (0); } preformat(s, d) register char *s, *d; { register int si, di, qm; si = di = qm = 0; while (s[si] == ' ' || s[si] == 9) ++si; while (s[si]) { if (qm && s[si] != '\"' && s[si] != '\\') { d[di++] = s[si++] | 0x80; continue; } switch (s[si]) { case ' ': case 9: d[di++] = ' '; while (s[si] == ' ' || s[si] == 9) ++si; if (s[si] == 0 || s[si] == '|' || s[si] == ';') --di; break; case '*': case '?': d[di++] = 0x80; case '!': d[di++] = s[si++]; break; case ';': case '|': d[di++] = s[si++]; while (s[si] == ' ' || s[si] == 9) ++si; break; case '\\': d[di++] = s[++si] | 0x80; if (s[si]) ++si; break; case '\"': qm = 1 - qm; ++si; break; case '^': d[di++] = s[++si] & 0x1F; if (s[si]) ++si; break; case '$': /* search end of var name and place false space */ d[di++] = 0x80; d[di++] = s[si++]; while (isalphanum(s[si])) d[di++] = s[si++]; d[di++] = 0x80; break; default: d[di++] = s[si++]; break; } } d[di++] = 0; d[di] = 0; if (Debug & 0x01) { printf ("PREFORMAT: %ld :%s:\n", strlen(d), d); } } /* * process formatted string. ' ' is the delimeter. * * 0: check '\0': no more, stop, done. * 1: check $. if so, extract, format, insert * 2: check alias. if so, extract, format, insert. goto 1 * 3: check history or substitution, extract, format, insert. goto 1 * * 4: assume first element now internal or disk based command. * * 5: extract each ' ' or 0x80 delimited argument and process, placing * in av[] list (except 0x80 args appended). check in order: * * '$' insert string straight * '>' setup stdout * '<' setup stdin * '*' or '?' do directory search and insert as separate args. * * ';' 0 '|' end of command. if '|' setup stdout * -execute command, fix stdin and out (|) sets * up stdin for next guy. */ fcomm(str, freeok) register char *str; { register char *istr; char *nextstr; char *command; char *pend_alias = NULL; /* pending alias */ char err = 0; mpush_base(); if (*str == 0) goto done1; step1: if (*str == '$') { if (istr = get_var (LEVEL_SET, str + 1)) str = format_insert_string(str, istr, &freeok); } istr = get_var (LEVEL_ALIAS, str); if (istr) { if (*istr == '%') { pend_alias = istr; } else { str = format_insert_string(str, istr, &freeok); goto step1; } } if (*str == '!') { istr = get_history(str); replace_head(istr); str = format_insert_string(str, istr, &freeok); goto step1; } nextstr = str; command = exarg(&nextstr); if (*command == 0) goto done0; if (pend_alias == 0) { register int ccno; ccno = find_command(command); if (Command[ccno].stat & ST_COND) goto skipgood; } if (Disable) goto done0; skipgood: { register char *arg, *ptr, *scr; short redir; short doexpand; short cont; short inc; ac = 1; av[0] = command; step5: /* ac = nextac */ if (!elast || elast == ';' || elast == '|') goto stepdone; av[ac] = NULL; cont = 1; doexpand = redir = inc = 0; while (cont && elast) { ptr = exarg(&nextstr); inc = 1; arg = ""; cont = (elast == 0x80); switch (*ptr) { case '<': redir = -2; case '>': ++redir; arg = ptr + 1; cont = 1; break; case '$': if ((arg = get_var(LEVEL_SET, ptr + 1)) == NULL) arg = ptr; break; case '*': case '?': doexpand = 1; arg = ptr; break; default: arg = ptr; break; } /* Append arg to av[ac] */ for (scr = arg; *scr; ++scr) *scr &= 0x7F; if (av[ac]) { register char *old = av[ac]; av[ac] = mpush(strlen(arg)+1+strlen(av[ac])); strcpy(av[ac], old); strcat(av[ac], arg); } else { av[ac] = mpush(strlen(arg)+1); strcpy(av[ac], arg); } if (elast != 0x80) break; } /* process expansion */ if (doexpand) { char **eav, **ebase; int eac; eav = ebase = expand(av[ac], &eac); if (eav) { inc = 0; if (ac + eac + 2 > MAXAV) { ierror (NULL, 506); err = 1; } else { for (; eac; --eac, ++eav) av[ac++] = strcpy(mpush(strlen(*eav)+1), *eav); } free_expand (ebase); } else { puts ("null expansion"); err = 1; } } /* process redirection */ if (redir && !err) { register char *file = (doexpand) ? av[--ac] : av[ac]; if (redir < 0) Cin_name = file; else Cout_name = file; inc = 0; } /* check elast for space */ if (inc) { ++ac; if (ac + 2 > MAXAV) { ierror (NULL, 506); err = 1; /* error condition */ elast = 0; /* don't process any more arguemnts */ } } if (elast == ' ') goto step5; } stepdone: av[ac] = NULL; /* process pipes via files */ if (elast == '|' && !err) { static int which; /* 0 or 1 in case of multiple pipes */ which = 1 - which; Cout_name = (which) ? Pipe1 : Pipe2; Cout_ispipe = 1; } /* DO COMMAND HERE! */ if (err) goto done0; { register int i, len; char save_elast; char *avline; save_elast = elast; for (i = len = 0; i < ac; ++i) len += strlen(av[i]) + 1; avline = malloc(len+1); for (len = 0, i = ((pend_alias) ? 1 : 0); i < ac; ++i) { if (Debug & 0x02) printf ("AV[%2ld] %ld :%s:\n", i, strlen(av[i]), av[i]); strcpy(avline + len, av[i]); len += strlen(av[i]); if (i + 1 < ac) avline[len++] = ' '; } avline[len] = 0; if (pend_alias) { /* special % alias */ register char *ptr, *scr; for (ptr = pend_alias; *ptr && *ptr != ' '; ++ptr); set_var (LEVEL_SET, pend_alias + 1, avline); free (avline); scr = malloc((strlen(ptr) << 2) + 2); preformat (ptr, scr); fcomm (scr, 1); unset_var (LEVEL_SET, pend_alias + 1); } else { /* normal command */ register int ccno; register long cin, cout; ccno = find_command (command); if ((Command[ccno].stat & ST_NAME) == 0) { if (Cin_name) { cin = Cin; Cin = Open(Cin_name, 1005); if (Cin == 0) { ierror (NULL, 504); err = 1; Cin = cin; Cin_name = NULL; } } if (Cout_name) { cout = Cout; Cout = Open(Cout_name, 1006); if (Cout == NULL) { err = 1; ierror (NULL, 504); Cout = cout; Cout_name = NULL; } } } if (ac < Command[ccno].minargs + 1) { ierror (NULL, 500); err = 1; } else { i = (*Command[ccno].func)(avline, Command[ccno].val); err = (i < 0); } free (avline); if ((Command[ccno].stat & ST_NAME) == 0) { if (Cin_name) { Close(Cin); Cin = cin; } if (Cout_name) { Close(Cout); Cout = cout; } } } if (Cin_ispipe && Cin_name) DeleteFile(Cin_name); if (Cout_ispipe) { Cin_name = Cout_name; /* ok to assign.. static name */ Cin_ispipe = 1; } else { Cin_name = NULL; } Cout_name = NULL; Cout_ispipe = 0; elast = save_elast; } mpop_tobase(); /* free arguments */ mpush_base(); /* push dummy base */ done0: if (elast != 0 && !err) /* done? or more? */ err = fcomm(nextstr, 0); /* do next command ';' or '|' sep. */ if (Cin_name) /* pipe segment complete */ DeleteFile(Cin_name); Cin_name = NULL; Cin_ispipe = 0; done1: mpop_tobase(); if (freeok) free(str); return ((int)err); /* TRUE = error occured */ } char * exarg(ptr) unsigned char **ptr; { register unsigned char *end; register unsigned char *start; start = end = *ptr; while (*end && *end != 0x80 && *end != ';' && *end != '|' && *end != ' ') ++end; elast = *end; *end = '\0'; *ptr = end + 1; return ((char *)start); } static char **Mlist; mpush_base() { char *str; str = malloc(5); *(char ***)str = Mlist; str[4] = 0; Mlist = (char **)str; } char * mpush(bytes) { char *str; str = malloc(5 + bytes); *(char ***)str = Mlist; str[4] = 1; Mlist = (char **)str; return (str + 5); } mpop_tobase() { register char *next; while (Mlist) { next = *Mlist; if (((char *)Mlist)[4] == 0) { free (Mlist); Mlist = (char **)next; break; } free (Mlist); Mlist = (char **)next; } } /* * Insert 'from' string in front of 'str' while deleting the * first entry in 'str'. if freeok is set, then 'str' will be * free'd */ char * format_insert_string(str, from, freeok) char *str; char *from; int *freeok; { register char *new1, *new2; register unsigned char *strskip; int len; for (strskip = str; *strskip && *strskip != ' ' && *strskip != ';' && *strskip != '|' && *strskip != 0x80; ++strskip); len = strlen(from); new1 = malloc((len << 2) + 2); preformat(from, new1); len = strlen(new1) + strlen(strskip); new2 = malloc(len+2); strcpy(new2, new1); strcat(new2, strskip); new2[len+1] = 0; free (new1); if (*freeok) free (str); *freeok = 1; return (new2); } find_command(str) char *str; { int i; int len = strlen(str); if (*str >= '0' && *str <= '9') return (1); for (i = 0; Command[i].func; ++i) { if (strncmp (str, Command[i].name, len) == 0) return (i); } return (0); } do_help() { register struct COMMAND *com; for (com = &Command[2]; com->func; ++com) fprintf (Cout, "%s ", com->name); Oputs (""); } !Funky!Stuff! fi # end of overwriting check echo shar: "extracting 'globals.c'" '(1912 characters)' if test -f 'globals.c' then echo shar: "will not over-write existing file 'globals.c'" else cat << \!Funky!Stuff! > 'globals.c' /* * GLOBALS.C * * Matthew Dillon, 24 Feb 1986 * */ #include "shell.h" struct HIST *H_head, *H_tail; struct PERROR Perror[] = { 103, "insufficient free storage", 105, "task table full", 120, "argument line invalid or too long", 121, "file is not an object module", 122, "invalid resident library during load", 201, "no default directory", 202, "object in use", 203, "object already exists", 204, "directory not found", 205, "object not found", 206, "bad stream name", 207, "object too large", 209, "action not known", 210, "invalid stream component name", 211, "invalid object lock", 212, "object not of required type", 213, "disk not validated", 214, "disk write protected", 215, "rename across devices", 216, "directory not empty", 217, "too many levels", 218, "device not mounted", 219, "seek error", 220, "comment too long", 221, "disk full", 222, "file delete protected", 223, "file write protected", 224, "file read protected", 225, "not a DOS disk", 226, "no disk", 232, "no more entries in directory", /* custom error messages */ 500, "bad arguments", 501, "label not found", 502, "must be within source file", 503, "Syntax Error", 504, "redirection error", 505, "pipe error", 506, "too many arguments", 0, NULL }; char *av[MAXAV]; int Src_base[MAXSRC]; long Src_pos[MAXSRC]; char If_base[MAXIF]; int H_len, H_tail_base, H_stack; int Src_stack, If_stack; int ac; int Debug, Rval, Disable, Faillevel, Verbose; int Quit; int Cout, Cin; /* current input and output file handles */ int Uniq; /* unique value */ char *Cin_name, *Cout_name; /* redirection input/output name or NULL */ char *Pipe1, *Pipe2; /* the two pipe temp. files */ int S_histlen = 20; !Funky!Stuff! fi # end of overwriting check echo shar: "extracting 'instructions.txt'" '(11840 characters)' if test -f 'instructions.txt' then echo shar: "will not over-write existing file 'instructions.txt'" else cat << \!Funky!Stuff! > 'instructions.txt' INSTRUCTIONS FOR SHELL 2.01 (A) Compiling (B) Overview (C) Quicky tech notes on implimentation. (D) Command pre-processor (E) Command-list (F) special SET variables (G) example .login file. (A) COMPILING: Compile the shell with a 32 bit compiler. 16 bit compilers will not work. Use the Astartup.obj startup module, and the three libraries: MY.LIB AMIGA.LIB LC.LIB NOTE the order in which the libraries occur. LC.LIB is only in there because the idiots at lattice use a call to do 32-bit integer multiplies. MY.LIB is my own library which has been posted (but feel free to ask me to send it to you if you haven't got it) lc1 -i$incdir/ -i$incdir/lattice/ $y lc2 -s -v $y where $incdir is the include directory for C, and '$y' stands for each .C file. At this point you have nothing but object modules and must now link them together: alink faster Astartup.obj+main.o+...+run.o library $libdir/my.lib+$libdir/amiga.lib+$libs+$libdir/lc.lib to ram:shell main.o+...+run.o stands for all the object modules. Note that my library 'my.lib' must exist. Note also that Lattice's library is at the end. $libdir stands for where your C libraries are. Astartup.obj is Lattice's startup module which doesn't use Lattice's library (thank god!). As I said above, the only routine used from LC.LIB is their long multiply routine. Executable Size should be around 24K. (B) OVERVIEW: overview of the major features: -simple history -redirection -piping -aliases -variables & variable handling -file name expansion via '?' and '*' -conditionals -source files (w/ gotos and labels) -many built in commands to speed things up changes from V1.02: -you can now redirect through internal commands as well as external. -piping has been added. You can pipe from/to internal commands. -you can now embed strings ( charlie/$var/ben ), making the shell more flexible. -history substitution displays line to be executed. -verbose mode for debugging source files -break handled a little better (still can't send break to external commands) -some new commands sorry, I still havent figured out how to get the return value from external commands. (C) QUICK TECH NOTES: PIPES have been implimented using temporary RAM: files. Thus, you should be careful when specifying a 'ram:*' expansion as it might include the temp. files. These files are deleted on completion of the pipe segment. My favorite new feature is the fact that you can now redirect to and from, and pipe internal commands. 'echo charlie >ram:x', for instance. Another favorite: echo "echo mem | shell" | shell To accomplish these new features, I completely re-wrote the command parser in execom.c (D) COMMAND LINE PRE-PROCESSING: preprocessing is done on the command line before it is passed on to an internal or external routine: ^c where c is a character is converted to that control character. Thus, say '^l' for control-l. $name where name is a variable name. Variable names can consist of 0-9, a-z, A-Z, and underscore (_). The contents of the specified variable is used. If the variable doesn't exist, the specifier is used. That is, if the variable 'i' contains 'charlie', then '$i' -> 'charlie'. If the variable 'i' doesn't exist, then '$i'->'$i' . ; delimits commands. echo charlie ; echo ben. ' ' (a space). delimits arguments. "string" a quoted string. For instance, if you want to echo five spaces and an 'a': echo a -> a echo " a" -> a \c overide the meaning of special characters. '\^a' is a circumflex and an a rather than control-a. To get a backslash, you must say '\\'. >file specify output redirection. All output from the command is placed in the specified file. <file specify input redirection. The command takes input from the file rather than the keyboard (note: not all commands require input). It makes no sense to say 'echo <charlie' since the 'echo' command only outputs its arguments. | PIPE specifier. The output from the command on the left becomes the input to the command on the right. The current SHELL implimentation uses temporary files to store the data. !! execute the previously executed command. !nn (nn is a number). Insert the history command numbered n (see the HISTORY command) !partial search backwards through the history list for a command which looks the same as 'partial', and execute it. (E) COMMAND LIST: The first argument is the command-name... if it doesn't exist in the list below and isn't an alias, it is assumed to be an external (disk) command. NOTE: Many CLI specific external commands, such as 'path', and 'cd' will have no effect on the shell. However, you can execute these commands from the initial CLI (startup script) BEFORE you run the shell. HELP simply displays all the available commands. QUIT quit my SHELL (awww!). End, El-Zappo, Kapow. Done, Finis SET SET name SET name string The first method lists all current variable settings. The second method lists the setting for that particular variable, or creates the variable if it doesn't exist (to "") The last method sets a variable to a string. UNSET name name name.... unset one or more variables. Deletes them entirely. ALIAS ALIAS name ALIAS name string same as SET, but applies to the alias list. You can alias a single name to a set of commands. For instance: alias hi "echo a; echo b" then you can simply say 'hi'. Aliases come in two forms the second form allows you to place the arguments after an alias in a variable for retrieval: alias xxx "%i echo this $i is a test" % xxx charlie this charlie is a test The rest of the command line is placed in the specified variable for the duration of the alias. This is especially useful when used in conjunction with the 'FOREACH' command. UNALIAS name name name... delete aliases.. ECHO string ECHO -n string echo the string to the screen. If '-n' is specified, no newline is output. STRHEAD varname breakchar string remove everything after and including the breakchar in 'string' and place in variable 'varname': % strhead j . aaa.bbb % echo $j aaa % STRTAIL varname breakchar string remove everything before and including the breakchar in 'string' and place in variable 'varname': % strtail j . aaa.bbb % echo $j bbb % SOURCE file execute commands from a file. You can create SHELL programs in a file and then execute them with this command. Source'd files have the added advantage that you can have loops in your command files (see GOTO and LABEL) MV from to Exactly the Rename() call. Allows you to rename files or move them around within a disk. CD .. CD path Change your current working directory. You may specify '..' to go back one directory (this is a CD specific feature, and does not work with normal path specifications). RM file file file... DeleteFile(). Remove the specified files. MKDIR name name name... create the following directories. HISTORY [partial string] Displays the enumerated history list. The size of the list is controlled by the _history variable. If you specify a partial- string, only those entries matching that string are displayed. MEM Display current memory statistics. CAT [file file....] Type the specified files onto the screen. If no file is specified, STDIN in used. DIR [path path ... ] Get a directory listing of the current directory or specified directories. DEVINFO [device: device:... ] Display Device statistics for the current device (CD base), or specified devices. FOREACH varname ( strings ) command 'strings' is broken up into arguments. Each argument is placed in the variable 'varname' in turn and 'command' executed. To execute multiple commands, place them in quotes: % foreach i ( a b c d ) "echo -n $i;echo \" ha\"" a ha b ha c ha d ha RETURN [value] return from a source file. The rest of the source file is discarded. Currently, the optional value is thrown away. IF argument conditional argument ; IF argument If a single argument is something to another argument. Conditional clauses allowed: <, >, =, and combinations (wire or). Thus <> is not-equal, >= larger or equal, etc... If the left argument is numeric, both arguments are treated as numeric. usually the argument is either a constant or a variable ($varname). The second form if IF is conditional on the existance of the argument. If the argument is a "" string, then false , else TRUE. ELSE ; else clause. ENDIF ; the end of an if statement. LABEL name create a program label right here. GOTO label goto the specified label name. You can only use this command from a source file. FAILAT CHECKBREAK not implimented yet. DEC var INC var decrement or increment the numerical equivalent of the variable and place the ascii-string result back into that variable. INPUT varname input from STDIN (or a redirection, or a pipe) to a variable. The next input line is placed in the variable. VER display my name and the version number. SLEEP timeout Sleep for 'timeout' seconds. (F) SPECIAL SET VARIABLES _prompt This variable is set to the command you wish executed that will create your prompt. _history This variable is set to a numerical value, and specifies how far back your history should extend. _debug Debug mode... use it if you dare. _verbose Verbose mode (for source files). display commands as they are executed. (G) EXAMPLE .login file. from a CLI or the startup-script say 'SHELL filename'. That file is sourced first. thus, 'SHELL .login' will set up your favorite aliases: ------------------------------ example source file ----------------- echo "shells, Matt" alias l "%var if $var;echo $var;else;echo *;endif" alias c "echo ^l" alias cc "assign c: cb:c ;cd ram:" alias wb "assign c: sys:c ;cd ram:" alias ram "cp c:run ram:; cp c:assign ram:; cp c:cp ram:; assign c: ram:" alias ram2 "assign c: ram:" alias ed "run ED" alias c1 "%z foreach y ( $z ) \"echo $y;lc1 -o$temp -i$incdir/ -i$incdir/lattice/ $y\";echo DONE" alias c2 "%z foreach y ( $z ) \"echo $y;lc2 -s -v $temp$y\";echo DONE" alias ld "%var alink faster $libdir/lstartup.obj+$var library $libdir/lc.lib+$libdir/amiga.lib to $dest" alias lds "%var alink faster $libdir/Astartup.obj+$var library $libdir/my.lib+$libdir/amiga.lib+$libs to $dest" set dest ram:a set temp ram: set libdir cb:clib set incdir cb:include set libs $libdir/lc.lib cd ram: -------------------------------------------------------------------- !Funky!Stuff! fi # end of overwriting check echo shar: "extracting 'main.c'" '(1465 characters)' if test -f 'main.c' then echo shar: "will not over-write existing file 'main.c'" else cat << \!Funky!Stuff! > 'main.c' /* * MAIN.C * * Matthew Dillon, 24 Feb 1986 * * el main routine. */ #include "shell.h" #include <libraries/dosextens.h> extern long SetSignal(); char Inline[256]; main(argc, argv) register char *argv[]; { char *prompt; register int i; check32(); init_vars(); init(); for (i = 1; i < argc; ++i) { strcpy (Inline, "source "); strcat (Inline, argv[i]); do_source (Inline, 0); } for (;;) { if ((prompt = get_var (LEVEL_SET, V_PROMPT)) == NULL) prompt = "echo -n \"% \""; if (CHECKBREAK()) { while (WaitForChar(Input(), 1)) gets(Inline); } ++H_stack; exec_command (prompt); --H_stack; if (Quit || !gets(Inline)) main_exit(0); breakreset(); if (*Inline) exec_command(Inline); } } init_vars() { if (IsInteractive(Input())) set_var (LEVEL_SET, V_PROMPT, "echo -n \"% \""); else set_var (LEVEL_SET, V_PROMPT, ""); set_var (LEVEL_SET, V_HIST, "20"); } init() { static char pipe1[32], pipe2[32]; Cin = Input(); Cout= Output(); Uniq= FindTask(0); Pipe1 = pipe1; Pipe2 = pipe2; sprintf (pipe1, "ram:pipe1_%ld", Uniq); sprintf (pipe2, "ram:pipe2_%ld", Uniq); } main_exit(n) { free_memory(); exit (n); } breakcheck() { if (SetSignal(0,0) & SIGBREAKF_CTRL_C) return (1); else return (0); } breakreset() { SetSignal(0, SIGBREAKF_CTRL_C); } !Funky!Stuff! fi # end of overwriting check echo shar: "extracting 'run.c'" '(789 characters)' if test -f 'run.c' then echo shar: "will not over-write existing file 'run.c'" else cat << \!Funky!Stuff! > 'run.c' /* * RUN.C * * Matthew Dillon * */ #include "shell.h" do_run(str) char *str; { register char *ptr, *scr; register char *cin_name, *cout_name; register int i; cin_name = (Cin_name) ? Cin_name : "*"; cout_name= (Cout_name)? Cout_name: "*"; ptr = malloc(strlen(str) + strlen(cin_name) + strlen(cout_name) + 10); for (scr = str; *scr && *scr != ' '; ++scr); i = scr - str; /* str[i] is a space or \0 */ bmov (str, ptr, i); ptr[i++] = ' '; ptr[i++] = '<'; bmov (cin_name, ptr + i, strlen(cin_name)); i += strlen(cin_name); ptr[i++] = ' '; ptr[i++] = '>'; bmov (cout_name, ptr + i, strlen(cout_name)); i += strlen(cout_name); strcpy (ptr + i, scr); i = Execute (ptr, 0, 0); free (ptr); return ((i) ? 1 : -1); } !Funky!Stuff! fi # end of overwriting check echo shar: "extracting 'set.c'" '(3549 characters)' if test -f 'set.c' then echo shar: "will not over-write existing file 'set.c'" else cat << \!Funky!Stuff! > 'set.c' /* * SET.C * * Matthew Dillon, July 1986 * */ #include "shell.h" #define MAXLEVELS (3 + MAXSRC) struct MASTER { struct MASTER *next; struct MASTER *last; char *name; char *text; }; static struct MASTER *Mbase[MAXLEVELS]; char * set_var(level, name, str) register char *name, *str; { register struct MASTER *base = Mbase[level]; register struct MASTER *last; register int len; for (len = 0; isalphanum(name[len]); ++len); while (base != NULL) { if (strlen(base->name) == len && strncmp (name, base->name, len) == 0) { Free (base->text); goto gotit; } last = base; base = base->next; } if (base == Mbase[level]) { base = Mbase[level] = (struct MASTER *)malloc (sizeof(struct MASTER)); base->last = NULL; } else { base = (struct MASTER *)malloc (sizeof(struct MASTER)); base->last = last; last->next = base; } base->name = malloc (len + 1); bmov (name, base->name, len); base->name[len] = 0; base->next = NULL; gotit: base->text = malloc (strlen(str) + 1); strcpy (base->text, str); return (base->text); } char * get_var (level, name) register char *name; { register struct MASTER *base = Mbase[level]; register unsigned char *scr; register int len; for (scr = name; *scr && *scr != 0x80 && *scr != ' ' && *scr != ';' && *scr != '|'; ++scr); len = scr - name; while (base != NULL) { if (strlen(base->name) == len && strncmp (name, base->name, len) == 0) return (base->text); base = base->next; } return (NULL); } unset_level(level) { register struct MASTER *base = Mbase[level]; while (base) { Free (base->name); Free (base->text); Free (base); base = base->next; } Mbase[level] = NULL; } unset_var(level, name) char *name; { register struct MASTER *base = Mbase[level]; register struct MASTER *last = NULL; register int len; for (len = 0; isalphanum(name[len]); ++len); while (base) { if (strlen(base->name) == len && strncmp (name, base->name, len) == 0) { if (base != Mbase[level]) last->next = base->next; else Mbase[level] = base->next; if (base->next != NULL) base->next->last = last; if (base == Mbase[level]) Mbase[level] = base->next; Free (base->name); Free (base->text); Free (base); return (1); } last = base; base = base->next; } return (-1); } do_unset_var(str, level) char *str; { register int i; for (i = 1; i < ac; ++i) unset_var (level, av[i]); return (1); } do_set_var(command, level) char *command; { register struct MASTER *base = Mbase[level]; register char *str; if (ac == 1) { while (base) { fprintf (Cout, "%-10s ", base->name); Oputs (base->text); base = base->next; } return (1); } if (ac == 2) { str = get_var (level, av[1]); if (str) { fprintf (Cout, "%-10s ", av[1]); Oputs(str); } else { set_var (level, av[1], ""); } } if (ac > 2) set_var (level, av[1], next_word (next_word (command))); if (*av[1] == '_') { S_histlen = (str = get_var(LEVEL_SET, V_HIST)) ? atoi(str) : 0; Debug = (str = get_var(LEVEL_SET, V_DEBUG)) ? atoi(str) : 0; Verbose = (get_var(LEVEL_SET, V_VERBOSE)) ? 1 : 0; if (S_histlen < 2) S_histlen = 2; } return (1); } !Funky!Stuff! fi # end of overwriting check echo shar: "extracting 'shell.h'" '(2220 characters)' if test -f 'shell.h' then echo shar: "will not over-write existing file 'shell.h'" else cat << \!Funky!Stuff! > 'shell.h' /* * SHELL.H * * Matthew Dillon, 24 Feb 1986 * */ #include <exec/types.h> #define MAXAV 128 /* Max. # arguments */ #define MAXSRC 5 /* Max. # of source file levels */ #define MAXIF 10 /* Max. # of if levels */ #define LEVEL_SET 0 #define LEVEL_ALIAS 1 #define LEVEL_LABEL 2 #define M_RESET 0 #define M_CONT 1 #define V_PROMPT "_prompt" /* Special variable names */ #define V_HIST "_history" #define V_DEBUG "_debug" #define V_VERBOSE "_verbose" #define FL_DOLLAR 0x01 /* One of the following */ #define FL_BANG 0x02 #define FL_PERCENT 0x04 #define FL_QUOTE 0x08 #define FL_IDOLLAR 0x10 /* Any or all of the following may be set */ #define FL_EOC 0x20 #define FL_EOL 0x40 #define FL_OVERIDE 0x80 #define FL_WILD 0x100 #define FL_MASK (FL_DOLLAR|FL_BANG|FL_PERCENT|FL_QUOTE) #define VERSION "V2.01 (c)1986 Matthew Dillon." #ifndef NULL #define NULL 0L #endif #define CHECKBREAK() ( breakcheck() ? (printf("^C\n"),1) : 0) struct HIST { struct HIST *next, *prev; /* doubly linked list */ char *line; /* line in history */ }; struct PERROR { int errnum; /* Format of global error lookup */ char *errstr; }; struct DPTR { /* Format of directory fetch pointer */ struct FileLock *lock; /* lock on directory */ struct FileInfoBlock *fib; /* mod'd fib for entry */ }; extern struct HIST *H_head, *H_tail; extern struct PERROR Perror[]; extern struct DPTR *dopen(); extern char *set_var(), *get_var(), *next_word(); extern char *get_history(), *compile_av(); extern char *malloc(), *strcpy(), *strcat(), *AllocMem(), *gets(), *Ogets(); extern char **expand(); extern char *av[]; extern char *Current; extern int H_len, H_tail_base, H_stack; extern int Src_stack, If_stack; extern int ac; extern int Debug, Rval, Verbose, Disable, Faillevel, Quit; extern int S_histlen, Uniq; extern int Cin, Cout; extern char *Cin_name, *Cout_name; extern char *Pipe1, *Pipe2; extern int Src_base[MAXSRC]; extern long Src_pos[MAXSRC]; extern char If_base[MAXIF]; !Funky!Stuff! fi # end of overwriting check echo shar: "extracting 'sub.c'" '(10083 characters)' if test -f 'sub.c' then echo shar: "will not over-write existing file 'sub.c'" else cat << \!Funky!Stuff! > 'sub.c' /* * SUB.C * * Matthew Dillon, 24 Feb 1986 * */ #include <exec/types.h> #include <exec/exec.h> #include <libraries/dos.h> #include <libraries/dosextens.h> #include "shell.h" #define HM_STR 0 #define HM_REL 1 #define HM_ABS 2 extern struct FileLock *Lock(), *DupLock(), *CurrentDir(); extern struct FileLock *Clock; char * next_word(str) register char *str; { while (*str && *str != ' ' && *str != 9) ++str; while (*str && (*str == ' ' || *str == 9)) ++str; return (str); } char * compile_av(av, start, end) char **av; { char *cstr; int i, len; len = 0; for (i = start; i < end; ++i) len += strlen(av[i]) + 1; cstr = malloc(len + 1); *cstr = '\0'; for (i = start; i < end; ++i) { strcat (cstr, av[i]); strcat (cstr, " "); } return (cstr); } Free(ptr) char *ptr; { static char *old_ptr; if (old_ptr) free (old_ptr); old_ptr = ptr; } /* * Add new string to history (H_head, H_tail, H_len, * S_histlen */ add_history(str) char *str; { register struct HIST *hist; while (H_len > S_histlen) del_history(); hist = (struct HIST *)malloc (sizeof(struct HIST)); if (H_head == NULL) { H_head = H_tail = hist; hist->next = NULL; } else { hist->next = H_head; H_head->prev = hist; H_head = hist; } hist->prev = NULL; hist->line = malloc (strlen(str) + 1); strcpy (hist->line, str); ++H_len; } del_history() { if (H_tail) { --H_len; ++H_tail_base; free (H_tail->line); if (H_tail->prev) { H_tail = H_tail->prev; free (H_tail->next); H_tail->next = NULL; } else { free (H_tail); H_tail = H_head = NULL; } } } char * get_history(ptr) char *ptr; { register struct HIST *hist; register int len; int mode = HM_REL; int num = 1; char *str; char *result = NULL; if (ptr[1] >= '0' && ptr[1] <= '9') { mode = HM_ABS; num = atoi(&ptr[1]); goto skip; } switch (ptr[1]) { case '!': break; case '-': num += atoi(&ptr[2]); break; default: mode = HM_STR; str = ptr + 1; break; } skip: switch (mode) { case HM_STR: len = strlen(str); for (hist = H_head; hist; hist = hist->next) { if (strncmp(hist->line, str, len) == 0) { result = hist->line; break; } } break; case HM_REL: for (hist = H_head; hist && num--; hist = hist->next); if (hist) result = hist->line; break; case HM_ABS: len = H_tail_base; for (hist = H_tail; hist && len != num; hist = hist->prev, ++len); if (hist) result = hist->line; break; } if (result) puts (result); return (result); } replace_head(str) char *str; { if (str == NULL) str = ""; if (H_head) { free (H_head->line); H_head->line = malloc (strlen(str)+1); strcpy (H_head->line, str); } } perror(str) char *str; { ierror(str, IoErr()); } ierror(str, err) register char *str; { register struct PERROR *per = Perror; if (err) { for (; per->errstr; ++per) { if (per->errnum == err) { printf ("%s%s%s\n", per->errstr, (str) ? ": " : "", (str) ? str : ""); return (err); } } printf ("Unknown DOS error %ld %s\n", err, (str) ? str : ""); } return (err); } /* * Disk directory routines * * dptr = dopen(name, stat) * struct DPTR *dptr; * char *name; * int *stat; * * dnext(dptr, name, stat) * struct DPTR *dptr; * char **name; * int *stat; * * dclose(dptr) -may be called with NULL without harm * * dopen() returns a struct DPTR, or NULL if the given file does not * exist. stat will be set to 1 if the file is a directory. If the * name is "", then the current directory is openned. * * dnext() returns 1 until there are no more entries. The **name and * *stat are set. *stat = 1 if the file is a directory. * * dclose() closes a directory channel. * */ struct DPTR * dopen(name, stat) char *name; int *stat; { struct DPTR *dp; int namelen, endslash = 0; namelen = strlen(name); if (namelen && name[namelen - 1] == '/') { name[namelen - 1] = '\0'; endslash = 1; } *stat = 0; dp = (struct DPTR *)malloc(sizeof(struct DPTR)); if (*name == '\0') dp->lock = DupLock (Clock); else dp->lock = Lock (name, ACCESS_READ); if (endslash) name[namelen - 1] = '/'; if (dp->lock == NULL) { free (dp); return (NULL); } dp->fib = (struct FileInfoBlock *) AllocMem(sizeof(struct FileInfoBlock), MEMF_PUBLIC); if (!Examine (dp->lock, dp->fib)) { perror (name); dclose (dp); return (NULL); } if (dp->fib->fib_DirEntryType >= 0) *stat = 1; return (dp); } dnext(dp, pname, stat) struct DPTR *dp; char **pname; int *stat; { if (dp == NULL) return (0); if (ExNext (dp->lock, dp->fib)) { *stat = (dp->fib->fib_DirEntryType < 0) ? 0 : 1; *pname = dp->fib->fib_FileName; return (1); } return (0); } dclose(dp) struct DPTR *dp; { if (dp == NULL) return (1); if (dp->fib) FreeMem (dp->fib, sizeof(*dp->fib)); if (dp->lock) UnLock (dp->lock); free (dp); return (1); } free_expand(av) char **av; { char **base = av; if (av) { while (*av) { free (*av); ++av; } free (base); } } /* * EXPAND(wild_name, pac) * wild_name - char * (example: "df0:*.c") * pac - int * will be set to # of arguments. * * Standalone, except in requires Clock to point to the Current-Directory * lock. */ char ** expand(base, pac) char *base; int *pac; { char **eav = (char **)malloc (sizeof(char *)); int eleft, eac; char *ptr, *name; char *bname, *ename, *tail; int stat, scr; struct DPTR *dp; *pac = eleft = eac = 0; base = strcpy(malloc(strlen(base)+1), base); for (ptr = base; *ptr && *ptr != '?' && *ptr != '*'; ++ptr); for (; ptr >= base && !(*ptr == '/' || *ptr == ':'); --ptr); if (ptr < base) { bname = strcpy (malloc(1), ""); } else { scr = ptr[1]; ptr[1] = '\0'; bname = strcpy (malloc(strlen(base)+1), base); ptr[1] = scr; } ename = ptr + 1; for (ptr = ename; *ptr && *ptr != '/'; ++ptr); scr = *ptr; *ptr = '\0'; tail = (scr) ? ptr + 1 : NULL; if ((dp = dopen (bname, &stat)) == NULL || stat == 0) { free (bname); free (base); free (eav); puts ("Could not open directory"); return (NULL); } while (dnext (dp, &name, &stat)) { if (compare_ok(ename, name)) { if (tail) { int alt_ac; char *search, **alt_av, **scrav; struct FileLock *lock; if (!stat) /* expect more dirs, but this not a dir */ continue; lock = CurrentDir (Clock = dp->lock); search = malloc(strlen(name)+strlen(tail)+2); strcpy (search, name); strcat (search, "/"); strcat (search, tail); scrav = alt_av = expand (search, &alt_ac); CurrentDir (Clock = lock); if (scrav) { while (*scrav) { if (eleft < 2) { char **scrav = (char **)malloc(sizeof(char *) * (eac + 10)); bmov (eav, scrav, (eac + 1) << 2); free (eav); eav = scrav; eleft = 10; } eav[eac] = malloc(strlen(bname)+strlen(*scrav)+1); strcpy(eav[eac], bname); strcat(eav[eac], *scrav); free (*scrav); ++scrav; --eleft, ++eac; } free (alt_av); } } else { if (eleft < 2) { char **scrav = (char **)malloc(sizeof(char *) * (eac + 10)); bmov (eav, scrav, (eac + 1) << 2); free (eav); eav = scrav; eleft = 10; } eav[eac] = malloc (strlen(bname)+strlen(name)+1); eav[eac] = strcpy(eav[eac], bname); strcat(eav[eac], name); --eleft, ++eac; } } } dclose (dp); *pac = eac; eav[eac] = NULL; free (bname); free (base); if (eac) return (eav); free (eav); return (NULL); } /* * Compare a wild card name with a normal name */ #define MAXB 8 compare_ok(wild, name) char *wild, *name; { char *w = wild; char *n = name; char *back[MAXB][2]; int bi = 0; while (*n || *w) { switch (*w) { case '*': if (bi == MAXB) { puts ("Too many levels of '*'"); return (0); } back[bi][0] = w; back[bi][1] = n; ++bi; ++w; continue; goback: --bi; while (bi >= 0 && *back[bi][1] == '\0') --bi; if (bi < 0) return (0); w = back[bi][0] + 1; n = ++back[bi][1]; ++bi; continue; case '?': if (!*n) { if (bi) goto goback; return (0); } break; default: if (*n != *w) { if (bi) goto goback; return (0); } break; } if (*n) ++n; if (*w) ++w; } return (1); } Oputs(str) char *str; { Write (Cout, str, strlen(str)); Write (Cout, "\n", 1); } char * Ogets(str) char *str; { register int i = 0; while (Read(Cin, str + i, 1) == 1) { if (str[i] == '\n') { str[i] = 0; return (str); } if (++i == 255) { str[i] = 0; return (str); } } return (NULL); } !Funky!Stuff! fi # end of overwriting check exit 0 # End of shell archive