/*
    LIB - a librarian for compatible OBJ/LIB files
    Copyright (C) 1995,1996  Steffen Kaiser

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* $RCSfile: TYPES.C $
   $Locker: ska $	$Name: v3_2 $	$State: Rel $

	global definitions

*/

#ifndef _MICROC_
#include <string.h>
#include <stdlib.h>
#include <dos.h>
#else
#include <intr.h>
#endif
#include <portable.h>
#include "types.h"
#include "yerror.h"

#ifndef lint
static char const rcsid[] = 
	"$Id: TYPES.C 3.3 1999/02/17 05:18:08 ska Rel ska $";
#endif

#ifndef chkHeap
#define chkHeap()
#endif

void *getmem(unsigned const size)
/* allocate dynamic memory and emit an error message if failed */
{	void *h;

	chkHeap()
	if((h = U_malloc(size)) == NULL)
		fatal(E_noMem);
	chkHeap()
	return h;
}

#ifndef _MICROC_
#ifndef NDEBUG
#if defined(__TINY__) || defined(__SMALL__) || defined(__COMPACT__)
#define depoi unsigned long)(unsigned
#else
#define depoi unsigned long
#endif

void U_free(void *poi)
/* Debug free() function */
{	DB_ENTER("U_free");
	DB_PRINT("mem", ("free = %08lx", (depoi)(poi)));
	free(poi);
	DB_EXIT
}

void *U_malloc(unsigned const size)
/* Debug malloc() function */
{	char *p;

	DB_ENTER("U_malloc");
	p = malloc(size);
	DB_PRINT("mem", ("malloc = %08lx (%04x)", (depoi)(p), size));
	DB_RETURN( p);
}

char *U_strdup(const char * const poi)
/* Debug malloc() function */
{	char *p;

	DB_ENTER("U_strdup");
	p = strdup(poi);
	DB_PRINT("mem", ("strdup = %08lx (%04x)", (depoi)(p), strlen(poi) + 1));
	DB_RETURN( p);
}

void *U_realloc(void *poi, const unsigned size)
{	void *h;

	DB_ENTER("U_realloc");
	h = realloc(poi, size);
	
	DB_PRINT("mem", ("realloc = %08lx (%04x) [%08lx]", (depoi)(h)
	 , size , (depoi)(poi)));

	DB_RETURN( h);
}
#undef depoi
#endif
#endif


char *dupstr(const char * const str)
/* duplicate string and emit an error message if failed */
{	char *h;

	chkHeap()
	if((h = U_strdup(str)) == NULL)
		fatal(E_noMem);
	chkHeap()
	return h;
}

void *dupmem(const int length, const void * const mem)
/* duplicate memory block and emit error message if failed */
{	void *h;

	chkHeap()
	memcpy(h = getmem(length + 1), mem, length);
	((unsigned char*)h)[length] = '\0';
	chkHeap()
	return h;
}

#ifdef __TURBOC__
#pragma argsused
#endif
int fsplit(char *fname, char **dr, char **path, char **name, char **ext, int d)
/* Split a file name.
	fname is the file name to split
	dr, path, name, ext:	Pointers, which will receive the results.
							These are malloc'ed and NULL, if the particular
							part is missing.
							dr does not has the ':'.
							the trailing '\\' is stripped.
							ext does not contain the leading '.'.
							slashes are transformed into backslashes '/' -> '\\'.
							multiple backslashes are reduced to a single one.
*/
{	char *p, *h, *z;

#define iff(var,value) if(var) *(var) = (value)
	iff(dr, NULL);
	iff(path, NULL);
	iff(name, NULL);
	iff(ext, NULL);
	if((p = fname) == NULL) return !NUL;

	chkHeap()
	if(p[1] == ':') {	/* drive spec */
		if(dr) {
			if((*dr = z = U_malloc(2)) == NULL)
				return NUL;
			*z = *p;
			z[1] = NUL;
		}
		p += 2;
	}

	chkHeap()
	if(*p) {	/* there are characters left */
	/* flipping slashes */
		h = p - 1;
		while((h = strchr(h + 1, '/')) != NULL)
			*h = '\\';
	/* locate last '\\' */
		if((h = strrchr(p, '\\')) != NULL) { 	/* there is a backslash => a path */
			if(path) {
				if((*path = z = U_malloc(h - p + 1)) == NULL)
					return NUL;
				/* because malloc() before squeeze => if multiple '\\'s 
					some memory is wasted */
				/* while copying squeeze multiple '\\'s */
				do {
					if(*p == '\\')	/* squeeze */
						while(p[1] == '\\') ++p;
					if(p >= h) break;
					*z++ = *p++;
				} while(1);
				*z = NUL;
			}
			p = h + 1;	/* skip path */
			chkHeap()
		}
	/* check for special names "." and ".." */
		if((h = strrchr(p, '.')) == NULL 	/* no extension at all */
		 || *p == '.' && (p[1] == NULL || p[1] == '.' && p[2] == NUL)) /* spc */
		 	return name? (*name = U_strdup(p)) != NULL: !NUL;
	/* copy the name */
		if(name) {
			if((*name = z = U_malloc(h - p + 1)) == NULL)
				return NUL;
			memcpy(z, p, h - p);
			z[h - p] = NUL;
		}
		chkHeap()
	/* copy the extension */
		return ext? (*ext = U_strdup(h + 1)) != NULL: !NUL;
	}
	return !NUL;
}


char *fmerge(char *fname, char *dr, char *path, char *name, char *ext)
/* Merge a file name.

	dr, path, name, ext:	Pointers of the path components.
							If one of these is NULL, it is not merged.
							If it is "", it's merged and the standard characters
								are inserted (':', '\\', '.').
	fname:	Pointer to the memory to receive the result.
			If NULL: the necessary memory is malloc'ed.

   Return: pointer to the beginning of the result or NULL if malloc failed.
*/
{	int len;
	char *p;

	if((p = fname) == NULL) {	/* determine file name length */
		len = dr? 3: 1;		/* add the NUL terminator */
		if(path) len += strlen(path) + 1;
		if(name) len += strlen(name);
		if(ext) len += strlen(ext) + 1;
		if((fname = p = U_malloc(len)) == NULL)
			return NULL;
		*fname = NUL;
	}
	if(dr) {
		*p++ = *dr;
		*p++ = ':';
		*p = NUL;
	}
	if(path) {
		p = stpcpy(p, path);
		*p++ = '\\';
		*p = NUL;
	}
	if(name)	p = stpcpy(p, name);
	if(ext) {
		*p++ = '.';
		strcpy(p, ext);
	}
	chkHeap()
	return fname;
}

char *namePart(char *fnam)
{	char *p;
	FLAG8 c;

	p = strchr(fnam, NUL);
	while(--p >= fnam
	 && (c = *p) != ':'
	 && c != '/'
	 && c != '\\');

	return p;
}

char *replName(char *nam, char *fnam)
/*	Replaces the filename portion (not included the extension) with
	the potion of fnam.
*/
{	char *drive, *path, *name, *ext;

	if(!fsplit(nam, &drive, &path, NULL, &ext, 0)
	 || !fsplit(fnam, NULL, NULL, &name, NULL, 0)
	 || (nam = fmerge(NULL, drive, path, name, ext)) == NULL)
	 	fatal(E_noMem);
	U_free(drive);
	U_free(path);
	U_free(name);
	U_free(ext);
	return nam;
}

int wildcarded(char *name)
{	char *s, *c;

	s = "SKAUS19A4";

	if(!fsplit(name, NULL, NULL, &c, NULL, 0))
		fatal(E_noMem);

	if(c) {
		s = strpbrk(c, "*?");
		U_free(c);
		return s != NULL;
	}

	return NULL;
}

#ifndef _MICROC_
int cbreak(int action)
/* Controls the verbose ^Break checking flag of DOS:
	action == -1:	return the current state of the flag
	action ==  0:	disable verbose ^Break checking
	action ==  1:	enable verbose ^Break checking

	Return:	current or new state (0 == disable)
*/
{	USEREGS

	if(action < 0)	/* question the current state */
		_AX = 0x3300;	/* DOS API "Extended Break Checking" Get */
	else {			/* set the state */
		_DX = !!action;	/* DL := 0 (disable), 1 (enable) */
		_AX = 0x3301;	/* DOS API "Extended Break Checking" Set */
	}
	geninterrupt(0x21);
	return _AL;
}
#endif

void beautify(char * const str)
/* Insert thousend separators into the string. */
{	char *p, *h, c;
	struct REGPACK reg;
	int i;

	if(!*str) {			/* no number at all -> zero */
		*(word*)str = '0';
		return;
	}

	reg.r_ax = 0x3800;	/* DOS API "Get Country Information" */
	reg.r_dx = FP_OFF(&ybuf[sizeof(ybuf) - 0x28]);
	reg.r_ds = FP_SEG(ybuf);
	intr(0x21, aS(reg));
	if(reg.r_flags & 1) return;		/* no country information */
	if((c = ybuf[sizeof(ybuf) - 0x28 + 7]) == NUL)	/* no separator */
		return;

	i = 3;
	h = strchr(str, NUL);
	*(p = h + (h - str - 1) / 3) = NUL;
	while(h != p) {
		*--p = *--h;
		if(!--i) {
			i = 3;
			*--p = c;
		}
	}
}

void upStr(unsigned char *s, unsigned table)
/* Upcase a ASCIZ string according table type (2 == normal, 4 == file names) */
{	unsigned c;

#ifdef _MICROC_
	struct {
		byte NLS_id;
		dword NLS_addr;
	} nls;

	nls;				/* lea ax, nls */
	asm {
		mov di, ax
		mov ax, ss
		mov es, ax		; /* ES:DI address of buffer */
		mov cx, 5		; /* Length of buffer */
		mov bx, 0ffffh	; /* current country */
		mov dx, bx		; /* current code page */
	}
	table;				/* mov ax, table */
	asm {
		mov ah, 65h		; /* Extended Country Information */
		int 21h
		sbb ax, ax
	}
	if(nargs())			/* call failed */
#else
#include <algnbyte.h>
	struct {
		byte NLS_id;
		dword NLS_addr;
	} nls;
#include <algndflt.h>
#ifdef __TURBOC__
	asm {
		lea di, ss:nls
		mov ax, ss
		mov es, ax		; /* ES:DI address of buffer */
		mov cx, 5		; /* Length of buffer */
		mov bx, 0ffffh	; /* current country */
		mov dx, bx		; /* current code page */
		mov al, Byte Ptr table
		mov ah, 65h		; /* Extended Country Information */
		int 21h
		sbb ax, ax
	}
	if(_AX)
#else
	struct REGPACK rp;

	rp.r_di = FP_OFF(&nls);
	rp.r_es = FP_SEG(&nls);
	rp.r_cx = 5;
	rp.r_bx = rp.r_dx = 0xFFFF;
	rp.r_ax = table | 0x6500;
	intr(0x21, &rp);
	if(rp.r_flags & 1)
#endif
#endif
		strupr(s);		/* Because the get of the NLS failed, use 7-bit */
	else while((c = *s) != 0) {				/* Use the table to upcase */
		if(c >= 'a' && c <= 'z')
			*s += 'A' - 'a';
		else if(c > 127)
			*s = peekb(DW_HI(nls.NLS_addr), DW_LO(nls.NLS_addr) + c - 0x80 + 2);
		++s;
	}
}

void upCaseFct(int mode, char *s)
/* Upper-case the string s.
	If mode == 0, no upper-case; mode == 1, non-file name upper-case;
	mode == 2, file name upper-case
*/
{	switch(mode) {
		case 1: nameupr(s);
		case 0: break;
		case 2: fnameupr(s); break;
#ifndef NDEBUG
		default:	fatal(E_noUpcaseMode);
#endif
	}
}

#ifdef _MICROC_
int creat(/*char **/fnam, /*int */attr)
asm {
	mov dx, 6[bp]			; /* load the file name */
	mov cx, 4[bp]			; /* file attributes */
	mov ah, 3ch
	int 21h
	jnc creat1				; /* creation successful */
	sbb ax, ax				; /* AX := -1 to indicate open failure */
creat1:
}
#endif

void Rewind(FILEP fp, char *fnam)
{
#ifdef _MICROC_
	if(lrewind(fp))
		fatal(E_accessFile, fnam);
#else
	if(ferror(fp))
		fatal(E_accessFile, fnam);
	rewind(fp);
#endif
}
