
/*
 * SUB.C
 *
 * (c)1986 Matthew Dillon     9 October 1986
 *
 * Version 2.07M by Steve Drew 10-Sep-87
 * Version 4.01A by Carlo Borreo & Cesare Dieni 17-Feb-90
 * Version 5.00L by Urban Mueller 17-Feb-91
 *
 */

#include "shell.h"

static void del_history( void );
static int dnext( DPTR *dp, char **pname, int *stat);
static char *svfile( char *s1, char *s2, FIB *fib);
static void quicksort( char **av, int n );

void
seterr( int err )
{
	static int LastErr;
	char buf[32], *val;
	int  stat=0;

	Lastresult=err;

	if( LastErr!=err ) {
		LastErr=err;
		sprintf(buf, "%d", err);
		set_var(LEVEL_SET, v_lasterr, buf);

		if( val=get_var(LEVEL_SET, v_stat))
			stat = atoi(val);
		if (stat < Lastresult) set_var(LEVEL_SET, v_stat, buf);
	}
}

char *
next_word( char *str )
{
	while (*str && ! ISSPACE(*str)) ++str;
	while (*str &&   ISSPACE(*str)) ++str;
	return str;
}

/*
 * FREE(ptr)   --frees without actually freeing, so the data is still good
 *               immediately after the free.
 */


void
Free( void *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
 */

void
add_history( char *str )
{
	HIST *hist;
	char *get;

	for( get=str; *get; get++ )
		if( (*get&127)<' ')
			*get=' ';

	if (H_head != NULL && !strcmp(H_head->line, str))
		return;
	while (H_len > S_histlen)
		del_history();
	hist = (HIST *)salloc (sizeof(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 = salloc (strlen(str) + 1);
	strcpy (hist->line, str);
	++H_len;
	H_num= H_tail_base+ H_len;
}

static void
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( char *ptr )
{
	int   hnum=find_history( ptr+1, 1 );
	HIST *hist;

	for( hist=H_head; hist && hnum--; hist=hist->next) ;
	if( !hist ) {
		fprintf(stderr,"History failed\n");
		return "";
	}
	fprintf(stderr, "%s\n", hist->line );
	return hist->line;
}


int
find_history( char *ptr, int min )
{
	HIST *hist;
	int  len, num;

	if     ( *ptr=='!' )
		return 1;
	else if( *ptr=='-' )
		return atoi(ptr+1);
	else if (*ptr >= '0' && *ptr <= '9')
		return H_len-(atoi(ptr)-H_tail_base)-1;

	len = strlen(ptr);
	for (hist=H_head,num=0; hist; hist=hist->next, num++)
		if ( !strnicmp(hist->line, ptr, len) && num>=min)
			return num;
	return -1;
}

void
replace_head( char *str )
{
	if (str && strlen(str) && H_head) {
		free (H_head->line);
		H_head->line = salloc (strlen(str)+1);
		strcpy (H_head->line, str);
	}
}


void
pError(char *str )
{
	int ierr = (long)IoErr();
	ierror(str, ierr);
}

ierror( char *str, int err )
{
	PERROR *per = Perror;
	char msgbuf[256];

	setioerror(err);

	if (err) {
		if(err<500 && Fault(err,str,msgbuf,255)) {
			fprintf(stderr,"%s\n",msgbuf);
			return err;
		}
		else {
			for (; per->errstr; ++per) {
				if (per->errnum == err) {
					fprintf (stderr, "%s%s%s\n",
					per->errstr,
					(str) ? ": " : "",
					(str) ? str : "");
					return err;
				}
			}
		}
		fprintf (stderr, "Unknown DOS error %d: %s\n", err, (str) ? str : "");
	}
	return err;
}

void
setioerror( int err )
{
	static int LastIoError=-1;
	char buf[20];

	IoError=err;
	if( IoError<0 ) IoError=0;
	if( LastIoError!=IoError) {
		LastIoError=IoError;
		sprintf(buf, "%d", IoError);
		set_var(LEVEL_SET, v_ioerr, buf);
	}
}

char *
ioerror(int num)
{
	PERROR *per = Perror;

	for ( ; per->errstr; ++per)
		if (per->errnum == num)
			return per->errstr;
	return NULL;
}

/*
 * Disk directory routines
 *
 * dptr = dopen(name, stat)
 *    DPTR *dptr;
 *    char *name;
 *    int *stat;
 *
 * dnext(dptr, name, stat)
 *    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 != 0 if the file is a directory.
 *
 * dclose() closes a directory channel.
 *
 */

DPTR *
dopen( char *name, int *stat)
{
	DPTR *dp;

	IoError=0;
	*stat = 0;
	dp = (DPTR *)salloc(sizeof(DPTR));
	if (*name == '\0')
		dp->lock = DupLock(Myprocess->pr_CurrentDir);
	else
		dp->lock = Lock (name,ACCESS_READ);
	if (dp->lock == NULL) {
		IoError=IoErr();
		free (dp);
		return NULL;
	}
	dp->fib = (FIB *)SAllocMem((long)sizeof(FIB), MEMF_PUBLIC);
	if (!Examine (dp->lock, dp->fib)) {
		pError (name);
		dclose (dp);
		return NULL;
	}
	if (dp->fib->fib_DirEntryType >= 0) *stat = 1;

#ifdef MA_AMK
	{
	char *c;
	if (c=strchr(name,':')) {
		if (dp->dname = strdup(name))
			dp->dvp = GetDeviceProc(dp->dname,NULL);
		else
			dp->dvp = NULL;
	}
	else {
		dp->dname = NULL;
		dp->dvp   = NULL;
	}
	}
#endif

	return dp;
}

static int
dnext( DPTR *dp, char **pname, int *stat)
{
	if (dp == NULL) return (0);

	if (ExNext (dp->lock, dp->fib)) {
		*stat = 0;
		if( dp->fib->fib_DirEntryType >= 0)
			*stat= dp->fib->fib_DirEntryType!=ST_USERDIR ? 2 : 1;
		*pname = dp->fib->fib_FileName;
		return 1;
	}

#ifdef MA_AMK
	{
	BOOL ma_done = FALSE;
	if (IoErr()==ERROR_NO_MORE_ENTRIES) {
		while (!ma_done && dp->dvp && (dp->dvp->dvp_Flags&DVPF_ASSIGN)) {
			dp->dvp = GetDeviceProc(dp->dname,dp->dvp);
			if (dp->dvp && dp->dvp->dvp_Lock) {
				char buf[256];
				BPTR new_lock,old_lock;
				char *add = strchr(dp->dname,':')+1;
				if (NameFromLock(dp->dvp->dvp_Lock,buf,255)) {
					AddPart(buf,add,255);
					printf("«%s»\n",buf);

					if (new_lock=Lock(buf,ACCESS_READ)) {
						if (Examine(new_lock,dp->fib)) {
							if (dp->lock) UnLock(dp->lock);
							dp->lock = new_lock;
							if (dp->fib->fib_DirEntryType >= 0) *stat = 1;
							*pname = dp->fib->fib_FileName;
							return 1;
							ma_done = TRUE;
						}
					}
				}
				else {
					fprintf(stderr,"csh.dnext: NameFromLock() failed\n");
				}
			}
		}
	}
	}
#endif

	return 0;
}

int
dclose( DPTR *dp )
{
	if (dp == NULL)
		return 1;
	if (dp->fib)
		FreeMem (dp->fib,(long)sizeof(*dp->fib));
	if (dp->lock)
		UnLock (dp->lock);
#ifdef MA_AMK
	if (dp->dvp)
		FreeDeviceProc(dp->dvp);
#endif
	free (dp);
	return 1;
}


int
isdir( char *file )
{
	DPTR *dp;
	int stat;

	stat = 0;
	if (dp = dopen (file, &stat))
		dclose(dp);
	return (stat!=0);
}


void
free_expand( char **av )
{
	char **get = av;

	if (av) {
		while (*get)
		free (*get++-sizeof(FILEINFO));
		free (av);
	}
}

/*
 * EXPAND(base,pac)
 *    base           - char * (example: "df0:*.c")
 *    pac            - int  *  will be set to # of arguments.
 *
 * 22-May-87 SJD.  Heavily modified to allow recursive wild carding and
 *                 simple directory/file lookups. Returns a pointer to
 *                 an array of pointers that contains the full file spec
 *                 eg. 'df0:c/sear*' would result in : 'df0:C/Search'
 *
 *                 Now no longer necessary to Examine the files a second time
 *                 in do_dir since expand will return the full file info
 *                 appended to the file name. Set by formatfile().
 *
 *                 Caller must call free_expand when done with the array.
 *
 * base             bname =       ename =
 * ------           -------       -------
 *  "*"               ""            "*"
 *  "!*.info"         ""            "*.info" (wild_exclude set)
 *  "su*d/*"          ""            "*"      (tail set)
 *  "file.*"          ""            "file.*"
 *  "df0:c/*"         "df0:c"       "*"
 *  ""                ""            "*"
 *  "df0:.../*"       "df0:"        "*"      (recur set)
 *  "df0:sub/.../*"   "df0:sub"     "*"      (recur set)
 *
 * ---the above base would be provided by execom.c or do_dir().
 * ---the below base would only be called from do_dir().
 *
 *  "file.c"          "file.c"      ""       if (dp == 0) fail else get file.c
 *  "df0:"            "df0:"        "*"
 *  "file/file"       "file/file"   ""       if (dp == 0) fail
 *  "df0:.../"        "df0:"        "*"      (recur set)
 *
 */

char **
expand( char *base, int *pac )
{
	char *ptr;
	char **eav = (char **)salloc(sizeof(char *) * (2));
	short eleft, eac;
	char *name;
	char *bname, *ename, *tail;
	int  stat, recur, scr, bl;
	DPTR *dp;
	PATTERN *pat;

	IoError = *pac = recur = eleft = eac = 0;

	base = strcpy(salloc(strlen(base)+1), base);
#if 1
	for (ptr = base; *ptr && *ptr != '?' && *ptr != '*'; ++ptr);
#else
	/* AMK: experimental DOS pattern detection */
	/*printf("trying to expand... %s\n",base);*/
	for (ptr=base; *ptr && *ptr != '?' && *ptr!='*' && *ptr!='!' &&
	               *ptr != '(' && *ptr!=')' &&
	               *ptr != '|' && *ptr!='%' &&
	               *ptr != '~' && *ptr!='#' &&
	               *ptr != '[' && *ptr!=']';
	     ++ptr);
#endif

	if (!*ptr)   /* no wild cards */
		--ptr;
	else
		for (; ptr >= base && !(*ptr == '/' || *ptr == ':'); --ptr);

	if (ptr < base) {
		bname = strcpy (salloc(1), "");
	} else {
		scr = ptr[1];
		ptr[1] = '\0';
		if (!strcmp(ptr-3,".../")) {
			recur = 1;
			*(ptr-3) = '\0';
		}
		bname = strcpy (salloc(strlen(base)+2), base);
		ptr[1] = scr;
	}
	bl = strlen(bname);
	ename = ++ptr;
	for (; *ptr && *ptr != '/'; ++ptr);
	scr = *ptr;
	*ptr = '\0';
	if (scr) ++ptr;
	tail = ptr;

	if ((dp = dopen (bname, &stat)) == NULL || (stat == 0 && *ename)) {
		free (bname);
		free (base);
		free (eav);
		return NULL;
	}

	if (!stat) {                /* eg. 'dir file' */
		char *p,*s;
		for(s = p = bname; *p; ++p) if (*p == '/' || *p == ':') s = p;
		if (*s=='/' || *s==':') s++;
		*s=0;
		eav[eac++] = svfile(bname,dp->fib->fib_FileName,dp->fib);
		goto done;
	}
	if (!*ename) ename = "*";    /* eg. dir df0: */
	if (*bname && bname[bl-1] != ':' && bname[bl-1] != '/') { /* dir df0:c */
		bname[bl] = '/';
		bname[++bl] = '\0';
	}

	pat= compare_preparse( ename, 0 );
	while ((dnext (dp, &name, &stat)) && !breakcheck()) {
		int match = compare_ok( pat, name);
		if (match && (recur || !*tail)) {
			if (eleft < 2) {
				char **scrav = (char **)salloc(sizeof(char *) * (eac + 10));
				memmove (scrav, eav, (eac + 1) << 2);
				free (eav);
				eav = scrav;
				eleft = 10;
			}
			eav[eac++] = svfile(bname,name,dp->fib);
			--eleft;
		}
		if ((*tail && match) || recur) {
			int alt_ac;
			char *search, **alt_av, **scrav;
			BPTR lock;

			if (stat!=1)           /* expect more dirs, but this not a dir */
				continue;
			lock = CurrentDir (dp->lock);
			search = salloc(strlen(ename)+strlen(name)+strlen(tail)+6);
			strcpy (search, name);
			strcat (search, "/");
			if (recur) {
				strcat(search, ".../");
				strcat(search, ename);
			}
			strcat (search, tail);
			scrav = alt_av = expand (search, &alt_ac);
			free(search);
			CurrentDir (lock);
			if (scrav) {
				while (*scrav) {
					int l;
					if (eleft < 2) {
						char **scrav = (char **)salloc(sizeof(char *)*(eac+10));
						memmove ( scrav, eav, (eac + 1) << 2);
						free (eav);
						eav = scrav;
						eleft = 10;
					}

					l = strlen(*scrav);
					eav[eac] = salloc(bl+l+1+sizeof(FILEINFO));
					memcpy( eav[eac], *scrav-sizeof(FILEINFO),sizeof(FILEINFO));
					eav[eac]+=sizeof(FILEINFO);
					strcpy( eav[eac], bname);
					strcat( eav[eac], *scrav);

					free (*scrav-sizeof(FILEINFO));
					++scrav;
					--eleft, ++eac;
				}
				free (alt_av);
			}
		}
	}
	compare_free( pat );
done:
	dclose (dp);
	*pac = eac;
	eav[eac] = NULL;
	free (bname);
	free (base);

	if (eac) {
		int i, j, len;

		QuickSort( eav, eac );
		for( i=0; i<eac-1; i++ ) {
			len=strlen(eav[i]);

			for( j=i+1; j<eac && !strnicmp(eav[i],eav[j],len); j++ )
				if( !stricmp(eav[j]+len,".info")) {
					((FILEINFO*)eav[i]-1)->flags |= INFO_INFO;
					break;
				}
		}
		return eav;
	}

	free (eav);
	return NULL;
}

char *
strupr( char *s )
{
	char *old=s;
	while (*s) *s=toupper(*s), s++;
	return old;
}

char *
strlwr( char *s )
{
	char *old=s;
	while (*s) *s=tolower(*s), s++;
	return old;
}

/*
 * Compare a wild card name with a normal name
 */

extern void *PatternBase;

PATTERN *
compare_preparse( char *wild, int casedep )
{
/*	struct pattern *pat=salloc(256);*/
	struct pattern *pat;
	char *my_wild = wild;

	pat = salloc(sizeof(struct pattern) + strlen(wild)*2 + 3);
	/* this is not exact ('lil bit too much) but better readable */

	pat->casedep=casedep;
	if (pat->queryflag=(*wild=='&')) wild++;

	my_wild = salloc(strlen(wild)+3);
	strcpy(my_wild,wild);
	if (*my_wild=='!') *my_wild='~';
	if (!casedep) strlwr(my_wild);
	if (*my_wild=='~') {
		strins(my_wild+1,"(");
		strcat(my_wild,")");
	}

	DOSBase->dl_Root->rn_Flags |= RNF_WILDSTAR;
	if (PatternBase) {
		if ((pat->patptr=AllocPattern( my_wild, 0 ))<0) {
			free(pat);
			free(my_wild);
			return NULL;
		}
	}
	else {
		if ((ParsePattern(my_wild,pat->pattern,strlen(wild)*2+2)<0)) {
			free(pat);
			free(my_wild);
			return NULL;
		}
	}

	free(my_wild);
	return pat;
}

int
compare_ok( PATTERN *pat, char *name )
{
	char *lowname=name;

	if (!pat)
		return 0;

	if (!pat->casedep) {
		if (lowname=strdup(name))
			strlwr(lowname);
		else
			return 0;
	}

	DOSBase->dl_Root->rn_Flags |= RNF_WILDSTAR;
	if (PatternBase) {
		if (MatchThePattern(pat->patptr,lowname)!=1) {
			if (lowname!=name) free(lowname);
			return 0;
		}
	} else {
		if (!MatchPattern(pat->pattern,lowname)) {
			if (lowname!=name) free(lowname);
			return 0;
		}
	}

	if (lowname!=name)
		free(lowname);

	if (pat->queryflag) {
		char buf[260];
		printf("Select %s%-16s%s [y/n] ? ",o_hilite,name,o_lolite);
		gets(buf);
		return (toupper(*buf)=='Y');
	}

	return 1;
}

void
compare_free( PATTERN *pat )
{
	if( !pat )
		return;
	if( PatternBase )
		FreePattern(pat->patptr);
	free( pat );
}

#if 0
int
compare_strings( char *pattern, char *str, int casedep )
{
	PATTERN *pat=compare_preparse( pattern, casedep );
	int ret=compare_ok(pat,str);
	compare_free(pat);
	return ret;
}
#endif

static char *
svfile( char *s1, char *s2, FIB *fib)
{
	FILEINFO *info=salloc(sizeof(FILEINFO)+strlen(s1)+strlen(s2)+1);
	char     *p = (char *)(info+1);

	strcpy(p, s1);
	strcat(p, s2);
	info->flags = fib->fib_Protection | (*fib->fib_Comment ? INFO_COMMENT : 0);
	info->type  = fib->fib_DirEntryType;
	info->size  = (fib->fib_DirEntryType<0 || fib->fib_DirEntryType==ST_SOFTLINK) ? fib->fib_Size      : -1;
	info->blocks= (fib->fib_DirEntryType<0 || fib->fib_DirEntryType==ST_SOFTLINK) ? fib->fib_NumBlocks :  0;
	info->date  = fib->fib_Date;
	info->uid   = fib->fib_OwnerUID;
	info->gid   = fib->fib_OwnerGID;
	info->class[0]=1;
	return p;
}

static char   *FullPath;
static FIB    *PrevFile;
static int   (*Action)(long, char *, char *);
static long    Mask;
static int     Queued=0;

static int
passinfo( FIB *fib )
{
	char   *p = (char *)(PrevFile+1);
	int    oldlen, ret=0;

	if( Queued ) {
		oldlen=strlen(FullPath);
		/* AMK: don't know the exact length... used dummy 256L */
		AddPart( FullPath, p, 256L );
		ret=(*Action)( Mask, p, FullPath );
		FullPath[oldlen]=0;
		Queued=0;
	}

	if( fib ) {
		*PrevFile= *fib;
		strcpy( p, fib->fib_FileName);
		Queued=1;
	}

	return ret;
}


static int
nu_recurse( char *name )
{
	BPTR lock, cwd;
	FIB  *fib  =(FIB *)SAllocMem(sizeof(FIB),MEMF_PUBLIC);
	int  oldlen=strlen( FullPath ), ret=0;

	/* AMK: don't know the length... used dummy 256L */
	AddPart( FullPath, name, 256L );

	if (lock=Lock(name,ACCESS_READ)) {
		if( Mask & SCAN_DIRENTRY )
			(*Action)( SCAN_DIRENTRY, name, FullPath );

		cwd =CurrentDir(lock);
		if (Examine(lock, fib)) {
			while (ExNext(lock, fib) && !breakcheck()) {
				if (fib->fib_DirEntryType==ST_USERDIR) {
					if ( Mask & SCAN_RECURSE ) {
						if(!ret && !(ret=passinfo( NULL )))
							ret=nu_recurse(fib->fib_FileName);
					}
					if ( Mask & SCAN_DIR     )
						if( !ret )
							ret=passinfo( fib );
				} else if( fib->fib_DirEntryType<0 && Mask & SCAN_FILE )
					if( !ret )
						ret=passinfo( fib );
			}
			if( breakcheck() )
				ret=5;

			if( !ret )
				passinfo( NULL );

		}
		UnLock(CurrentDir(cwd));

		if( Mask & SCAN_DIREND )
			(*Action)( SCAN_DIREND, name, FullPath );
	} else
		pError(name);

	FullPath[oldlen]=0;
	FreeMem(fib, sizeof(FIB));
	return ret;
}

int
newrecurse(int mask, char *name, int (*action)FUNCARG(long,char *,char *))
{
	int ret;

	FullPath = salloc( 512 );
	PrevFile = salloc( sizeof(FIB)+108+1 );
	Action   = action;
	Mask     = mask;
	Queued   = 0;
	*FullPath= 0;

	ret=nu_recurse( name );

	free( PrevFile );
	free( FullPath );

	return ret;
}


/* Sort routines */

static int reverse, factor;

int
cmp( FILEINFO *s1, FILEINFO *s2)
{
	return stricmp( (char *)(s1+1), (char *)(s2+1) );
}

int
cmp_case( FILEINFO *s1, FILEINFO *s2)
{
	return strcmp( (char *)(s1+1), (char *)(s2+1) );
}

int
sizecmp( FILEINFO *s1, FILEINFO *s2)
{
	return s2->size - s1->size;
}

int
datecmp_csh( FILEINFO *s1, FILEINFO *s2 )
{
	int r;
	struct DateStamp *d1=&s1->date, *d2=&s2->date;
	if( !(r= d2->ds_Days - d1->ds_Days))
		if( !(r=d2->ds_Minute - d1->ds_Minute ) )
			r=d2->ds_Tick - d1->ds_Tick;
	return r;
}


int
numcmp( FILEINFO *s1, FILEINFO *s2 )
{
	return atoi((char *)(s1+1))-atoi((char *)(s2+1));
}

static void
enterclass( FILEINFO *info )
{
	char *class, *iclass=info->class, *t;

	if( *iclass==1 ) {
		if( class=getclass( (char *)(info+1))) {
			strncpy( iclass, class, 11 );
			iclass[11]=0;
			if( t=index(iclass,0xA0))
				*t=0;
		} else 
			iclass[0]=0;
	}
}

int
classcmp( FILEINFO *info1, FILEINFO *info2 )
{
	int r;

	enterclass( info1 );
	enterclass( info2 );

	r= stricmp( info1->class, info2->class );
	if( !r ) r=stricmp((char *)(info1+1),(char *)(info2+1));
	return r;
}


void
QuickSort( char *av[], int n)
{
	reverse=factor=0;
	DirQuickSort( av, n, cmp, 0, 0 );
}

static int (*compare)(FILEINFO *, FILEINFO *);

static int
docompare(char *s1,char *s2)
{
	FILEINFO *i1=(FILEINFO *)s1-1, *i2=(FILEINFO *)s2-1;
	int r=(*compare)( i1,i2 );

	if( reverse ) r =-r;
	if( factor )  r+= factor*((i2->size<0) - (i1->size<0));
	return r;
}

#define QSORT

void
DirQuickSort( char *av[], int n, int (*func)(FILEINFO *,FILEINFO *), int rev, int fac)
{
	reverse=rev; compare=func; factor=fac;

	quicksort( av, n-1 );
}

static void
quicksort( char **av, int n )
{
	char **i, **j, *x, *t;


	if( n>0 ) {
		i=av; j=av+n; x=av[ n>>1 ];
		do {
			while( docompare(*i,x)<0 ) i++;
			while( docompare(x,*j)<0 ) --j;
			if( i<=j )
				{ t=*i; *i=*j; *j=t; i++; j--; }
		} while( i<=j );

		if( j-av < av+n-i ) {
			quicksort( av, j-av  );
			quicksort( i , av+n-i);
		} else {
			quicksort( i , av+n-i);
			quicksort( av, j-av  );
		}
	}
}

int
filesize( char *name )
{
	BPTR lock;
	struct FileInfoBlock *fib;
	int  len=0;

	if( lock = Lock (name,ACCESS_READ)) {
		if( fib=(struct FileInfoBlock *)AllocMem(sizeof(*fib),MEMF_PUBLIC)) {
			if (Examine (lock, fib))
				len=fib->fib_Size;
			FreeMem( fib, sizeof(*fib));
		}
		UnLock(lock);
	}
	return len;
}


#ifndef MIN
#define MIN(x,y) ((x)<(y)?(x):(y))
#endif

char **
and( char **av1, int ac1, char **av2, int ac2, int *ac, int base )
{
	char **av=(char **)salloc(MIN(ac1,ac2)*sizeof(char *) ), *str;
	int i, j, k=0;

	for( i=0; i<ac1; i++ )
		for( j=0, str=base ? (char*)FilePart(av1[i]) : av1[i]; j<ac2; j++ )
			if( !stricmp(str, base ? (char*)FilePart(av2[j]) : av2[j]))
				av[k++]=av1[i];
	*ac=k;
	return av;
}

char **
without( char **av1, int ac1, char **av2, int ac2, int *ac, int base )
{
	char **av=(char **)salloc(ac1*sizeof(char *) ), *str;
	int i, j, k=0;

	for( i=0; i<ac1; i++ ) {
		for( j=0, str=base ? (char*)FilePart(av1[i]) : av1[i]; j<ac2; j++ )
			if( !stricmp(str, base ? (char*)FilePart(av2[j]) : av2[j] ) )
				break;
		if( j==ac2 )
			av[k++]=av1[i];
	}
	*ac=k;
	return av;
}

char **
or( char **av1, int ac1, char **av2, int ac2, int *ac, int base )
{
	char **av=(char **)salloc((ac1+ac2)*sizeof(char *) ), *str;
	int i, j, k=0;

	for( i=0; i<ac1; i++ )
		av[k++]=av1[i];

	for( i=0; i<ac2; i++ ) {
		for( j=0, str=base ? (char*)FilePart(av2[i]) : av2[i]; j<ac1; j++ )
			if( !stricmp(str, base ? (char*)FilePart(av1[j]) : av1[j] ) )
				break;
		if( j==ac1 )
			av[k++]=av2[i];
	}

	*ac=k;
	return av;
}

void
clear_archive_bit( char *name )
{
	DPTR *dp;
	int  stat;

	if(dp = dopen(name,&stat) ) {
		SetProtection( name, dp->fib->fib_Protection&~FIBF_ARCHIVE);
		dclose( dp );
	}
}

char *
itoa( int i )
{
	static char buf[20];
	char *pos=buf+19;
	int count=4, flag=0;

	if( i<0 )
		flag=1, i=-i;

	do {
		if( !--count )
			count=3, *--pos=',';
		*--pos= i%10+'0';
	} while( i/=10 );

	if( flag )
		*--pos='-';

	return pos;
}

char *
itok( int i )
{
#if 0
	static char buf[16], which;
	char *exp=" KMG", *ptr= buf+(which=8-which);
#endif
	/* now three buffers instead of two as needed for do_info() */
	static char buf[24], which;
	char *exp=" KMG", *ptr= buf+(8*(which=(which+1)%3));
	int res,m=1024;

/* AMK: don't cripple small numbers */
	if (i<1000) {
		sprintf(ptr,"%d",i);
		return ptr;
	}

/* AMK: allow four places for kilobytes */
	if ((res=(i+512)/1024)<10000) {
		sprintf(ptr,"%dK",res);
		return ptr;
	}
/* AMK: because of HD floppies with 1760K (not 2MB :-) */

	do {
		res=(i+512)/m;
		m*=1024;
		++exp;
	}
	while (res>999);

	if (res==0)
		res=1;

	sprintf(ptr,"%d%c",res,*exp);

	return ptr;
}

char *
next_a0( char *str )
{
	while( *str && (UBYTE)*str!=0xA0 && *str!='=' && *str!=',') str++;
	if( *str )
		return str+1;
	return NULL;
}

static int
gethex( char *str, int l )
{
	int i, val=0, n, c;

	if( *str=='.' ) return l==2 ? 256 : 0;

	for( i=0; i<l || !l; i++ ) {
		c=*str++;
		if     ( c>='0' && c<='9' ) n=c-'0';
		else if( c>='a' && c<='f' ) n=c-'a'+10;
		else if( c>='A' && c<='F' ) n=c-'A'+10;
		else break;;
		val=16*val+n;
	}
	return (l && i!=l) ? -1 : val;
}

strwrdcmp( char *str, char *wrd )
{
	PATTERN *pat;
	int ret;
	char *ind=index(wrd,0xA0);

	if( ind ) *ind=0;
	pat=compare_preparse( wrd,0 );
	ret=compare_ok(pat,str);
	compare_free( pat );
	if( ind ) *ind=0xA0;
	return !ret;
}

int
wrdlen( char *str )
{
	char *old=str;

	while( *str && (UBYTE)*str!=0xA0 ) str++;
	return str-old;
}

char *
getclass(char *file)
{
	CLASS *cl;
	char *class, *str, *arg, *get, *buf;
	int offs, byte, len, fail;
	BPTR fh;

	if( isdir(file) ) return "dir";

	if( !(buf=calloc(1024,1))) return NULL;
	if( !(fh=Open(file,MODE_OLDFILE))) return NULL;
	len=Read( fh,buf,1023);
	Close(fh);

	for( cl=CRoot; cl; cl=cl->next ) {
		class=cl->name;
		if(!(str=next_a0(cl->name))) continue;
		while( str ) {
			if(!(arg=next_a0( str ))) goto nextclass;
			switch( *str ) {
			case 's':
				if( (offs=strlen(file)-wrdlen(arg))<0 ) break;
				if( !strwrdcmp(file+offs,arg)) goto found;
				break;
			case 'n':
				if( !strwrdcmp(FilePart(file),arg) ) goto found;
				break;
			case 'd':
				goto found;
			case 'o':
				offs=gethex(arg,0);
				if( !(arg=index(arg,','))) goto nextclass;
				if( offs>len-10 ) break;
				for( get=buf+offs, ++arg; (byte=gethex(arg,2))>=0; arg+=2 )
					if( (UBYTE)*get++!=byte && byte!=256 )
						goto nexttry;
				goto found;
			case 'c':
				if( !len )
					break;
				for( get=buf, fail=0; get<buf+len; get++ )
					if( *get<9 || *get>13 && *get<32 || *get>127  )
						fail++;
				if( fail*8>len )
					break;
				goto found;
			case 'i':
				break;
			case 'a':
				goto nextclass;
			default:
				goto nextclass;
			}
nexttry:	str=next_a0(arg);
		}
nextclass: ;
	}

	free(buf);
	return NULL;

found:
	free(buf);
	return (char *)class;
}

char *
superclass( char *subclass )
{
	CLASS *cl;
	char *cur;
	int len;

	for( len=0; subclass[len] && (UBYTE)subclass[len]!=0xA0; len++ ) ;
	for( cl=CRoot; cl; cl=cl->next ) {
		if( strncmp( cur=cl->name,subclass,len ))
			continue;
		do
			cur=index( cur,0xA0 );
		while( cur && *++cur!='i');

		if( cur && (cur=index(cur,'=')))
			return ++cur;
	}
	return NULL;
}

char *
getaction( char *class, char *action )
{
	CLASS *cl;
	char *cur, *ind;
	int len;

	for( len=0; class[len] && (UBYTE)class[len]!=0xA0; len++ ) ;
	for( cl=CRoot; cl; cl=cl->next ) {
		if( strncmp( cur=cl->name,class,len ) || !issep(cur[len]))
			continue;
		do
			cur=index( cur,0xA0 );
		while( cur && *++cur!='a');

		if( cur && (cur=index( ++cur,0xA0 ))) {
			do {
				if( !(ind=index( ++cur,'=' )))
					return NULL;
				len=ind-cur;
				if( len==strlen(action) && !strncmp(action,cur,len))
					return ++ind;
			} while( cur=index(cur,0xA0) );
		}
	}
	return NULL;
}

int
doaction( char *file, char *action, char *args )
{
	char *class, *com, *c, *copy;
	int ret, spc=hasspace(file), i=0;

	if( !(class=getclass(file)))
		return exists(file) ? 10 : 9;

	do
		if( com=getaction(class,action))
			break;
	while( (class=superclass( class )) && ++i<4 );
	if( !class )
		return 11;
	if( c=index(com,0xA0) )
		*c=0;
	copy=salloc( strlen(com)+strlen(file)+strlen(args)+7 );
	sprintf(copy,spc?"%s \"%s\" %s":"%s %s %s", com, file, args);
	ret=execute(copy);
	free(copy);
	if( c )
		*c=0xA0;
	return ret ? 12 : 0;
}

void *
salloc( int len )
{
	void *ret;

	if( !len ) len++;

	if( !(ret=malloc(len))) {
		fprintf(stderr,"Out of memory -- exiting\n");
		main_exit( 20 );
	}
	return ret;
}

void *
SAllocMem( long size, long req  )
{
	void *ret;

	if( !(ret=AllocMem(size,req))) {
		fprintf(stderr,"Out of memory -- exiting\n");
		main_exit( 20 );
	}
	return ret;
}

int
issep( char s )
{
	return !s || ISSPACE(s) || s=='/' || s==';' || s=='|' || s=='&';
}


char *
filemap( char *buf, int last )
{
	char *s=buf, *d, *lcd;
	int  len;

	if( last && *s=='~' && issep(s[1])) {
		if (!(lcd=get_var(LEVEL_SET,v_lcd)))
			return buf;
		len= strlen(lcd);
		memmove( s+len, s+1, strlen(s)+1);
		memcpy( s, lcd, strlen(lcd));
		if( s[len]!='/' )
			return buf;
		s+=len+1;
	}
	d=s;
	for( ;; ) {
		if( s[0]=='.' && issep(s[1]) ) {
#if 0
			char AmkBuf[256];
			strcpy(AmkBuf,get_var(LEVEL_SET, v_cwd));
			AddPart(AmkBuf,(s[1]==0)?s+1:s+2,255);
			printf(". = %s\n",AmkBuf);
#endif
			s+=1+(s[1]!=0);
		}
		else if( s[0]=='.' && s[1]=='.' && issep(s[2]))
			*d++='/', s+=2+(s[2]!=0);
		else
			break;
	}
	while( *d++=*s++ ) ;

	return buf;
}


char *
safegets( char *buf, FILE *in )
{
	char *t;

	if( dobreak() )
		return NULL;

	if( !fgets(buf,249,in) )
		return NULL;

	if( t=index(buf,'\n' ))
		*t=0;

	return buf;
}



/* ------------------------ string array handling -------------------------- */

/*
 *  First call:  pass "*array=NULL" and "*len=0" !!
 */

#define AL_BLOCKSIZE 20

BOOL add_array_list(char ***array, long *len, char *str)
{
	char **new_array = *array;
	long i=*len;
	BOOL success=FALSE;

	if (i%AL_BLOCKSIZE == 0) {
		if (new_array = malloc((i+AL_BLOCKSIZE) * sizeof(char *))) {
			memcpy(new_array,*array,i * sizeof(char *));
			free(*array);			/* free old array */
			*array = new_array;		/* set new array */
		}
		else
			return(success);
	}

	if (new_array[i] = strdup(str)) {
		*len = ++i;				/* update length */
		success = TRUE;
	}

	return(success);
}

void free_array_list(char **array, long len)
{
	long i;
	if (len>0) {
		for (i=0; i<len; i++)
			free(array[i]);
		free(array);
	}
}

/* ------------------------ string array handling -------------------------- */



#ifndef ACTION_SET_OWNER
#define ACTION_SET_OWNER 1036
#endif

BOOL SetOwner37(UBYTE *name, LONG uidgid)
{
	BOOL Res = FALSE;

	struct DevProc *devproc = NULL;
	BPTR lock;

	while(devproc = GetDeviceProc(name, devproc))
	{
		if (lock = Lock(name, SHARED_LOCK))
		{
			Res = DoPkt(devproc->dvp_Port,
				ACTION_SET_OWNER,
				NULL, lock, MKBADDR("\0"), uidgid, NULL);
			UnLock(lock);
			break;
		}
		else if (IoErr() != ERROR_OBJECT_NOT_FOUND)
			break;
	}
	if (devproc)
		FreeDeviceProc(devproc);
	else
		if (IoErr() == ERROR_NO_MORE_ENTRIES)
			SetIoErr(ERROR_OBJECT_NOT_FOUND);
	return Res;
}


