/*
 * COMM1.C
 *
 * Matthew Dillon, August 1986
 *
 * version 2.04M (Manx ver) by Steve Drew 9-Dec-86
 *
 */

#include "shell.h"
typedef struct FileInfoBlock FIB;
 
#define DIR_SHORT 0x01
#define DIR_FILES 0x02
#define DIR_DIRS  0x04

#define BPTR_TO_C(strtag, var)  ((struct strtag *)(BADDR( (ULONG) var)))
#define C_TO_BPTR(strtag, var)  ((struct strtag *)(((ULONG)var)>>2))

extern char *btocstr();
extern int has_wild;
char cwd[256];
struct FileLock *Clock;
  
do_sleep()
{
   register int i;
 
   if (ac == 2) {
      i = atoi(av[1]);
      while (i > 0) {
         Delay ((long)50*2);
         i -= 2;
         if (CHECKBREAK())
            break;
      }
   }
   return (0);
}
 
 
do_number()
{
   return (0);
}
 
do_cat()
{
   FILE *fopen(), *fi;
   int i;
   char buf[256];

   if (ac == 1) {
      while (gets(buf)) {
      	 if (CHECKBREAK()) break;
         puts(buf);
         }
      clearerr(stdin);
      return (0);
   }

   for (i = 1; i < ac; ++i) {
      if ((fi = fopen (av[i], "r")) != 0) {
           while (fgets(buf,256,fi)) {
            fputs(buf,stdout);
            fflush(stdout); 
            if (CHECKBREAK()) {
               break;
            }
         }
         fclose (fi);
      } else {
         fprintf (stderr, "could not open %s\n", av[i]);
      }
   }
   return (0);
}

/* things shared with disp_entry */

int   filecount, col; 
long  bytes, blocks;

do_dir(garbage, com)
char *garbage;
{
   void   		disp_entry();
   struct DPTR          *dp;
   struct InfoData      *info;
   char                 *name;
   int                  i = 0, stat, clen, more;
   char                 options = 0;
   char                 *c;
   char                 lspec[256];
   char                 volume[40];
   char                 *volname();
   char			*dates();
  
   col = filecount = 0;
   bytes = blocks = 0L;
   
   while((++i < ac) && (av[i][0] == '-')) {
   	for (c = av[i]+1; *c ; c++) {
   		switch(*c) {
   			case 's':
   				options |= DIR_SHORT;
   				break;
   			case 'f':
   				options |= DIR_FILES;
   				break;
   			case 'd':
   				options |= DIR_DIRS;
   				break;
   			default:
   				break;
   		}
   	}
   }
   
   if (ac == i) {
      ++ac;
      av[i] = "";
   if (has_wild)
      return(0);
   }
   if (!(options & (DIR_FILES | DIR_DIRS)))  options |= (DIR_FILES | DIR_DIRS);
   
   for (; i < ac; ++i) {
      if (!(dp = dopen (av[i], &stat)))
         continue;
      if (com < 0) {
         info = (struct InfoData *)AllocMem((long)sizeof(struct InfoData), MEMF_PUBLIC);
         if (Info (dp->lock, info)) {
            printf ("Unit:%2ld  Errs:%3ld  Used: %-4ld %3ld%% Free: %-4ld  Volume: %s\n",
                  info->id_UnitNumber,
                  info->id_NumSoftErrors,
                  info->id_NumBlocksUsed,
                  (info->id_NumBlocksUsed * 100)/ info->id_NumBlocks,
                  (info->id_NumBlocks - info->id_NumBlocksUsed),
                  volname(dp->lock,volume));

         } else {
            pError (av[i]);
         }
         FreeMem (info,(long) sizeof(*info));
         dclose(dp);
         continue;
         return(0);
      } 
 
     /* start of directory routine */
      
            c = av[i];
            clen = strlen(c);
            if (!stat || has_wild) {    /* if not wild and is a dir don't */
                                         /* extract dir from file name     */
               while (clen && c[clen] != '/' && c[clen] != ':') clen--;
               if (c[clen] == ':' || c[clen] == '/') clen++;
               c[clen] = '\0';
            }
            if (!clen)  c = cwd;
            if (strcmp (c, &lspec) != 0)  {
               strcpy(lspec, c);
               if (col)    printf("\n");
               printf ("Directory of %s\n", lspec);
               fflush(stdout);
               col = 0;
            } 
            more = stat;
            do {
            	if (more && !has_wild) {
            	    *lspec = '\0';
            	    if (!(more = dnext(dp, &name, &stat)))
            	    break;
            	}
                if (CHECKBREAK()) {
              	   i = ac;
              	   break;
                }
                disp_entry (dp->fib, options);
      } while(more && !has_wild);     
      dclose(dp); 
    }                /* end for */
   if (col)  printf("\n");    
   if (filecount > 1) {
       blocks += filecount;     /* account for dir blocks */
       printf (" %ld Blocks, %ld Bytes used in %d files\n", blocks, bytes, filecount);
   }
   return (0);
}

char *
volname(lock,buf)
struct FileLock *lock;
char *buf;
{

        struct    DeviceList    *dl;
        char                    *p;

     Forbid();
                /* Only way I know to get Volume label since InfoData  */
                /* seems to always have NULL for this string           */

        lock = BPTR_TO_C(FileLock, lock);
        dl = BPTR_TO_C(DeviceList, lock->fl_Volume);
        p = btocstr(dl->dl_Name,buf);

     Permit();
     return p;
}

void
disp_entry(fib, options)
char options;
register struct FileInfoBlock *fib;
{

   char str[5];
   int italics;
   
   
   if (!(((options & DIR_FILES) && (fib->fib_DirEntryType < 0)) || 
         ((options & DIR_DIRS)  && (fib->fib_DirEntryType > 0))))
                return;
                
 if (!(options & DIR_SHORT)) { 
   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) ? '-' : 'e';
   str[3] = (fib->fib_Protection & FIBF_DELETE) ? '-' : 'd';

   printf ("   %-24s  %s  ", fib->fib_FileName, str);
   if (fib->fib_DirEntryType < 0) printf("%6ld %4ld", (long)fib->fib_Size, (long)fib->fib_NumBlocks);
    else printf("   Dir     ");
   printf("  %s", dates(&fib->fib_Date));
   fflush(stdout);
   } 
   else {
   
        if ((col == 3) && strlen(fib->fib_FileName)>18) {
            printf("\n");
            col = 0;
        } 
        if (fib->fib_DirEntryType > 0)  {
            printf ("\033[3m");
            italics = 1;
        }
        if (strlen(fib->fib_FileName)>18) {
            printf(" %-37s",fib->fib_FileName);
            col += 2;
        } 
        else { 
            printf(" %-18s",fib->fib_FileName);
            col++;
        } 
        if (col > 3) {
            printf("\n");
            col = 0;
        }
        if (italics) printf("\033[0m");
   }
   fflush(stdout);
   blocks += fib->fib_NumBlocks;
   bytes  += fib->fib_Size;
   filecount++;
   return;
}

/* converts dos date stamp to a time string of form dd-mmm-yy  */

char *
dates(dss)
struct DateStamp *dss;
{
   register struct tm tm;
   register long time, t;
   register int i;
   static char timestr[20];
   static char months[12][4] = {
   	"Jan","Feb","Mar","Apr","May","Jun",
   	"Jul","Aug","Sep","Oct","Nov","Dec"
   };
   static char days[12] = {
   	31,28,31,30,31,30,31,31,30,31,30,31
   };
   time = dss->ds_Days * 24 * 60 * 60 + dss->ds_Minute * 60 +
   				       dss->ds_Tick/TICKS_PER_SECOND;
   tm.tm_sec = time % 60; time /= 60;
   tm.tm_min = time % 60; time /= 60;
   tm.tm_hour= time % 24; time /= 24;
   tm.tm_wday= time %  7;
   tm.tm_year= 78 + (time/(4*365+1)) * 4; time %= 4 * 365 + 1;
   while (time) {
   	t = 365;
   	if ((tm.tm_year&3) == 0) t++;
   	if (time < t) break;
   	time -= t;
   	tm.tm_year++;
   }
   tm.tm_yday = ++time;
   for (i=0;i<12;i++) {
   	t = days[i];
   	if (i == 1 && (tm.tm_year&3) == 0) t++;
   	if (time <= t) break;
   	time -= t;
   }  
   tm.tm_mon = i;
   tm.tm_mday = time;
   
   sprintf(timestr,"%02d-%s-%2d %02d:%02d:%02d\n",tm.tm_mday,
   		months[tm.tm_mon],tm.tm_year,
   		tm.tm_hour,tm.tm_min,tm.tm_sec);
   return(timestr);
   
}
 
date()
{
   struct   DateStamp   dss;
   char *s, *dates();

   DateStamp(&dss);
   s = dates(&dss);
   printf("%s",s);
   return(0);
}
 
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;
   }
   printf("%s",ptr);
   fflush(stdout);
   if (nl)
      printf("\n");
   return (0);
}

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

   if (Src_stack == MAXSRC) {
      printf (stderr,"Too many source levels\n");
      return(-1);
   }
   if ((fi = fopen (av[1], "r")) == 0) {
      fprintf (stderr,"Cannot open %s\n", av[1]);
      return(-1);
   }   
   set_var(LEVEL_SET, V_PASSED, next_word(next_word(str)));
   ++H_stack;
   Src_pos[Src_stack] = 0;
   Src_base[Src_stack] = (long)fi;
   ++Src_stack;
   while (fgets (buf, 256, fi)) {
      buf[strlen(buf)-1] = '\0';
      Src_pos[Src_stack - 1] += 1+strlen(buf);
      if (Verbose)
         fprintf(stderr,"%s\n",buf);
      exec_command (buf);
      if (CHECKBREAK())
         break;
   }
   --H_stack;
   --Src_stack;
   unset_level(LEVEL_LABEL + Src_stack);
   unset_var(LEVEL_SET, V_PASSED);
   fclose (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)
char *str;
{
   char sc, *ptr;
   char *name;

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

      fib = (FIB *)AllocMem((long)sizeof(FIB), MEMF_PUBLIC);
      if ((Clock = (struct FileLock *)Myprocess->pr_CurrentDir) == 0) 
          attempt_cd(":"); /* if we just booted 0 = root lock */
      lock = (struct FileLock *)DupLock(Clock);
      cwd[i = 255] = '\0';
      
      while (lock) {
         newlock = (struct FileLock *)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, (long)sizeof(FIB));
      bmov(cwd + i, cwd, 256 - i);
      if (str)
         puts(cwd);
      set_var(LEVEL_SET, V_CWD, cwd);
 
     /* put the current dir name in our CLI task structure */

      ptr = (char *)((ULONG)((struct CommandLineInterface *)
          BADDR(Myprocess->pr_CLI))->cli_SetName << 2);
      ptr[0] = strlen(cwd);
      movmem(cwd,ptr+1,(int)ptr[0]);
      return (0);
   }
   str = next_word(str);
   if (*str == '\0')
      puts(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;
   }
   do_cd(NULL,-1);
}

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

   if (filelock = (struct FileLock *)Lock(str, ACCESS_READ)) {
      if (isdir(str)) {
         if (oldlock = (struct FileLock *)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;
{
   char *ptr = str + strlen(str) - 1;
   while (ptr != str && *ptr != '/' && *ptr != ':')
      --ptr;
   if (*ptr != ':')
      ptr[0] = '\0';
   else
      ptr[1] = '\0';
}


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

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


do_mv()
{
   char dest[256];
   register int i;
   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 int i;

   for (i = 1; i < ac; ++i) {
      if (ac !=2) printf("  %s...",av[i]);
      fflush(stdout);
      if (CHECKBREAK()) break;
      if (!DeleteFile(av[i]))
         pError (av[i]);
     else {
        if (ac !=2) printf("Deleted\n");
          }
   }
   return (0);
}
 
 
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) {
         printf ("%3d ", i);
         puts (hist->line);
      }
      ++i;
      if (CHECKBREAK())
         break;
   }
   return (0);
}
 
do_mem()
{
   long cfree, ffree;
   extern long AvailMem();

   Forbid();
   cfree = AvailMem (MEMF_CHIP);
   ffree = AvailMem (MEMF_FAST);
   Permit();

   if (ffree)       {
   printf ("FAST memory: %ld\n", ffree);  
   printf ("CHIP memory: %ld\n", cfree);   
   }
   printf ("Total  Free: %ld\n", cfree + ffree);
   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 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) {
      fprintf (stderr,"')' expected\n");
      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)
char *str;
{
   int rcode = 0;
   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);
}


 

do_copy()
{
   char tofile[60],todir[256];
   int i;
   char *p;

   todir[0] = '\0';
   --ac;
   if (ac == 1)
         strcpy(todir, cwd);
   else if (ac == 2) {
         strcpy(todir, av[ac]);
         }
   else  {
         strcpy (todir,av[ac]);
         --ac;
         if (strcmp(av[ac],"to") && strcmp(av[ac],"TO"))  ++ac;
        }
   if (ac == 1) ++ac;

   for (i = 1;i < ac; ++i) {
      if (isdir(todir))  {
        if (todir[strlen(todir)-1] != '/' && todir[strlen(todir)-1] != ':')
                     strcat(todir,"/");
        strcpy(tofile, todir);
        p = &av[i][strlen(av[i])-1];
        while (p != av[i] && *p != ':' && *p != '/')
           p--;
        if (*p == ':' || *p == '/')
           p++;
        strcat(tofile, p);
        }
      else   {
        strcpy(tofile, todir);
        if (ac > 2) {
          fprintf (stderr,"Copy error: %s is not a directory\n", todir);
          return(-1);
          }
      }

   /* if from file is a dir name don't copy     */

   if (isdir(av[i])) continue;

   printf("copying %s to %s....", av[i], tofile);
   fflush(stdout);
   if (!file_copy(av[i], tofile)) printf("[ok]\n");
   if (CHECKBREAK()) {
      return(0);
      }
   }
   return(0);
}

file_copy(s, tofile)
char  *s, *tofile;
{
   struct      FileHandle  *copyin;
   struct      FileHandle  *copyout;
   long        size;
   long        actual;
   int         ok = 1;
   char        *copybuf;

      copyin = Open(s, MODE_OLDFILE);
   if (copyin == 0L)
      pError(s);

 else {
        copyout = Open(tofile, MODE_NEWFILE);

      if (copyout == 0) {
         pError(tofile);
         Close(copyin);
         }
      else {

         size = Seek(copyin, 0L, OFFSET_END);
         size = Seek(copyin, 0L, OFFSET_BEGINING);

       if (size != 0L) {                     /* if zero length close em */
         do {
            copybuf = AllocMem((long)size, MEMF_PUBLIC|MEMF_CLEAR);
            if (copybuf == 0L)
               size = size/2;
            }
         while (copybuf == 0L & size > 512);
         if (copybuf == 0L) {
           fprintf(stderr,"Copy Error: Not Enough Memory\n");
            }
         else
            do {
               ok = 0;
               actual = Read(copyin, copybuf, size);
               if (Write(copyout, copybuf, actual) != actual) {
                  ok = pError(NULL);
                  break;
                  }
               }
            while (actual == size);
          }
            Close(copyin);
            Close(copyout);
         FreeMem(copybuf,(long) size);
         if (size == 0L) ok = 0;
         }
     }
return(ok);
}

