/* **************************************************************
 *								*
 *			  SYSLIB5.C				*
 *	       SYSTEM DIRECTORY UTILITY ROUTINES		*
 *		  last modified 02/10/84 dsb			*
 *								*
 ************************************************************** */

#include "libc.h"
#include "syslib.h"
#include "hardio.h"

/* #define DBUG 1  */

/* ***************** REVISION HISTORY OF THIS MODULE *************

    01/18/84	Begin piling in functions left over when other
		 modules become overlays, i.e. functions which
		 used to be in those modules but which have to
		 be available to more than one overlay.
    01/19/84	Added list functions needed by List and Add 
    01/23/84	Added prtname (print until blank, 8 chars max)
    01/24/84	Changed yorn to return boolean
    01/27/84	Fixed get_name to return 0 for empty input
		 (rather than retrying with no prompt)
    02/10/84	Used register variable whenever possible (speed)

****************************************************************** */

/*page*/
#define MAX_PROD 20
long gethex(), atoh();



map_test(map,bit)	    /* test a bit in a bit map */
char map[];	
int bit;
{
    return(map[bit/8] & (1 << (bit % 8)));
}


wait_user()
{
    char a;

    printf("\nHit RETURN to continue, <ctl-C> to abort: ");
    if(dgets(&a,1) && (a==0x03))
	bye();
    return();
}


waitkey()	/* get a char, throw in bit bucket */
{
    char a;
    printf("\nPress RETURN to continue --> ");
    dgets(&a,1);
}


check_wait()	/* check for pause request, hang on until released */
{		/* pause request comes from kbd only, NOT stdin!! */
    char a;
    if(bdos(11,0))
    {
	bdos(1,0);	      /* get key into bit bucket */
	bdos(1,0);	      /* wait for next one & eat it too */
    }
   return();
}
/*page*/

yorn(msg)
char *msg;
{
    char b;
    register char a;
    register int i;

    do
    {
	printf("%s (Y/N)? --> ",msg);
	i = dgets(&b,1);
	a = toupper(b);
    } while((i != 1) || ((a != 'Y') && (a != 'N')));
    return(a == 'Y');
}


its_correct()
{
    return(yorn("\nIs this correct "));
}

/*page*/
dgets(buf,max)	    /* get a string from stdin, terminated by <cr>  */
char *buf;	    /* or when max chars have been entered.  */
int max;	    /* don't put the <cr> in the string; return */
{		    /* the number of characters in the string. */
    register char a;
    register int i,j;
    char tempbuf[20];

    if(max > 20)
    {
	printf("\n*BUG* call to dgets wants too many chars (%d)",max);
	return(0);
    }

    for(i=0; (i<20) && ((a=getchar()) != '\n'); i++)
	switch(a)
	{
	    default:	tempbuf[i] = a;
			break;
	    case DEL:	if(i)
			    printf("%c",BS);
	    case BS:	if(i)
			{     
			    printf(" %c",BS);
			    --i;
			}
			--i;
			break;
	    case CTRL_X:
			while(i)
			{
			    printf("%c %c",BS,BS);
			    --i;
			}
			--i;
			break;
	} /* switch on input character */

    if(i==20)			     /* if we exited because of full buffer */
	while((a=getchar()) != '\n')	/* eat chars until <cr>  */
	    ;
    for(j = 0; (j<i) && (j<max); j++)	  /* copy into user's buffer */
	*buf++ = tempbuf[j];

    return(i);
} /* dgets */

/*page*/



long gethex(minlen,maxlen)   /* get a valid hex number, <cr> to end */
int minlen,maxlen;
{
    char conbuf[8];
    int  count;

    error = abort_req = FALSE;

    clear(conbuf,8,'\0');
    if(!(count = dgets(conbuf,maxlen)))
    {
	abort_req = TRUE;
	return(0L);
    }
#ifdef DBUG
    printf("\n*DEBUG* conbuf = ");
    print8(conbuf);
#endif
    if((count<minlen) || (count>maxlen))
    {
	error = TRUE;
	return(0L);
    }
    if(*conbuf == ESC)
    {
	abort_req = TRUE;
	return((long)ESC);
    }
    return(atoh(conbuf));   /* atoh will set error if bad digit */
} /* gethex (get string from console, translate into long) */


long atoh(buf)		    /* convert a buffer of up to 8 chars */
char *buf;		    /* from ascii string to long integer */
{
    long l = 0;
    register int i = 0;
    int n;

    error = FALSE;
    while(*buf && (i++<8))
    {
	l *= 16;
	n = nibval(*buf++);
	if(n == -1)
	{
	    error = TRUE;
	    return(0);
	}
	l += n;
    }
    return(l);
} /* atoh */

/*page*/
nibval(nib)
char nib;
{
    nib = toupper(nib);
    if((nib >= '0') && (nib <= '9'))
	return(nib-'0');
    else if ((nib >= 'A') && (nib <= 'F'))
	return(nib-'A'+10);
    else
	return(-1);	    /* error */
}



chrcmp(c1,c2,len)
char c1[],c2[];
int len;
{
    for(; len > 0; len--,c1++,c2++)
	if(*c1 != *c2)
	    return(*c1-*c2);
    return(0);
} /* compare character strings (nulls are nothing special) */


match(a,b,l)		/* tell if two strings are equal */
char *a, *b;
int l;
{
    return(chrcmp(a,b,l) == 0);
}


/*page*/
get_name(name)			/* get a file name (8 chars) */
char name[];
{
    register char *p;
    register int i= -1;
    register int j;

    while ((i < 0) || (i > 8))
    {
	i = dgets(name,8);
	if(i > 8)
	    printf("\nToo long!  Re-enter --> ");
    }

    clear(name+i, 8-i, ' ');		/* pad with blanks */
    for(j=0, p = name; j < 8; j++)	/* upper-case the string */
	if(*p < ' ')
	    error = TRUE;		/* check for control characters */
	else
	    *p++ = toupper(*p);
    return(i);
} /* get_name */


print8(p)		/* print an 8-character name with trailing blanks */
char *p;
{
    register int i;
    for(i=0; i<8; i++)
	printf("%c",*p++);
}

prtname(p)		/* print an 8-character name, no trailing blanks */
char *p;
{
    register int i;
    for(i=0; (i<8) && (*p != ' '); i++)
	printf("%c",*p++);
}


sort(base,len,key,keylen,num)	/* sort table according to an integer field  */
char *base;		 /* pointer to beginning of table */
int len;		 /* length of a table entry */
int key;		 /* offset to key */
int keylen;		 /* 1 for character, 2 for integer */
int num;		 /* number of entries to be sorted */
{
    register int i;
    int  gap, j;
    char temp[96];	    /* big enough for OS table entry */
    register char *cp1, *cp2;
    unsigned x1,x2;

    for (gap = num/2; gap > 0; gap /=2) 	/* K & R's shell sort */
	for(i=gap; i < num; i++)
	    for(j=i-gap; j>=0; j-=gap)
	    {
		cp1 = base + (j*len);
		cp2 = base + ((j+gap) * len);
		x1  = *((unsigned *)(cp1 + key));
		x2  = *((unsigned *)(cp2 + key));
		if(keylen == 1)
		{
		    x1 &= 0xFF;
		    x2 &= 0xFF;
		}
		if(x2 > x1)
		    break;
		blockmv(temp,cp1,len);
		blockmv(cp1,cp2,len);
		blockmv(cp2,temp,len);
	    } /* reverse entries where necessary for one gap size */
	      /* (then outer loop for a given gap size) */
	      /* (outermost of three loops reduces gap size) */
} /* sort (sort table entries on an integer key) */

delay(time)
int time;
{
    for( ; time > 0; time--)
	;
}

/*page*/
chop_name(longer,shorter)	/* remove drive and filetype from filename */
char *longer,*shorter;		/* also make the copy upper case */
{
    register char *start;
    register int  i;

    if((start = index(longer,':')) == NULL)
	start = longer;  
    else
       start++; 			   /* point at first char of name */

    for(i=0; (i<8) && (*start) && (*start != '.'); i++) /* copy to . or end */
	*shorter++ = toupper(*start++);

    for( ; i<8; i++)			    /* fill with blanks */
	*shorter++ = ' ';

} /* chop name (make copy of name only, no drive or extension) */


dump_ent(sd,ost,os)	    /* display summary of directory entry */
struct SDentry *sd;
struct OSTentry *ost;
int os;
{
    register int i,j,k;
    char a;

    printf("\n\nFilename   Load Addr.   Entry Off.   ");
    if(os)
	printf("   Support Files");
    printf("  \n--------   ----------  -----------   ");
    if(os)
	printf("   -------------");
    printf("\n");

    for(i=0; i<8; i++)
	printf("%c",sd->name[i]);
    printf("   %8lx        ",sd->load_addr);
    printf("%4x         ",sd->exec_off);

    if(os)
	for(i=1; (i<8) && ost->load_list[i][0]; i+=3)	/* 3 to a line */
	{
	    for(j=i; (j<i+3) && (j<8) && (ost->load_list[j][0]); j++)
	    {
		for(k=0; k<8; k++)
		    printf("%c",ost->load_list[j][k]);
		printf("    ");
	    }
	    if((j<8) && (ost->load_list[j][0])) /* if there's another line */
		printf("\n\t\t\t\t\t"); 	/* tab over for it */
	}
} /* dump_ent */

/*page*/

kill(sd_ent)			     /* perform physical removal */
struct SDentry *sd_ent;
{
    char tranbuf[512];
    struct SDentry *sdp;
    struct OSTentry *ostp;
    int max_sd;
    int trk,sec;
    int secs_to_move,secs_this_xfer;
    int srce_trk, srce_sec, dest_trk, dest_sec;
#ifdef DBUG
    char *clock = 0x40;
#endif

    max_sd = sd_entries()-1;
    if(((struct SDentry *)sysdir.buffer)+max_sd == sd_ent)  /* last one? */
	return();				/* if so don't move data */

    dest_trk = sd_ent->track;		       /* where to move down to */
    dest_sec = sd_ent->sector;
    srce_trk = (sd_ent+1)->track;	       /* where to start moving from */

    srce_sec = (sd_ent+1)->sector;	

    max_sd=sd_entries()-1;
    sdp = ((struct SDentry *)sysdir.buffer)+max_sd;  /* last entry */
    trk = sdp->track - srce_trk;		    /* length in tracks */
    sec = sdp->sector + sdp->length - srce_sec;     /* plus sectors */
    secs_to_move = SECTS_PER_TRACK*trk + sec;

#ifdef DBUG
    printf("%d sectors to move",secs_to_move);
    printf("\nStarting move at %02d:%02d:%02d'%02d",*(clock+3),*(clock+2),
		*(clock+1),*clock);
#endif

    while(secs_to_move > 0)
    {
	if(secs_to_move >= 4)
	    secs_this_xfer = 4;
	else
	    secs_this_xfer = secs_to_move;

	hd_xfer(READ,srce_trk,srce_sec,tranbuf,secs_this_xfer);
	hd_xfer(WRITE,dest_trk,dest_sec,tranbuf,secs_this_xfer);

	srce_sec += secs_this_xfer;
	dest_sec += secs_this_xfer;
	if(srce_sec > SECTS_PER_TRACK)
	{
	    srce_sec -= SECTS_PER_TRACK;
	    ++srce_trk;
	}
	if(dest_sec > SECTS_PER_TRACK)
	{
	    dest_sec -=SECTS_PER_TRACK;
	    ++dest_trk;
	}
	secs_to_move -= secs_this_xfer;
    } /* while there are sectors to be moved */
#ifdef DBUG
    printf("\nFinished move at %02d:%02d:%02d'%02d",*(clock+3),*(clock+2),
		*(clock+1),*clock);
#endif
    return();
} /* kill_sd (physically remove a file) */

/*page*/
sd_delete(sd_ent)		/* remove entry from System Directory */
struct SDentry *sd_ent;
{
    register int ent_num, max, i;

    ent_num = sd_ent - (struct SDentry *)sysdir.buffer;
    max = sd_entries()-1;
    if((ent_num < 0) || (ent_num > max))
    {
	printf("\n\07**BUG** bad SD entry # %d in sd_delete()",ent_num);
	return();
    }
    for(i=ent_num; i<max ; ++i,++sd_ent)
    {
	blockmv(sd_ent,sd_ent+1,sizeof(struct SDentry));  /* move next down */
	fix_loc(sd_ent);		      /* put right disk address in  */
    }
    clear(((struct SDentry *)sysdir.buffer)+max,sizeof(struct SDentry),0);
    return();
} /* sd_delete (remove entry in System Directory) */

os_delete(os_ent)		/* remove entry from OS Table  */
struct OSTentry *os_ent;
{
    int ent_num,max;

    if(os_ent == NULL)
	return();

    ent_num = os_ent - (struct OSTentry *)ostable.buffer;
    max = ost_entries()-1;
    if((ent_num < 0) || (ent_num > max))
    {
	printf("\n\07**BUG** bad OS entry # %d in os_delete()",ent_num);
	return();
    }
    blockmv(os_ent,os_ent+1,(max-ent_num) * sizeof(struct OSTentry));
    clear(((struct OSTentry *)ostable.buffer)+max,sizeof(struct OSTentry),0);
    return();
} /* os_delete (remove entry in OS Table) */

/*page*/
fix_loc(ent)		/* correct the track & sector address of an entry */
struct SDentry *ent;
{
    int trk,sec;

    trk = 3;		/* defaults: if this is the first entry, these */
    sec = 1;		/* are the right track and sector for it. */

    if(ent != (struct SDentry *)sysdir.buffer)
    {
	--ent;				 /* look at previous entry */
	trk = ent->track;		 /* calculate our starting address */
	sec = ent->sector + ent->length; /* as their starting address plus */
	while(sec > SECTS_PER_TRACK)	 /* length.  Make sure sector lies */
	{				 /* in range. */
	    sec -= SECTS_PER_TRACK;
	    ++trk;
	}
	++ent;				 /* restore pointer to current entry */
    }

    ent->track = trk;
    ent->sector = sec;
} /* fix_loc (adjust SD entry's disk address fields after a move) */

/*page*/
pr_entries()		/* count the entries in the Product Type Table */
{
    register struct PTentry *entry;
    register int i;

    entry = (struct PTentry *) prodtable.buffer;
    for(i=0; (i<40) && (entry->type > 0) && (entry->type < MAX_PRODS); i++)
	entry++;
    return(i);
} /* pr_entries */

/*page*/

sd_header()
{
    printf("\nEntry   Filename   Track Sect Records  Load Addr Exc Off");
    printf("\n-----   --------   ----- ---- -------  --------- -------");
    return();
} /* print header for system directory */


prt_sd(ent)
struct SDentry *ent;
{
    print8(ent->name);
    printf("    %4x  %2x    %4x   %8lx   %4x",ent->track,ent->sector,
	    ent->length,ent->load_addr,ent->exec_off);

} /* print an sd entry */


ost_header()
{
    printf("\nEntry   Filename   OS Type       Support Files Required ");
    printf("\n-----   --------   -------     -------------------------------");
} /* print header for OS Table	*/

/*page*/

prt_os(entry)
struct OSTentry *entry;
{
    register int i,j;

    print8(&(entry->load_list[0][0]));
    printf("   ");
    switch(entry->os)  
    {
	default:    printf("ODD OS 0x%x ",entry->os);
		    break;
	case 0x11:  printf("CP/M 2.2  ");
		    break;	    
	case 0x12:  printf("CP/M 86   ");
		    break;
	case 0x13:  printf("HiDos     ");
		    break;	       
	case 0x21:  printf("MS-DOS 2.0");
		    break;
	case 0x22:  printf("MS-DOS 2.x");
		    break;
	case 0x23:  printf("MS-DOS 3.x");
		    break;
    } /* switch on OS type */
    printf("  ");
    for(i=1; i<5; i++)
	if(entry->load_list[i][0])
	{
	    print8(&(entry->load_list[i][0]));
	    if(i<4)
		printf("    ");
	}

    if(entry->load_list[5][0])
    {
	printf("\n\t\t\t       ");
	for(i=5; i<8; i++)
	    if(entry->load_list[i][0])
	    {
		print8(&(entry->load_list[i][0]));
		printf("    ");
	    }
    }
} /* print an os entry (not much detail) */

	    {
		print8(&(entry->load_list[i][0]));
		printf("    ");
	    }
    }
} /* print an os entry (