
/*
 * COMM1.C
 *
 * (c)1986 Matthew Dillon     9 October 1986
 *
 *    SLEEP
 *    NUMBER      -handles values as commands, actually a NULL command.
 *    CAT
 *    DIR         -also handles DEVINFO
 *    QUIT        -also handles EXIT
 *    ECHO
 *    SOURCE
 *    CD
 *    MKDIR
 *    MV
 *    RM
 *    HISTORY
 *    MEM
 *    FOREACH
 *    FOREVER
 *
 *    NOTE: SET/UNSET/ALIAS/UNALIAS handled in SET.C
 *
 */

#include <exec/types.h>
#include <exec/exec.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <lattice/fcntl.h>
#include "shell.h"

typedef struct FileInfoBlock FIB;
typedef struct FileLock LOCK;

extern struct FileLock  *CreateDir(), *CurrentDir(), *ParentDir();
extern struct FileLock  *Lock(), *DupLock();
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 (0);
}


do_number()
{
   return (0);
}

do_cat()
{
   long fi;
   short i;
   char buf[256];

   if (ac == 1) {
      while (Ogets(buf))
         Oputs(buf);
      return (0);
   }
   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 {
         fprintf (Cerr, "could not open %s\n", av[i]);
      }
   }
   return (0);
}


do_dir(garbage, com)
char *garbage;
{
   register struct DPTR          *dp;
   register struct InfoData      *info;
   char *name;
   register short i;
   int   stat;
   short longmode = 0;
   register long total = 0;

   if (strcmp(av[1], "-l") == 0)
      longmode = 1;
   if (ac == 1 + longmode)
      av[ac++] = "";
   for (i = longmode + 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 %%full: %ld\n",
                  info->id_UnitNumber,
                  info->id_NumSoftErrors,
                  bpb * info->id_NumBlocks,
                  bpb * (info->id_NumBlocks - info->id_NumBlocksUsed),
                  info->id_NumBlocksUsed * 100 / info->id_NumBlocks);
         } else {
            perror (av[i]);
         }
         FreeMem (info, sizeof(*info));
      } else {
         if (stat) {
            while (dnext (dp, &name, &stat)) {
               total += disp_entry (dp->fib, longmode);
               if (CHECKBREAK())
                  break;
            }
         } else {
            total += disp_entry(dp->fib, longmode);
         }
      }
      dclose (dp);
   }
   fprintf (Cout, "TOTAL: %ld\n", total);
   return (0);
}


char *
datetos(d)
register struct DateStamp *d;
{
   static char stamp[32];
   static char dim[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
   static char *Month[12] = { "Jan","Feb","Mar","Apr","May","Jun","Jul",
                              "Aug","Sep","Oct","Nov","Dec" };
   register long day, month, year, scr;

   day = d->ds_Days;       /* iteration (could be done w/equations) */
   year = 1978;

   while (day >= (scr = ((year&3)|!(year%100)) ? 365 : 366)) {
      ++year;
      day -= scr;
   }
   dim[1] = ((year&3)|!(year%100)) ? 28 : 29;
   for (month = 0; day >= dim[month]; (day -= dim[month]), ++month);
   sprintf(stamp, "%2ld %s %2ld:%ld%ld %4ld",
      day + 1, Month[month], d->ds_Minute/60,
      d->ds_Minute%60/10, d->ds_Minute%10, year);
   return (stamp);
}



static long
disp_entry(fib, lengthy)
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  %-20s", str, (long)fib->fib_Size,
      dirstr, fib->fib_FileName);
   if (lengthy)
      fprintf (Cout, " %s\n", datetos(&fib->fib_Date));
   else
      fprintf (Cout, "\n");
   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 (0);
}


do_source(str)
char *str;
{
   register long fi;
   char buf[256];

   if (Src_stack == MAXSRC) {
      Eputs ("Too many source levels");
      return(-1);
   }
   fi = xopen (av[1], "r", 512);
   if (fi == 0) {
      fprintf (Cerr, "Cannot open %s\n", next_word(str));
      return(-1);
   }
   set_var(LEVEL_SET, V_PASSED, next_word(next_word(str)));
   ++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)
         Eputs(buf);
      exec_command (buf);
      if (CHECKBREAK())
         break;
   }
   --H_stack;
   --Src_stack;
   unset_level(LEVEL_LABEL + Src_stack);
   unset_var(LEVEL_SET, V_PASSED);
   xclose (fi);
   return (0);
}

/*
 * CD
 *
 * CD(str, -1)      -do pwd and display current cd. if str = NULL don't disp.
 * CD(str, 0)       -do CD operation.
 *
 *    standard operation: breakup path by '/'s and process independantly
 *    x:    -reset cwd base
 *    ..    -remove last cwd element
 *    N     -add N or /N to cwd
 */

do_cd(str, com)
register char *str;
{
   static char cwd[256];
   register char sc, *ptr;
   char *name;

   if (com < 0) {
      register struct FileLock *lock, *newlock;
      register FIB *fib;
      short i, len;

      fib = (FIB *)AllocMem(sizeof(FIB), 0);
      Clock = (struct FileLock *)((struct Process *)FindTask(0))->pr_CurrentDir;
      if (!Clock)
         CurrentDir(Clock = Lock(":", ACCESS_READ));
      lock = DupLock(Clock);
      cwd[i = 255] = '\0';
      while (lock) {
         newlock = ParentDir(lock);
         Examine(lock, fib);
         name = fib->fib_FileName;
         if (*name == '\0')            /* HACK TO FIX RAM: DISK BUG */
            name = "ram";
         len = strlen(name);
         if (newlock) {
            if (i == 255) {
               i -= len;
               bmov(name, cwd + i, len);
            } else {
               i -= len + 1;
               bmov(name, cwd + i, len);
               cwd[i+len] = '/';
            }
         } else {
            i -= len + 1;
            bmov(name, cwd + i, len);
            cwd[i+len] = ':';
         }
         UnLock(lock);
         lock = newlock;
      }
      FreeMem(fib, sizeof(FIB));
      bmov(cwd + i, cwd, 256 - i);
      if (str)
         Oputs(cwd);
      goto cdset;
   }
   str = next_word(str);
   if (*str == '\0')
      Oputs(cwd);
   str[strlen(str)+1] = '\0';          /* add second \0 on end */
   while (*str) {
      for (ptr = str; *ptr && *ptr != '/' && *ptr != ':'; ++ptr);
      switch (*ptr) {
      case ':':
         sc = ptr[1];
         ptr[1] = '\0';
         if (attempt_cd(str))
            strcpy(cwd, str);
         ptr[1] = sc;
         break;
      case '\0':
      case '/':
         *ptr = '\0';
         if (strcmp(str, "..") == 0 || str == ptr)
            str = "/";
         if (*str && attempt_cd(str)) {
            if (*str == '/') {
               rmlast(cwd);
            } else {
               if (cwd[0] == 0 || cwd[strlen(cwd)-1] != ':')
                  strcat(cwd, "/");
               strcat(cwd, str);
            }
         }
         break;
      }
      str = ptr + 1;
   }
cdset:
   set_var(LEVEL_SET, V_CWD, cwd);
   return (0);
}

attempt_cd(str)
char *str;
{
   register struct FileLock *oldlock, *filelock;

   if (filelock = Lock(str, ACCESS_READ)) {
      if (isdir(str)) {
         if (oldlock = CurrentDir(filelock))
            UnLock(oldlock);
         Clock = filelock;
         return (1);
      }
      UnLock(filelock);
      ierror(str, 212);
   } else {
      ierror(str, 205);
   }
   return (0);
}


/*
 * remove last component. Start at end and work backwards until reach
 * a '/'
 */

rmlast(str)
char *str;
{
   register char *ptr = str + strlen(str) - 1;

   while (ptr != str && *ptr != '/' && *ptr != ':')
      --ptr;
   if (*ptr != ':')
      ptr[0] = '\0';
   else
      ptr[1] = '\0';
}


do_mkdir()
{
   register short i;
   register struct FileLock *lock;

   for (i = 1; i < ac; ++i) {
      if (lock = CreateDir (av[i])) {
         UnLock (lock);
         continue;
      }
      perror (av[i]);
   }
   return (0);
}


do_mv()
{
   char dest[256];
   register short i;
   register char *str;

   --ac;
   if (isdir(av[ac])) {
      for (i = 1; i < ac; ++i) {
         str = av[i] + strlen(av[i]) - 1;
         while (str != av[i] && *str != '/' && *str != ':')
            --str;
         if (str != av[i])
            ++str;
         if (*str == 0) {
            ierror(av[i], 508);
            return (-1);
         }
         strcpy(dest, av[ac]);
         if (dest[strlen(dest)-1] != ':')
            strcat(dest, "/");
         strcat(dest, str);
         if (Rename(av[i], dest) == 0)
            break;
      }
      if (i == ac)
         return (1);
   } else {
      i = 1;
      if (ac != 2) {
         ierror("rm:", 507);
         return (-1);
      }
      if (Rename (av[1], av[2]))
         return (0);
   }
   perror (av[i]);
   return (-1);
}


do_rm()
{
   register short i, recur;

   recur = (strncmp(av[1], "-r", 2)) ? 0 : 1;

   for (i = 1 + recur; i < ac; ++i) {
      if (isdir(av[i]) && recur)
         rmdir(av[i]);
      if (!DeleteFile(av[i]))
         perror(av[i]);
   }
   return (0);
}

rmdir(name)
char *name;
{
   register LOCK *lock, *cwd;
   register FIB *fib;
   register char *buf;

   buf = (char *)AllocMem(256, 0);
   fib = (FIB *)AllocMem(sizeof(FIB), 0);

   if (lock = Lock(name, ACCESS_READ)) {
      cwd = CurrentDir(lock);
      if (Examine(lock, fib)) {
         buf[0] = 0;
         while (ExNext(lock, fib)) {
            if (isdir(fib->fib_FileName))
               rmdir(fib->fib_FileName);
            if (buf[0]) {
               if (!DeleteFile(buf))
                  perror(buf);
            }
            strcpy(buf, fib->fib_FileName);
         }
         if (buf[0]) {
            if (!DeleteFile(buf))
               perror(buf);
         }
      }
      UnLock(CurrentDir(cwd));
   } else {
      perror(name);
   }
   FreeMem(fib, sizeof(FIB));
   FreeMem(buf, 256);
}


do_history()
{
   register struct HIST *hist;
   register short i = H_tail_base;
   register short 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 (0);
}


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 (0);
}

/*
 * 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 short 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) {
      Eputs ("')' 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 (0);
}


do_forever(str)
register char *str;
{
   long rcode = 0;
   register char *ptr = next_word(str);

   ++H_stack;
   for (;;) {
      if (CHECKBREAK()) {
         rcode = 20;
         break;
      }
      if (exec_command (ptr) < 0) {
         str = get_var(LEVEL_SET, V_LASTERR);
         rcode = (str) ? atoi(str) : 20;
         break;
      }
   }
   --H_stack;
   return (rcode);
}


