
/*
 * COMM1.C
 *
 * (c)1986 Matthew Dillon     9 October 1986
 *
 *    SLEEP
 *    NUMBER	  -handles values as commands, actually a NULL command.
 *    CAT
 *    COMMENT
 *    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 "shell.h"
#include <stdio.h>

extern LOCK *CreateDir(), *CurrentDir(), *ParentDir();
extern LOCK *Lock(), *DupLock();

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()
{
    FILE *fi;
    short i;
    char buf[256];

    if (ac == 1) {
	while (fgets(buf, 256, stdin)) {
	    Write(Cout, buf, strlen(buf));
	    if (CHECKBREAK())
		break;
	}
	clearerr(stdin);
    }
    for (i = 1; i < ac; ++i) {
	if (fi = fopen (av[i], "r")) {
	    while (fgets (buf, 256, fi)) {
		Write(Cout, buf, strlen(buf));
		if (CHECKBREAK())
		    break;
	    }
	    fclose(fi);
	} else {
	    fhprintf (Cerr, "could not open %s\n", av[i]);
	}
    }
    return (0);
}

/*
 * comment file string
 */

do_comment(str)
char *str;
{
    register char *ptr = next_word(next_word(str));

    if (SetComment(av[1], ptr) == 0) {
	perror(av[1]);
	return(1);
    }
    return(0);
}

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

    if (av[1][0] == '-') {
	++avstart;
	for (i = 1; av[1][i]; ++i) {
	    switch(av[1][i]) {
	    case 's':
		longmode = 0;
		break;
	    case 'l':
		longmode = 1;
		break;
	    case 'C':
		dcomment = 0;
		break;
	    }
	}
    }
    if (ac == avstart)
	av[ac++] = "";
    for (i = avstart; !br && 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;
		fhprintf (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, dcomment);
		    if (CHECKBREAK()) {
			br = 1;
			break;
		    }
		}
	    } else {
		total += disp_entry(dp->fib, longmode, dcomment);
	    }
	}
	dclose (dp);
    }
    fhprintf (Cout, "TOTAL: %ld\n", total);
    return (0);
}

static long
disp_entry(fib, lengthy, comment)
register struct FileInfoBlock *fib;
{
    char str[6];
    char dstr[32];
    register char *dirstr;

    str[5] = '\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';
    str[4] = (fib->fib_Protection & FIBF_ARCHIVE) ? '-' : 'a';
    dirstr = (fib->fib_DirEntryType < 0) ? "   " : "DIR";

    fhprintf (Cout, "%s %6ld %s  %-20s", str, (long)fib->fib_Size,
	dirstr, fib->fib_FileName
    );
    if (lengthy)
	fhprintf (Cout, " %s", datetos(&fib->fib_Date, dstr, "D M Y h:m"));
    if (comment)
	fhprintf (Cout, " %s", fib->fib_Comment);
    fhprintf(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 FILE *fi;
    register char *buf;

    buf = malloc(256);
    if (buf == NULL) {
	Eputs ("no memory");
	goto error;
    }
    if (Src_stack == MAXSRC) {
	Eputs ("Too many source levels");
error:
	if ((long)av[0] == -1)
	    UnLock(av[1]);
	return(-1);
    }
    if ((long)av[0] == -1) {
	long oldir = (long)CurrentDir(av[1]);
	fi = fopen("", "r");
	UnLock(CurrentDir(oldir));
    } else {
	fi = fopen (av[1], "r");
    }
    if (fi == NULL) {
	fhprintf(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] = (long)fi;
    ++Src_stack;
    while (fgets(buf, 256, fi)) {
	register int len = strlen(buf);
	buf[len-1] = 0; 		    /* remove \n		*/
	Src_pos[Src_stack - 1] += len;	    /* + (1 + actual length)    */
	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);
    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)
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 *)((PROC *)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)) {
	    UnLock(CurrentDir(filelock));
	    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);
}

/*
 *  MV file1 file2
 *  MV file1 file2....fileN dir
 */

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

    --ac;
    if (isdir(av[ac])) {
	len = strlen(av[ac]);
	for (i = 1; i < ac; ++i) {
	    str = av[i] + strlen(av[i]) - 1;
	    while (str >= av[i] && *str != '/' && *str != ':')
		--str;
	    ++str;
	    if (*str == 0) {
		ierror(av[i], 508);
		return (-1);
	    }
	    strcpy(dest, av[ac]);
	    if (dest[0] && dest[len-1] != ':' && dest[len-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("mv:", 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) {
	    fhprintf (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)
	fhprintf (Cout, "FAST memory:%10ld\n", ffree);
    fhprintf (Cout, "CHIP memory:%10ld\n", cfree);
    fhprintf (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);
}


