/*
 * COMM1.C
 *
 * Matthew Dillon, August 1986
 *
 * Version 2.07M by Steve Drew 10-Sep-87
 *
 * Version 3.02A by Carlo Borreo & Cesare Dieni 20-Dec-88
 *
 */

extern char *v_passed, *v_gotofwd, *v_cwd, *v_lasterr;

#define DIR_SHORT 0x01
#define DIR_FILES 0x02
#define DIR_DIRS  0x04

extern int has_wild;
char cwd[256];

/*
	Parse the options specified in sw[]
	Setting a bit in global variable options
	for each one found
*/

get_opt(sw,count)
char *sw;
int *count;
{
register char *c,*s;
unsigned int l,i = 0;

options=0;
while((++i < ac) && (av[i][0] == '-')) {
	for (c = av[i]+1; *c ; c++) {
		for(l = 0,s = sw;*s && *s != *c; ++s) ++l;
		if (*s) options |= (1 << l);
		}
	}
*count = i;
}

do_sleep()
{
int i;

if (ac == 2) {
	i = atoi(av[1]);
	while (i > 0) { Delay (100L); i -= 2; if (CHECKBREAK()) break; }
	}
return 0;
}

do_protect()
{
register long mask=0xf;
register char *s, *p, *flags="DEWRAPSH";
register unsigned int i;
for (s=av[--ac]; *s; s++)
	if (p=index(flags,Toupper(*s))) mask^=(1 << (p-flags));
	else ierror(av[ac],500);
for (i=1; i<ac; i++) if (!SetProtection(av[i],mask)) pError(av[i]);
return 0;
}

do_filenote()
{
char *note=av[--ac];
register unsigned int i;

for (i=1; i<ac; i++) if (!SetComment(av[i], note)) pError(av[i]);
return 0;
}

do_cat()
{
FILE *fopen(), *fi;
register unsigned int lctr;
unsigned int i;
char buf[256];

get_opt("n",&i);
if (i>=ac) {
	if (has_wild) { printf("No files matching\n"); return 20; }
	while (gets(buf) && !dobreak()) {
		if (options) printf("%4d ",++lctr);
		puts(buf);
		}
	}
for (; i<ac; i++)
	if (fi = fopen (av[i], "r")) {
		lctr=0;
		while (fgets(buf,256,fi) && !dobreak()) {
			if (options) printf("%4d ",++lctr);
			printf("%s",buf);
			}
		fclose (fi);
		}
	else pError(av[i]);
return 0;
}

do_info()
{
BPTR lock;
struct InfoData *info;
unsigned int size, free;
char *p,*s,*state;
struct DirectoryEntry *de_head=NULL, *de;

info=(struct InfoData *)AllocMem((long)sizeof(struct InfoData),MEMF_PUBLIC);
AddDADevs(&de_head, DLF_DEVICES | DLF_DISKONLY );
Myprocess->pr_WindowPtr = (APTR)(-1);
printf ("Unit  Size  Bytes  Used Blk/By-Free Full Errs  Status    Name\n");
for (de=de_head; de; de=de->de_Next) {
	printf("%-5s",de->de_Name);
	if (lock=Lock(de->de_Name,ACCESS_READ)) {
	    if (Info(lock, info)) {
		s = get_pwd(lock);
		if (p=index(s,':')) *p = '\0';
		size = ((info->id_NumBlocks + 2)* info->id_BytesPerBlock)/ 1024;
		free = (((info->id_NumBlocks-info->id_NumBlocksUsed))*
			   info->id_BytesPerBlock)/ 1024;
		switch(info->id_DiskState) {
			case ID_WRITE_PROTECTED: state="Read Only "; break;
			case ID_VALIDATED:	 state="Read/Write"; break;
			case ID_VALIDATING:	 state="Validating"; break;
			}
		printf("%4d%c%6ld%7ld%7ld%4d%c%4ld%%%4ld  %s %s\n",
			(size>1024) ? ((size+512) >> 10) : size,
			(size>1024) ? 'M' : 'K',
			info->id_BytesPerBlock,
			info->id_NumBlocksUsed,
			info->id_NumBlocks-info->id_NumBlocksUsed,
			(free>1024) ? ((free+512) >> 10) : free,
			(free>1024) ? 'M' : 'K',
			(info->id_NumBlocksUsed * 100)/info->id_NumBlocks,
			info->id_NumSoftErrors,
			state,
			s);
		}
	    else pError (de->de_Name);
	    UnLock(lock);
	    }
	else puts("  No disk present");
	}
FreeDAList(&de_head);
Myprocess->pr_WindowPtr = NULL;
FreeMem(info,(long)sizeof(struct InfoData));
return 0;
}

/* things shared with display_file */

char lspec[128];
int filecount, col;
long bytes, blocks;

/*
 * the args passed to do_dir will never be expanded
 */
do_dir()
{
   void display_file();
   int i;

   col = filecount = 0;
   bytes = blocks = 0L;
   *lspec = '\0';

   get_opt("sfd",&i);

   if (ac == i) {
      ++ac;
      av[i] = "";
   }
   if (!(options & (DIR_FILES | DIR_DIRS)))  options|=(DIR_FILES | DIR_DIRS);

   for (; i < ac; ++i) {
      char **eav;
      int c,eac;
      if (!(eav = expand(av[i], &eac)))
	  continue;
      QuickSort(eav, eac);
      for(c=0;c < eac && !breakcheck();++c) display_file(options,eav[c]);
      free_expand (eav);
      if (CHECKBREAK()) break;
   }
   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;
}

void
display_file(options,filestr)
int options;
char *filestr;
{
   long atol();
   int isadir,slen;
   char sc;
   char *c,*s,*fi;
   BPTR lock;

/*	if current dir different from lspec then
	look for ':' or '/' if found lock it and get_pwd.
	else then use cwd.
*/
   for(s = c = filestr; *c; ++c) if (*c == ':' || *c == '/') s = c;
   if (*s == ':') ++s;
   sc = *s;
   *s = '\0';
   c = filestr;
   if (!*c) c = cwd;
   if (strcmp (c, &lspec))  {
	strcpy(lspec, c);
	if (col) printf("\n");
	if (lock=Lock(c,SHARED_LOCK)) {
		printf("Directory of %s\n", get_pwd(lock));
		UnLock(lock);
		}
	col = 0;
	}
   *s = sc;
   if (sc == '/') s++;
   slen = strlen(s);
   fi = s + slen + 1;
   isadir = (fi[12] =='D');

   if (!(((options & DIR_FILES) && !isadir) ||
	  ((options & DIR_DIRS) &&  isadir)))
      return;
   if (isadir) printf ("\23333m");
   if (options & DIR_SHORT) {
	if (col==3 && slen>18) { printf("\n"); col = 0; }
	if (slen>18) { printf(" %-37s",s); col+= 2; }
		else { printf(" %-18s",s); col++; }
	if (col > 3) { printf("\n"); col=0; }
	}
   else printf("   %-24s %s",s ,fi);
   if (isadir) printf("\2330m");
   fflush(stdout);
   fi[16] = fi[21] = '\0';
   bytes  += atol(fi+10);
   blocks += atol(fi+17);
   filecount++;
   return;
}

do_quit()
{
if (Src_stack) {
	Quit = 1;
	return(do_return());
	}
main_exit(0);
}

do_echo(str)
register char *str;
{
char nl=1;

for (; *str && *str != ' '; ++str);
if (*str==' ') ++str;
if (av[1] && !strcmp(av[1],"-n")) {
	nl = 0;
	str += 2;
	if (*str==' ') ++str;
	}
printf("%s",str);
if (nl) printf("\n");
fflush(stdout);
return 0;
}

/* gets a line from file, joining two lines if the first ends in '\' */

char *myfgets(buf, buflen, file)
char *buf;
FILE *file;
{
char *bufptr=buf;
int remain=buflen, n, flag;

do {
	if (fgets(bufptr, remain, file)==NULL) {
		if (remain != buflen)
			fprintf(stderr,"Source: file ends in '\\'\n");
		return NULL;
		}
	n=strlen(buf);
	bufptr += n;
	if (flag= (*(bufptr-2)=='\\')) bufptr-=2;
	remain -= (n+2);
    } while (flag);
return buf;
}

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

if (Src_stack == MAXSRC) {
	ierror(NULL,217);
	return -1;
	}
if ((fi = fopen (av[1], "r")) == 0) { pError(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 (myfgets (buf, 256, fi) && !dobreak()) {
	int len = strlen(buf);
	buf[len-1] = '\0';
	Src_pos[Src_stack - 1] += len;
	if (Verbose && !forward_goto) fprintf(stderr,"%s\n",buf);
	exec_command (buf);
	}
--H_stack;
--Src_stack;
if (forward_goto) ierror(NULL,501);
forward_goto = 0;
unset_level(LEVEL_LABEL + Src_stack);
unset_var(LEVEL_SET, v_gotofwd);
unset_var(LEVEL_SET, v_passed);
fclose (fi);
return 0;
}

/*
 * return ptr to string that contains full cwd spec.
 */
char *get_pwd(flock)
BPTR flock;
{
static char pwdstr[130];
PathName(flock, pwdstr, 128L);
return pwdstr;
}

/*
 * set process cwd name and $_cwd, if str != NULL also print it.
 */
do_pwd(str)
char *str;
{
char *ptr;

if (Myprocess->pr_CurrentDir == 0)
	attempt_cd(":"); /* if we just booted 0 = root lock */
strcpy(cwd,get_pwd(Myprocess->pr_CurrentDir,1));
if (str) puts(cwd);
set_var(LEVEL_SET, v_cwd, cwd);
/* put the current dir name in our CLI task structure */
CtoBStr(cwd, Mycli->cli_SetName, 128L);
}

/*
 * CD
 *
 * 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)
char *str;
{
   char sc, *ptr;
   int err=0;

   str = next_word(str);
   if (*str == '\0') {
      puts(cwd);
      return(0);
   }
   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';
	  err = attempt_cd(str);
	  ptr[1] = sc;
	  break;
      case '\0':
      case '/':
	  *ptr = '\0';
	  if (strcmp(str, "..") == 0 || str == ptr)
	     str = "/";
	  if (*str) err = attempt_cd(str);
	  break;
      }
      if (err) break;
      str = ptr + 1;
   }
   do_pwd(NULL);	  /* set $_cwd */
   return err;
}

attempt_cd(str)
char *str;
{
BPTR oldlock, filelock;

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

do_mkdir()
{
register unsigned int i;
BPTR lock;

for (i=1; i<ac; ++i) {
	if (exists(av[i])) ierror(av[i],203);
	else if (lock=CreateDir(av[i])) UnLock (lock);
	else pError(av[i]);
	}
return 0;
}

do_mv()
{
char *dest, buf[256];
int dirflag;
register unsigned int i;

dirflag=isdir(dest=av[--ac]);
if (ac>3 && !dirflag) { ierror(dest, 507); return (-1); }
for (i=1; i<ac; ++i) {
	strcpy(buf, dest);
	if (dirflag) TackOn(buf, BaseName(av[i]));
	if (Rename(av[i], buf)==0)
		{ pError(av[i]); return -1; }
	}
return 0;
}

int dirstoo;

all_args(args, action, dirsflag)
char *args;
int (*action)();
{
unsigned int i;

get_opt(args, &i);
dirstoo=dirsflag;
for (; i<ac && !dobreak(); ++i)
	if (isdir(av[i])) {
		if (options & 1) recurse(av[i], action);
			else if (dirstoo) (*action)(av[i]);
		}
	else (*action)(av[i]);
return 0;
}

char *searchstring;

search_file(s)
char *s;
{
FILE *fopen(), *fi;
unsigned int lctr, len=strlen(searchstring);
int yesno;
char buf[256], lowbuf[256], first;
register char *p, *l, *t;

if (!(options & 2)) for (t=searchstring; *t=Toupper(*t); t++);
first=*searchstring;
if ((fi=fopen(s, "r"))==NULL) { pError(s); return; }
lctr=0;
if (!(options & 32)) printf("Examining %s...\n",s);
while (fgets(buf,256,fi) && !dobreak()) {
	lctr++;
	if (options & 4) yesno=compare_ok(searchstring,buf);
	else {
		yesno=0;
		p=buf;
		if (!(options & 2)) {
			l=lowbuf;			/* p is already =buf */
			while (*l++=Toupper(*p++));	/* lowbuf=upper(buf) */
			p=lowbuf;
			}
		while (p=index(p,first))
			if (!strncmp(p++,searchstring,len)) { yesno=1; break; }
		}
	if (yesno ^ ((options & 16)!=0) ) {
			/* default: print line numbers */
		if (!(options & 8)) printf("%4d ",lctr);
		printf("%s",buf);
		}
	}
fclose (fi);
}

do_search()
{
searchstring=av[--ac];
all_args("rcwneq", search_file, 0);
return 0;
}

rm_file(file)
char *file;
{
if (has_wild) printf(" %s...",file);
if (options & 2) SetProtection(file,0L);
if (!DeleteFile(file)) pError (file); else if (has_wild) printf("Deleted\n");
}

do_rm()
{
all_args("rp", rm_file, 1);
return 0;
}

recurse(name, action)
char *name;
int (*action)();
{
register BPTR lock, cwd;
register FIB *fib=(FIB *)AllocMem((long)sizeof(FIB),MEMF_PUBLIC);
char *namecopy=malloc(256);

if (name[0] =='\0') return;
namecopy[0]=0;
if (lock=Lock(name,ACCESS_READ)) {
    cwd =CurrentDir(lock);
    if (Examine(lock, fib))
	while (ExNext(lock, fib) && !CHECKBREAK()) {
	    if (*namecopy) { (*action)(namecopy); namecopy[0]=0; }
	    if (fib->fib_DirEntryType>=0) recurse(fib->fib_FileName,action);
		else strcpy(namecopy,fib->fib_FileName);
	    }
    if (*namecopy) (*action)(namecopy);
    UnLock(CurrentDir(cwd));
    if (dirstoo) (*action)(name);
    }
else pError(name);
free(namecopy);
FreeMem(fib, (long)sizeof(FIB));
}

do_history()
{
register struct HIST *hist;
int i = H_tail_base;
int len = (av[1]) ? strlen(av[1]) : 0;

for (hist = H_tail; hist && !dobreak(); hist = hist->prev)
	if (len == 0 || !strncmp(av[1], hist->line, len))
		printf("%3d %s\n", i++, hist->line);
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\nCHIP memory: %ld\n", ffree, cfree);
printf("Total  Free: %ld\n", cfree+ffree);
return 0;
}

/* forline i RAM:temp echo line $_linenum: $i */

do_forline()
{
char vname[33], buf[256];
register unsigned short lctr;
FILE *f;
char *cstr;
static char *linenumname="_linenum";

strcpy(vname,av[1]);
f=fopen(av[2],"r");
if (f==NULL) pError(av[2]);
lctr=0;
++H_stack;
cstr = compile_av (av, 3, ac, ' ');
while (fgets(buf,256,f) && !dobreak()) {
	buf[strlen(buf)-1]='\0';	/* remove CR */
	lctr++;
	set_var(LEVEL_SET, vname, buf);
	sprintf(buf,"%d",lctr);
	set_var(LEVEL_SET, linenumname, buf);
	exec_command(cstr);
	}
fclose(f);
--H_stack;
free (cstr);
unset_var (LEVEL_SET, vname);
unset_var (LEVEL_SET, linenumname);
return 0;
}

/* fornum i 1 10 echo $i */

do_fornum()
{
char vname[33], buf[16];
int n1, n2;
char *cstr;
register int i;

strcpy(vname,av[1]);
n1=myatoi(av[2],0 ,32767); if (Errno) return 20;
n2=myatoi(av[3],n1,    32767); if (Errno) return 20;
++H_stack;
cstr = compile_av (av, 4, ac, ' ');
for (i=n1; i<=n2 && !CHECKBREAK(); i++) {
	sprintf(buf,"%d",i);
	set_var (LEVEL_SET, vname, buf);
	exec_command(cstr);
	}
--H_stack;
free (cstr);
unset_var (LEVEL_SET, vname);
return 0;
}

/*
 * foreach var_name  ( str str str str... str ) commands
 * spacing is important (unfortunately)
 *
 * 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 cstart, cend;
register char *cstr;
char **fav;
char vname[33];
int i;

get_opt("v",&i);
strcpy(vname, av[i++]);
if (*av[i] == '(') i++;
cstart = i;
while (i<ac && *av[i] != ')') i++;
if (i > ac) { fprintf(stderr,"')' expected\n"); return 20; }
++H_stack;
cend = i;

fav = (char **)malloc(sizeof(char *) * (ac));
cstr = compile_av (av, cend + 1, ac, ' ');

for (i = cstart; i < cend; ++i) fav[i] = av[i];

for (i = cstart; i<cend && !CHECKBREAK(); ++i) {
	set_var (LEVEL_SET, vname, fav[i]);
	if (options & 1) printf("foreach: %s\n", fav[i]);
	exec_command(cstr);
	}
--H_stack;
free (fav);
free (cstr);
unset_var (LEVEL_SET, 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_exec(str)
char *str;
{
return exec_command(next_word(str));
}

extern struct Window *w;
extern struct IntuitionBase *IntuitionBase;

do_window()
{
long x, y, maxwidth, maxheight, arg[5];
unsigned int i;
struct Screen *screen;
struct Window *window;

get_opt("slfbaq", &i);
if (options & 1)
	SizeWindow(w, (long)(w->MinWidth-w->Width), (long)(w->MinHeight-w->Height));
if (options & 2) {
	x=-w->LeftEdge;
	y=-w->TopEdge;
	MoveWindow(w,x,y);
	x=IntuitionBase->ActiveScreen->Width -w->Width;
	y=IntuitionBase->ActiveScreen->Height-w->Height;
	SizeWindow(w,x,y);
	}
if (options & 4) WindowToFront(w);
if (options & 8) WindowToBack(w);
if (options & 16) ActivateWindow(w);
if(ac >= 5) {
	for(i=1; i<5; i++) {
		arg[i] = myatoi(av[i],0,1023);
		if (Errno) return 20;
		}
	maxwidth = w->WScreen->Width;
	maxheight= w->WScreen->Height;
	if (arg[3] > maxwidth - arg[1] || arg[4] > maxheight- arg[2]) {
		ierror(NULL, 500);
		return 20;
		}
	x = -w->LeftEdge;
	y = -w->TopEdge;
	MoveWindow(w, x, y);
	x = arg[3] - w->Width;
	y = arg[4] - w->Height;
	SizeWindow(w, x, y);
	x = arg[1];
	y = arg[2];
	MoveWindow(w, x, y);
	}
if(options & 32) {
	for (screen=IntuitionBase->FirstScreen; screen; screen=screen->NextScreen) {
	    printf("\nScreen \"%s\" (%d,%d,%dx%d):\n",
			screen->Title,
			screen->LeftEdge,
			screen->TopEdge,
			screen->Width,
			screen->Height
			);
		for (window=screen->FirstWindow; window; window=window->NextWindow) {
		printf("\tWindow\t\"%s\" (%d,%d,%dx%d)\n",
			window->Title,
			window->LeftEdge,
			window->TopEdge,
			window->Width,
			window->Height
			);
		}
	    }
	return 0;
	}
Delay(25L); /* pause 1/2 sec. before trying to print */
printf("\014");
return 0;
}

setsystemtime(ds)
struct DateStamp *ds;
{
struct timerequest tr;
long secs= ds->ds_Days*86400 + ds->ds_Minute*60 + ds->ds_Tick/TICKS_PER_SECOND;

if (OpenDevice(TIMERNAME, UNIT_VBLANK, &tr, 0L)) {
	fprintf(stderr,"Clock error: can't open timer device\n");
	return;
	}
tr.tr_node.io_Message.mn_Node.ln_Type = NT_MESSAGE;
tr.tr_node.io_Message.mn_Node.ln_Pri = 0L;
tr.tr_node.io_Message.mn_Node.ln_Name = NULL;
tr.tr_node.io_Message.mn_ReplyPort = NULL;
tr.tr_node.io_Command = TR_SETSYSTIME;
tr.tr_time.tv_secs = secs;
tr.tr_time.tv_micro = 0L;
if (DoIO (&tr)) fprintf(stderr,"Clock error: can't talk to timer device\n");
CloseDevice (&tr);
}

char tday[10];

char *dates(dss)
struct DateStamp *dss;
{
static char timestr[40];
char tdate[10], ttime[10];
struct DateTime dt;
struct DateStamp *myds=&(dt.dat_Stamp);

dt.dat_Format=FORMAT_DOS;
dt.dat_StrDay=tday;
dt.dat_StrDate=tdate;
dt.dat_StrTime=ttime;
dt.dat_Flags=NULL;
myds->ds_Days=dss->ds_Days;
myds->ds_Minute=dss->ds_Minute;
myds->ds_Tick=dss->ds_Tick;
StamptoStr(&dt);
sprintf(timestr,"%s %s\n",tdate,ttime);
timestr[18]='\n';
timestr[19]='\0';	/* protection against bad timestamped files */
return timestr;
}

date()
{
struct DateStamp dss;
register unsigned short i;
struct DateTime dt;

dt.dat_Format=FORMAT_DOS;
if (ac==1) {
	DateStamp(&dss);
	printf("%s %s",tday,dates(&dss));
	}
else {
	DateStamp(& (dt.dat_Stamp));
	for (i=1; i<ac; i++) {
		dt.dat_StrDate=NULL;
		dt.dat_StrTime=NULL;
		dt.dat_Flags=DTF_FUTURE;
		if (index(av[i],':')) dt.dat_StrTime=av[i];
			else dt.dat_StrDate=av[i];
		if (StrtoStamp(&dt)) ierror(av[i],500);
		}
	setsystemtime( & (dt.dat_Stamp) );
	}
return 0;
}
