/* $Revision Header *** Header built automatically - do not edit! ***********
 *
 *	(C) Copyright 1991 by Torsten Jürgeleit
 *
 *	Name .....: files.c
 *	Created ..: Thursday 19-Dec-91 20:00:48
 *	Revision .: 0
 *
 *	Date        Author                 Comment
 *	=========   ====================   ====================
 *	11-Apr-92   Torsten Jürgeleit      corrected abort if line too long
 *	19-Dec-91   Torsten Jürgeleit      Created this file!
 *
 ****************************************************************************
 *
 *	Support routines for reading and parsing text files
 *
 * $Revision Header ********************************************************/

	/* Includes */

#include <exec/types.h>
#include <exec/memory.h>
#include <libraries/dos.h>
#include <libraries/diskfont.h>
#include <intuition/intuition.h>
#ifdef AZTEC_C
#include <functions.h>   /* needed for Aztec C - prototypes and pragmas for all Amiga system functions */
#endif
#include <libraries/memwatch.h>   /* header file for memory debug link library (Fish 240) - AFTER functions.h */
#include <string.h>
#include "/render/render.h"
#include "files.h"

	/* Defines for internal flags */

#define TEXT_FILE_FLAG_READ_NEXT_LINE		(1 << 15)	/* if current line is empty or continued then read next line too */

	/* Static prototypes */

SHORT fill_read_buffer(struct FileData  *fd);
BPTR  dos_open(BYTE *name, LONG flags);
LONG  dos_read(BPTR fh, BYTE *buffer, LONG size);
VOID  dos_close(BPTR fh);

	/* Static pragmas */

#pragma regcall(fill_read_buffer(a0))
#pragma regcall(dos_open(a0,d0))
#pragma regcall(dos_read(a0,a1,d0))
#pragma regcall(dos_close(a0))

	/* Open text file */

   struct FileData *
open_text_file(BYTE *name, USHORT read_buffer_size, USHORT line_buffer_size,
							       USHORT flags)
{
   struct FileData  *fd = NULL;

   if (name && read_buffer_size && line_buffer_size) {
      BPTR fh;

      if (fh = dos_open(name, (LONG)MODE_OLDFILE)) {
	 if (!(fd = AllocMem((LONG)(sizeof(struct FileData) +
				   read_buffer_size + line_buffer_size + 1),
						      (LONG)MEMF_PUBLIC))) {
	    dos_close(fh);
	 } else {

	    /* Init internal file data */
	    fd->fd_Name           = name;
	    fd->fd_ReadBufferSize = read_buffer_size;
	    fd->fd_LineBufferSize = line_buffer_size;
	    fd->fd_Flags          = flags;
	    fd->fd_FileHandle     = fh;
	    fd->fd_ReadBuffer     = (BYTE *)(fd + 1);
	    fd->fd_CurrentPtr     = NULL;
	    fd->fd_LineBuffer     = fd->fd_ReadBuffer + read_buffer_size;
	    fd->fd_ID             = ISUP_ID;

	    /* Init public file data struct */
	    fd->fd_Line    = NULL;
	    fd->fd_LineLen = 0;
	    fd->fd_LineNum = 0;
	 }
      }
   }
   return(fd);
}
	/* Read line from text file */

   SHORT
read_text_line(struct FileData  *fd)
{
   SHORT status = TEXT_FILE_STATUS_NORMAL;

   if (!fd || fd->fd_ID != ISUP_ID) {
      status = TEXT_FILE_ERROR_NO_FILE_DATA;
   } else {
      BYTE   *line = fd->fd_LineBuffer;
      USHORT flags = fd->fd_Flags, len = 0;

      do {
	 BYTE c, *ptr;

	 /* Fill read buffer */
	 if (!(ptr = fd->fd_CurrentPtr) || ptr >= fd->fd_EndPtr) {
	    if ((status = fill_read_buffer(fd)) == TEXT_FILE_STATUS_NORMAL) {
	       ptr = fd->fd_CurrentPtr;
	    }
	 }
	 if (status == TEXT_FILE_STATUS_NORMAL) {

	    /* Strip leading white space */
	    if (flags & TEXT_FILE_FLAG_TRIM_LINE) {
	       while ((c = *ptr) == ' ' || c == '\t') {
		  if (++ptr >= fd->fd_EndPtr) {
		     if ((status = fill_read_buffer(fd)) !=
						  TEXT_FILE_STATUS_NORMAL) {
			break;
		     } else {
			ptr = fd->fd_CurrentPtr;
		     }
		  }
	       }
	    }
	    if (status == TEXT_FILE_STATUS_NORMAL) {

	       /* Copy line from read buffer to line buffer */
	       if (flags & TEXT_FILE_FLAG_SKIP_COMMENTS) {
		  BOOL comment = FALSE;

		  /* Copy without comments */
		  while ((c = *ptr) != '\n' || comment == TRUE) {

		     /* Write char to line buffer */
		     if (comment == FALSE) {
			if (len >= fd->fd_LineBufferSize) {
			   status = TEXT_FILE_ERROR_LINE_TOO_LONG;
			   break;
			} else {
			   *line++ = c;
			   len++;
			}
		     }

		     /* Increment ptr and fill read buffer if neccessary */
		     if (++ptr == fd->fd_EndPtr) {
			if ((status = fill_read_buffer(fd)) !=
						  TEXT_FILE_STATUS_NORMAL) {
			   break;
			} else {
			   ptr = fd->fd_CurrentPtr;
			}
		     }

		     /* Check for comment delimiters */
		     if (comment == FALSE) {
			if (c == '/' && *ptr == '*') {
			   comment = TRUE;

			   /* Remove '/' from line */
			   line--;
			   len--;
			}
		     } else {
			if (c == '\n') {
			   fd->fd_LineNum++;
			} else {
			   if (c == '*' && *ptr == '/') {
			      comment = FALSE;

			      /* Skip '/' */
			      if (++ptr == fd->fd_EndPtr) {
				 if ((status = fill_read_buffer(fd)) !=
						  TEXT_FILE_STATUS_NORMAL) {
				    break;
				 } else {
				    ptr = fd->fd_CurrentPtr;
				 }
			      }
			   }
			}
		     }
		  }
	       } else {

		  /* Copy with comments */
		  while ((c = *ptr) != '\n') {

		     /* Write char to line buffer */
		     if (len >= fd->fd_LineBufferSize) {
			status = TEXT_FILE_ERROR_LINE_TOO_LONG;
			break;
		     } else {
			*line++ = c;
			len++;

			/* Increment ptr and fill read buffer if neccessary */
			if (++ptr == fd->fd_EndPtr) {
			   if ((status = fill_read_buffer(fd)) !=
						  TEXT_FILE_STATUS_NORMAL) {
			      break;
			   } else {
			      ptr = fd->fd_CurrentPtr;
			   }
			}
		     }
		  }
	       }
	       if (status == TEXT_FILE_STATUS_NORMAL) {

		  /* Strip trailing white space */
		  if (flags & TEXT_FILE_FLAG_TRIM_LINE) {
		     while (len && ((c = *(line - 1)) == ' ' || c == '\t')) {
			line--;
			len--;
		     }
		  }
		  if (!len) {

		     /* Check for skip empty line */
		     if (flags & TEXT_FILE_FLAG_SKIP_EMPTY_LINES) {
			flags |= TEXT_FILE_FLAG_READ_NEXT_LINE;
		     } else {
			flags &= ~TEXT_FILE_FLAG_READ_NEXT_LINE;
		     }
		  } else {

		     /* Check for line continuation - last char is '\'? */
		     if (flags & TEXT_FILE_FLAG_LINE_CONTINUATION) {
			if (*(line - 1) == '\\') {
			   flags |= TEXT_FILE_FLAG_READ_NEXT_LINE;

			   /* Strip '\' from end of line */
			   line--;
			   len--;
			} else {
			   flags &= ~TEXT_FILE_FLAG_READ_NEXT_LINE;
			}
		     } else {
			flags &= ~TEXT_FILE_FLAG_READ_NEXT_LINE;
		     }
		  }

		  /* Mark end of string */
		  *line             = '\0';
		  fd->fd_CurrentPtr = ptr + 1;   /* skip trailing '\n' */
		  fd->fd_LineNum++;
	       }
	    }
	 }
      } while (status == TEXT_FILE_STATUS_NORMAL &&
				   (flags & TEXT_FILE_FLAG_READ_NEXT_LINE));
      /* Init public data */
      if (status == TEXT_FILE_STATUS_NORMAL) {
	 fd->fd_Line    = fd->fd_LineBuffer;
	 fd->fd_LineLen = len;
      } else {
	 fd->fd_Line    = NULL;
	 fd->fd_LineLen = 0;
      }
   }
   return(status);
}
	/* Fill read buffer from text file */

   STATIC SHORT
fill_read_buffer(struct FileData  *fd)
{
   LONG  len;
   SHORT status;

   if ((len = dos_read(fd->fd_FileHandle, fd->fd_ReadBuffer, (LONG)
					   fd->fd_ReadBufferSize)) == -1L) {
      status = TEXT_FILE_ERROR_READ_FAILED;
   } else {
      if (!len) {
	 status = TEXT_FILE_STATUS_EOF;
      } else {
	 fd->fd_CurrentPtr = fd->fd_ReadBuffer;
	 fd->fd_EndPtr     = fd->fd_ReadBuffer + len;
	 status            = TEXT_FILE_STATUS_NORMAL;
      }
   }
   return(status);
}
	/* Close text file */

   VOID
close_text_file(struct FileData  *fd)
{
   if (fd && fd->fd_ID == ISUP_ID) {
      dos_close(fd->fd_FileHandle);
      FreeMem(fd, (LONG)(sizeof(struct FileData) + fd->fd_ReadBufferSize +
						fd->fd_LineBufferSize + 1));
   }
}
	/* DOS calls between open and close of dos library
	 * -> neccessary for DOS calls from within intuisup.library
	 */
#asm
;---------------------------------------------------------------------------
; Support macros
;---------------------------------------------------------------------------

PUSH	MACRO
	movem.l	\1,-(sp)
	ENDM

PULL	MACRO
	movem.l	(sp)+,\1
	ENDM

CALLSYS	MACRO
	XREF	_LVO\1
	jsr	_LVO\1(a6)
	ENDM

;---------------------------------------------------------------------------
; External definitions
;---------------------------------------------------------------------------

	XDEF	_dos_open
	XDEF	_dos_read
	XDEF	_dos_close

;---------------------------------------------------------------------------
; BPTR dos_open(BYTE *name, LONG flags)
;                    a0         d0
;---------------------------------------------------------------------------

_dos_open:
	PUSH	d2-d3/a2/a6

	; --- save parameters
	move.l	a0,a2			; a2 := file name ptr
	move.l	d0,d2			; d2 := flags
	moveq	#0,d3			; d3 := return code

	; --- open dos library
	move.l	(4).w,a6		; a6 := SysBase
	lea	dos_name(pc),a1		; a1 := library name
	moveq	#0,d0			; d0 := version
	CALLSYS	OpenLibrary
	tst.l	d0
	beq.s	do_exit

	; --- call DOS function Open()
	move.l	d0,a6			; a6 := DosBase
	move.l	a2,d1			; d1 := file name ptr
					; d2 := flags
	jsr	-$1e(a6)		; CALLSYS Open -> Bug in c16.lib (POSITIVE lib offset)
	move.l	d0,d3			; d3 := BPTR to file handle

	; --- close dos library
	move.l	a6,a1			; a1 := DosBase
	move.l	(4).w,a6		; a6 := SysBase
	CALLSYS	CloseLibrary

do_exit:
	; --- prepare return code
	move.l	d3,d0

	PULL	d2-d3/a2/a6
	rts

;---------------------------------------------------------------------------
; LONG dos_read(BPTR fh, BYTE *buffer, LONG size);
;                  a0         a1          d0
;---------------------------------------------------------------------------

_dos_read:
	PUSH	d2-d4/a2-a3/a6

	; --- save parameters
	move.l	a0,a2			; a2 := BPTR to file handle
	move.l	a1,a3			; a3 := buffer ptr
	move.l	d0,d3			; d3 := size
	moveq	#-1,d4			; d4 := return code

	; --- open dos library
	move.l	(4).w,a6		; a6 := SysBase
	lea	dos_name(pc),a1		; a1 := library name
	moveq	#0,d0			; d0 := version
	CALLSYS	OpenLibrary
	tst.l	d0
	beq.s	dr_exit

	; --- call DOS function Read()
	move.l	d0,a6			; a6 := DosBase
	move.l	a2,d1			; d1 := BPTR to file handle
	move.l	a3,d2			; d2 := buffer ptr
					; d3 := size
	jsr	-$2a(a6)		; CALLSYS Read -> Bug in c16.lib (POSITIVE lib offset)
	move.l	d0,d4			; d4 := num of characters read

	; --- close dos library
	move.l	a6,a1			; a1 := DosBase
	move.l	(4).w,a6		; a6 := SysBase
	CALLSYS	CloseLibrary

dr_exit:
	; --- prepare return code
	move.l	d4,d0

	PULL	d2-d4/a2-a3/a6
	rts

;---------------------------------------------------------------------------
; VOID dos_close(BPTR fh);
;                   a0
;---------------------------------------------------------------------------

_dos_close:
	PUSH	a2/a6

	; --- save parameters
	move.l	a0,a2			; a2 := BPTR to file handle

	; --- open dos library
	move.l	(4).w,a6		; a6 := SysBase
	lea	dos_name(pc),a1		; a1 := library name
	moveq	#0,d0			; d0 := version
	CALLSYS	OpenLibrary
	tst.l	d0
	beq.s	dc_exit

	; --- call DOS function Close()
	move.l	d0,a6			; a6 := DosBase
	move.l	a2,d1			; d1 := BPTR to file handle
	jsr	-$24(a6)		; CALLSYS Close -> Bug in c16.lib (POSITIVE lib offset)

	; --- close dos library
	move.l	a6,a1			; a1 := DosBase
	move.l	(4).w,a6		; a6 := SysBase
	CALLSYS	CloseLibrary

dc_exit:
	PULL	a2/a6
	rts

;---------------------------------------------------------------------------
; Static data
;---------------------------------------------------------------------------

dos_name:
	dc.b	"dos.library"

	EVEN
#endasm
