/* misc.c -  miscellanous routines
 *
 * 06/16/88 MattS	Created
 *
 * 10/15/89 MattS	Added AutoDoc comment blocks
*/

#include <stdio.h>
#include <ctype.h>
#ifdef MSDOS
#include <io.h>
#include <stdlib.h>
#endif
#include <fcntl.h>
#include <malloc.h>
#include <string.h>

#include "misc.h"

#define FALSE 0
#define TRUE 1

/*
** Compare a string with a hi-bit terminates string
*/

#ifdef NOT_NEEDED
/*
 * @doc MISC INTERNAL
 *
 * @func int | hb_strcmp | This function compares a string with a
 *  hi-bit terminated string. (instead of zero terminated)
 *
 * @parm unsigned char * | str | Zero terminated string.
 *
 * @parm unsigned char * | hb_str | High-bit terminated string.
 *
 * @rdesc The return is the same as strcmp.
 *
 */
hb_strcmp(str, hb_str)
	register unsigned char *str, *hb_str;
{
	unsigned char hbc;

	for (;;) {
		hbc = *hb_str & (unsigned char)0x7f;
		if (hbc == *str) {
			if (*++str == 0)
				return (*hb_str & 0x80) ? 0 : -1;
			else if (*hb_str++ & 0x80)
				return 1;
		} else
			return *str - hbc;
	}
}



/*
 * @doc MISC
 *
 * @func int | hex_nyb | This function converts the hex character into
 *  binary.
 *
 * @parm int | chr | The character to convert.
 *
 * @rdesc The return is the binary hex value of <p chr>.
 *
 */
int hex_nyb(chr)
	register int chr;
{
	if (chr >= '0' && chr <= '9')
		return chr - '0';
	else
		if (chr >= 'A' && chr <= 'Z')
			return chr - 'A' + 0xA;
		else
			return chr - 'a' + 0xA;
}

/*
 * @doc INTERNAL MISC
 *
 * @func int | hex_bytes | This function converts a hex string to binary.
 *
 * @parm char * | str | Specifies string to convert.
 *
 * @parm int | nbytes | Specifies length of string.
 *
 * @rdesc Returns binary value of hex string.
 *
 * @xref hex_nyb
 *
 */
int hex_bytes(str, nbytes)
	register char *str;
	register int nbytes;
{
	register int ret = 0;

	while (nbytes--) {
		ret <<= 4;
		ret += hex_nyb(*str++);
	}
	return ret;
}


/*
 * @doc INTERNAL MISC
 *
 * @func long | htoi | This function converts a hex string to a long.
 *
 * @parm char * | str | Specifies string to convert.
 *
 * @xref hex_bytes
 *
 */

/*
 * Interpret an ASCII string as a hex number, convert to long int.
 */
long htoi(str)
	char *str;
{
	register long ret = 0;
	register unsigned char ch;

	while (ch = *str++)
		if (ch >= '0' && ch <= '9')
			ret = (ret << 4) + ch - '0';
		else if (ch >= 'A' && ch <= 'Z')
			ret = (ret << 4) + ch - 'A' + 10;
		else if (ch >= 'a' && ch <= 'z')
			ret = (ret << 4) + ch - 'a' + 10;
		else return ret;
	return ret;
}

/* nindex --
 *
 */

/*
 *
 * @doc INTERNAL MISC
 *
 * @func int | nindex | looks for pattern p in string s starting at position start.
 *
 * @parm char * | p | Specifies pattern.
 *
 * @parm char * | s | Specifies string.
 *
 * @parm int | start | Specifies starting offset in string.
 *
 * @rdesc It returns an index to the position AFTER p if it succeeds; else -1.
 */
int
nindex(p,s,start)
    char p[];
    char s[];
    int start;
{
    register int j = start;
    register int i;
    int match = -1;
    int nLineLen = strlen(s);
    int nPattLen = strlen(p);

    while ( j < nLineLen )
    {
        /*find first letter of pattern
        */
        while (j < nLineLen)
        {
            if (p[0] == s[j++])
            {                       /* found 1st char of pattern */
                match = 1;          /* toggle flag */
                break;              /* go on to next step */
            }
        }

        if (match == 1)
        {
            /* look for rest of pattern
            */
            for (i = 1; (i < nPattLen) && (j < nLineLen) ; i++,j++)
            {
                if (p[i] != s[j])
                {
                    match = -1;     /* pattern doesn't match: toggle flag */
                    break;          /* stop looking */
                }
            }

            if (j >= nLineLen)      /* if we're at end, it doesn't matter */
            {
                match = -1;
                break;              /* break main loop with flag = fail */
            }
            else
            {
                if (match == 1)     /* found the pattern */
                {
                    match = j;      /* match points to char after pattern */
                    break;          /* break main loop with flag = success */
                }
            }
        }
    }

    return(match);
}



/*
 *
 * @doc INTERNAL MISC
 *
 * @func int | parse | Split of the line, point <p fl> array to each record, records are seperated by
 *  <p sep> charactor.
 *
 * @parm char * | cp | Specifies line.
 *
 * @parm char ** | fl | Specifies array of argument pointers.
 *
 * @parm char | sep | Specifies argument seperator character.
 *
 * @rdesc This function returns the number of fields it found.
 */
int
parse(cp, fl, sep)
	register char *cp, **fl, sep;
{
	register int nfields = 1;

	*fl++ = cp;
	while (1) {
		if (*cp == sep) {
			nfields++;
			*fl++ = cp + 1;
			*cp = 0;
		} else if (*cp == '\n' || *cp == '\0') {
			*cp = 0;
			return nfields;
		}
		cp++;
	}
}

/*
 *
 * Progress routines
 *
 *
 */

long pinc, ploc, loc;

/*
 *
 * @doc INTERNAL MISC
 *
 * @func void | init_progress | This function initializes the progress
 *  indicator.
 *
 * @parm long | size | Specifies the total size.
 *
 * @xref show_progress
 *
 */
void
init_progress(size)
	long size;
{
	ploc = pinc = size / 80;
	loc = 0;
}

/*
 *
 * @doc INTERNAL MISC
 *
 * @func void | show_progress | This function displays the progress
 *  according to the initialized value in <f init_progress>.
 *
 * @parm int | amount | Specifies the delta processed since last call
 *
 */
void
show_progress(int amt)
{
   if( (loc + amt) >ploc)
	{
		while( (loc + pinc) <= ploc+ amt) {
			loc+=pinc;
			fputc('.', stdout);
			fflush(stdout);
		}
		ploc += amt;
	}
}

/*
** mem_to_long and long_to_mem are used to convert back and forth between
 * the byte-order independant representation of integers and the
 * the internal C format
*/

/*
 *
 * @doc INTERNAL MISC
 *
 * @func long | mem_to_long | Used to convert back and forth between
 *  the byte-order independant representation of integers and the
 *  the internal C format.
 *
 * @parm unsigned char * |cp| Specifies the memory to convert.
 *
 * @parm short |nbytes | Specifies the number of bytes to convert.
 *
 * @rdesc This function returns the Local version of the value contained
 *  in the memory location.
 *
 * @comm This function was meant to provide byte-order independence capability
 *  in C code and was used in Microsoft Bookshelf and the Microsoft Library
 *  products.
 *
 * @xref long_to_mem
 *
 */
long
mem_to_long(cp, nbytes)
	register unsigned char *cp;
	register short nbytes;
{
	register int i;
	register long ret = 0;

	for (i = nbytes; i > 0;) {
		ret <<= 8;
		ret += cp[--i];
	}
	return ret;
}

/*
 *
 * @doc INTERNAL MISC
 *
 * @func void | long_to_mem | This function is used to convert back and forth between
 *  the byte-order independant representation of integers and the
 *  the internal C format.
 *
 * @parm long | value | Specifies the value to save into the memory.
 *
 * @parm unsigned char * |cp| Specifies the memory buffer.
 *
 * @parm short |nbytes | Specifies the number of bytes to convert.
 *
 * @comm This function was meant to provide byte-order independence capability
 *  in C code and was used in Microsoft Bookshelf and the Microsoft Library
 *  products.
 *
 * @xref mem_to_long
 *
 */
void
long_to_mem(val, cp, nbytes)
	register long val;
	register unsigned char *cp;
	register int nbytes;
{
	register int i;

	for (i = 0; i < nbytes; i++) {
		*cp++ = (unsigned char)val & (unsigned char)0xff;
		val >>= 8;
	}
}

/*
** This function is available under Sun 3.2 UNIX but not on MS-DOS so I
** had to write it in order to use the index building tools under MS-DOS
**
** getopt(argc, argv, template)
**	argc, argv are the arguments to main
**	template is a string of legal arguemnt characters.
**	if a character in the template is followed by a ':', this means
**	that the option takes an additional argument which will either be the
**	remainder of the argument string if this argument strings ended,
**	the next argument.
**
** RETURNS:
**		'?'	on error, print usage and exit
**		EOF	when end of options is hit, use "optind" to get non option args
**		chr means that option "chr" was specified and if it takes an
**			additional argument, that would be in the string "optarg"
**
** Here is an example program which just echos back how it parsed the command
** line, using getopt.  This template says the program has flags 'a' and 'b'
** and options 'c' and 'd'.  Legal commmand lines would be:
**		D>prog hello there folks
**		D>prog -a hello folks
**		D>prog -ab -chello -d there folks
**		D>prog -abc hello -dthere folks
**
**	extern char *optarg;
**	extern int optind;
**	char *my_template = "abc:d:";
**	
**	main(argc, argv)
**		char **argv;
**	{
**		int arg;
**	
**		while (arg = getopt(argv, argv, my_template))
**			if (optarg)
**				printf("-%c %s\n", arg, optarg);
**			else if (arg == '?') {
**				printf("Argument error\n");
**				break;
**			} else
**				printf("-%c\n", arg);
**		for (arg = optind; arg < argc - 1; arg++)
**			printf("### %s\n", argv[arg]);
**	}
*/


static int argNum;
static char *optparse;
int optind = 1;
char *optarg;

getopt(argc, argv, template)
	int		argc;
	char	**argv,
			*template;
{
	register char *arg, *tp;

	optarg = NULL;
	if (!optparse)
		if (++argNum < argc) {
			arg = argv[argNum];
			if (*arg != '-') {
				optind = argNum;
				return EOF;
			}
			optparse = arg + 1;
		} else
			return EOF;
	for (tp = template; *tp; tp++)
		if (*tp == *optparse) {
			if (tp[1] == ':') {
				if (optparse[1]) {
					optarg = optparse + 1;
					optparse = 0;
				} else
				if (++argNum == argc)
					return '?';
				else
					optarg = argv[argNum];
			}
			if (optparse && *++optparse == 0)
				optparse = NULL;
			return *tp;
		}
	return '?';
}

#endif

/*

/*
 *
 * @doc INTERNAL MISC
 *
 * @func int | findlshortname | This function returns the offset of the
 *  end of the complete path and file name not counting the file extension.
 *
 * @parm char * | fullname | Specifies the name to search.
 *
 * @rdesc The return value is the offset of the end of the complete path
 *  and file name not counting the file extension.
 *
 */
int
findlshortname(fullname) /* find the length of the short name */
		    /* full path/file name not counting extension */
char *fullname;
{
    char *ch;
    int cnt;

    if(!fullname || !*fullname)
	    return(0);

    cnt=strlen(fullname);
    ch=fullname+cnt;

    while(*ch!='.' && *ch!='\\' && *ch!='/' && cnt)
    {
	ch--;
	cnt--;
    }

    if(!cnt)
	return(strlen(fullname));

    return(ch - fullname);
}


/* getblong - get the next number from a buffer.  Returns a long positive
 * value if successful, otherwise returns -1.  Scans-off leading BS.
 * Parameters: takes a pointer to a char and a pointer to an int which
 * will become a pointer the character following the number.
 */
/*
 *
 * @doc INTERNAL MISC
 *
 * @func long | getblong | ATOL for buffer.
 *
 * @parm char * | line | Specifies buffer.
 *
 * @parm int * | i | Specifies current offset into buffer.  This value is
 *  updated as the buffer is scanned.
 *
 * @rdesc This function returns the Long value of the ASCII value contained
 *  in the buffer at the specified offset.
 *
 * The offset is updated to the next character after the last ASCII character.
 *
 */
long
getblong(line, i)
char *line;
int *i;
{
    int pos = *i;
    long result = 0;
    int nLineLen = strlen(line);

/*     while (!isdigit(line[pos]) && pos < nLineLen) */
/*	   ++pos; */

    if (pos == nLineLen)
        return ((long)-1);

    while (isdigit(line[pos]) && pos < nLineLen)
        result = 10 * result + line[pos++] - '0';

    *i = pos;

    return (result);
}

#ifdef NOT_NEEDED
/*
 *
 * @doc INTERNAL MISC
 *
 * @func char * | parse_sec_name | This function parses the section
 *  name from a complete name.
 *
 *  It was written for use in for Compound File indexing tools for
 *  Microsoft Library.
 *
 * @parm char ** | ppch | Specifies the name to parse.
 *
 * @rdesc Ack.
 *
 */
char *
parse_sec_name(char **ppch)
{
    char *pch;
    char *p2ch;
    int j;

    if(!ppch || !*ppch)
	return(NULL);

    pch=*ppch;

    while(*pch && *pch!='!')
	pch++;


    if(!*pch)		    /* default section name is file name */
	return(*ppch);

    *pch='\0';

    pch++;

    p2ch=*ppch;
    *ppch=pch;

    return(p2ch);
}

#endif


/*							*
 *							*
 * Memory Management routines				*
 *							*
 *							*
 *							*/

char _achmemout[]= "Oh my, we seem to be out of %smemory. %ld Allocated\n" ;

/* Generic memory management */

/*
 *
 * @doc INTERNAL MISC
 *
 * @func char * | cp_alloc | Allocates memory for the string and copies
 *  it into the buffer and returns it.
 *
 * @parm char * | pch| Specifies string to copy.
 *
 * @comm The buffer should be freed with <f my_free>.
 *
 */
char *
cp_alloc(pch)
char *pch;
{
    char *pch2;

    if(!pch)
	return(NULL);

    pch2=my_malloc(strlen(pch)+1);
    strcpy(pch2,pch);
    return pch2;
}

/*
 *
 * @doc INTERNAL MISC
 *
 * @func void | memfil | Fills the memory with zero.
 *
 * @parm int * | mem | Specifies the memory block to fill.
 *
 * @parm unsigned int | size | Specifies the size of the memory block.
 *
 * @comm The size of the memory block does not have to be a multiple of 2.
 *
 */
void
memfil(mem,size)
register int *mem;
register unsigned int size;
{
    char flag=FALSE;
    char *pch;

    if(size&1) /* it's odd so int fill won't work all the way */
    {
	flag=TRUE;
	size--;
    }

    size=size/2;
    while(size--)
	*mem++=0;

    if(flag)
    {
	pch=(char *)mem;
	*pch='\0';
    }

}

/*
 *
 * @doc INTERNAL MISC
 *
 * @func char * | clear_alloc | This function allocates memory and
 *  initialized it to zeros.
 *
 * @parm unsigned int | size | Specifies the size of the memory to allocate.
 *
 * @comm The allocated memory should be freed with <f my_free>.
 *
 */
char *
clear_alloc(size)
unsigned int size;
{
    char *pmem;

    pmem=my_malloc(size);
    memfil((int *)pmem,size);

    return pmem;
}

static long ivemalloc=0;

/*
 *
 * @doc INTERNAL MISC
 *
 * @func char * | my_malloc | Allocates memory and checks for errors.
 *
 * @parm unsigned int | size | Specifies the size of the memory to allocate.
 *
 * @comm The allocated memory should be freed with <f my_free>.
 *
 */
char *
my_malloc(size)
unsigned int size;
{
    char *pmem;
    int hck;

    if(size>32767)
    {
	fprintf(stderr,"Size >32K\n");
	exit(666);
    }
    hck=_heapchk();
    if(hck == _HEAPBADNODE)
    {
	fprintf(stderr,"Bad node in Heap.  Ack!\n");
	exit(666);
    }
    if(hck == _HEAPBADBEGIN)
    {
	fprintf(stderr,"Bad begin in Heap.  Ack!\n");
	exit(666);
    }
    pmem=(char *)malloc(size);
    if(pmem==NULL)
    {
	fprintf(stderr,_achmemout,"",ivemalloc);
	   exit(777);
    }
    ivemalloc+=size;
    return(pmem);
}


/*
 *
 * @doc INTERNAL MISC
 *
 * @func void | my_free | Frees the specified buffer.
 *
 * @parm void * | buffer | Specifies the buffe to free.
 *
 */
void
my_free(void * buffer)
{
    if(!buffer)
	return;

    ivemalloc-=_msize(buffer);
    free(buffer);
    return;

}


/* used by old indexing tools */

char *cpalloc(str)
	char *str;
{
	return(cp_alloc(str));
}



/*
 *
 * @doc INTERNAL MISC
 *
 * @func void | setmem | Sets the memory to the specified value.
 *
 * @parm char * | mem | Specifies the memory.
 *
 * @parm int | size | Specifies the size of the memory block.
 *
 * @parm char | val | Specifies the value to set the memory to.
 *
 * @comm Filling with zero is much faster with <f mem_fil>.
 *
 */
void
setmem(src, size, val)
	register char *src;
	register int size;
	register char val;
{
	while (size-- > 0)
		*src++ = val;
}

/*
 *
 * @doc INTERNAL MISC
 *
 * @func void | movmem | This function moves the specified memory.
 *  Overlap is not checked.
 *
 * @parm char * | src | Source of copy.
 *
 * @parm char * | dst | Destination of copy.
 *
 * @parm int | len |  Specifies number of bytes to copy.
 *
 */
void
movmem(src,dst,len)
	register char *src, *dst;
	register int len;
{
	while (len-- > 0)
		*dst++ = *src++;
}


#ifdef NOT_NEEDED

/* explicit NEAR memory management calls */

char near *
ncp_alloc(pch)
char near *pch;
{
    char near *pch2;

    if(!pch)
	return(NULL);

    pch2=nmy_malloc(strlen(pch)+1);
    strcpy(pch2,pch);
    return pch2;
}

void
nmemfil(mem,size)
register int near *mem;
register unsigned int size;
{
    char flag=FALSE;
    char near *pch;

    if(size&1) /* it's odd so int fill won't work all the way */
    {
	flag=TRUE;
	size--;
    }

    size=size/2;
    while(size--)
	*mem++=0;

    if(flag)
    {
	pch=(char near *)mem;
	*pch='\0';
    }

}

char near *
nclear_alloc(size)
unsigned int size;
{
    char near *pmem;

    pmem=nmy_malloc(size);
    memfil((int near*)pmem,size);

    return pmem;
}

static long ivenmalloc=0;

char near *
nmy_malloc(size)
unsigned int size;
{
    char near *pmem;

    pmem=(char near *)_nmalloc(size);
    if(pmem==NULL)
    {
	fprintf(stderr,_achmemout,"NEAR ",ivenmalloc);
	 exit(777);
    }
    ivenmalloc+=size;
    return(pmem);
}




/* used by old indexing tools */

char near *
ncpalloc(str)
char near *str;
{
	return(ncp_alloc(str));
}



void
nsetmem(src, size, val)
	register char near *src;
	register int size;
	register char val;
{
	while (size-- > 0)
		*src++ = val;
}

void
nmovmem(src,dst,len)
	register char near *src;
	register char near *dst;
	register int len;
{
	while (len-- > 0)
		*dst++ = *src++;
}






/* explicit FAR memory management calls */

char far *
fcp_alloc(pch)
char far *pch;
{
    char far *pch2;

    if(!pch)
	return(NULL);

    pch2=fmy_malloc(strlen(pch)+1);
    strcpy(pch2,pch);
    return pch2;
}

void
fmemfil(mem,size)
register int far *mem;
register unsigned int size;
{
    char flag=FALSE;
    char far *pch;

    if(size&1) /* it's odd so int fill won't work all the way */
    {
	flag=TRUE;
	size--;
    }

    size=size/2;
    while(size--)
	*mem++=0;

    if(flag)
    {
	pch=(char far *)mem;
	*pch='\0';
    }

}

char far *
fclear_alloc(size)
unsigned int size;
{
    char far *pmem;

    pmem=fmy_malloc(size);
    fmemfil((int far*)pmem,size);

    return pmem;
}

static long ivefmalloc=0;

char far *
fmy_malloc(size)
unsigned int size;
{
    char far *pmem;

    pmem=(char far *)_fmalloc(size);
    if(pmem==NULL)
    {
	fprintf(stderr,_achmemout,"FAR ",ivefmalloc);
	 exit(777);
    }
    ivefmalloc+=size;
    return(pmem);
}




/* used by old indexing tools */

char far *
fcpalloc(str)
char far *str;
{
	return(fcp_alloc(str));
}



void
fsetmem(src, size, val)
	register char far *src;
	register int size;
	register char val;
{
	while (size-- > 0)
		*src++ = val;
}

void
fmovmem(src,dst,len)
	register char far *src;
	register char far *dst;
	register int len;
{
	while (len-- > 0)
		*dst++ = *src++;
}



#endif

/*
 *
 * @doc INTERNAL MISC
 *
 * @func void | mymktemp | Make a temporary file.  The returned file name is
 *  guranteed to be unique and not already exist.
 *
 * @parm char * | lpszpath | Specifies the path to create the file.
 *
 * @parm char * | lpszbuffer | Specifies the buffer to receive the
 *  full path/file name.
 *
 *
 */
void
mymktemp(char * lpszpath, char * lpszbuffer)
{
    static int i=0;
    FILE *fp;

    sprintf(lpszbuffer,"%sdoc%05X.xxx",lpszpath,i++);
    fp=fopen(lpszbuffer,"r");
    if(!fp)
	return;

    fclose(fp);
    while(fp)
    {

	if(i>30000)
	{
 fprintf(stderr,"ERROR: Cannot create temporary files on %s.\n",lpszpath);
	    exit(1);
	}
	sprintf(lpszbuffer,"%sdoc%05X.xxx",lpszpath,i++);
	fp=fopen(lpszbuffer,"r");

	if(fp)
	    fclose(fp);
    }
}