[alt.sources.amiga] Genlib - Run-Time Library Generator Utility 1/2

ronbo@vixen.uucp (Ron Hitchens) (10/23/89)

    Have you been wanting to create your own Amiga disk-based run-time
libraries, but haven't been able to figure out how to do it?  Well, here's
a handy little utility that will do all the hard work for you.  Genlib
will let you write a run-time library in C nearly as easily as writing a
conventional program.

    I wrote Genlib to help me create a run-time library for a commercial
product that we're developing.  It began life as a hack, but evolved into
a stable, professional grade utility.  I considered maybe developing it
further into a product, but decided instead to release it to the
world to help stimulate talented hackers to create useful libraries.

    There are two shar files to this posting, this is the first, and
contains Genlib and its template files.  The second shar contains the
docs and source for a sample run-time library.

    Genlib is not public domain, but may be redistributed freely (see
the notice in README).  Permission is explictly given for Fred Fish to
include it in his collection, and for posting to services like CompuServe.

    Note the warning in the makefile for the sample library, make sure your
stack is big enough before running make.

    Enjoy.

Ron Hitchens	 _____	| ronbo@vixen.uucp			- Smart uucp
  Sleepless     /(O O)\	| ...!cs.utah.edu!caeco!vixen!ronbo	- Stupid uucp
    Software      (^)	| hitchens@cs.utexas.edu		- Internet
"To be is to do" -Socrates  "To do is to be" -Sartre  "Do be do be do" -Sinatra


# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by vixen!ronbo on Sun Oct 22 15:40:53 MDT 1989
# Contents:  README MANIFEST asmhdr.template chdr.template library.template
#	stubs.template main.c load.c dump.c parse.c getopt.c genlib.h makefile
 
echo x - README
sed 's/^@//' > "README" <<'@//E*O*F README//'

	Genlib - Automatically Generate Run-Time Libraries


   Genlib is a program and a set of template files that will allow you
to create your own Amiga disk-based run-time libraries.  It automatically
generates the various header and code files that are necessary to build
a library code file in the proper format for use by the Amiga system.
Genlib is setup so that you can write the majority of your library code
in C (specifically Manx Aztec C), although assembler routines can also
be placed in the same library, so long as they take arguments on the
stack like C does.

   Included in this posting are:
	The genlib program, which takes as input a specification file which
	defines various parameters of the library to be built, and a list
	of functions that will exist in the library.  It creates include
	files for both C and assembler, and two assembler source files;
	one which is the run-time library boilerplate interface code, and
	one which defines the link-library interface glue which allows
	application programs to call-through into the runtime library.

	Four template files.  These files are the skeletons of the four
	files that genlib will produce.  The information derived from the
	specification file is inserted into them to produce finished
	source code for the new library.

	Sample library code.  Three C source files are provided, along
	with the matching genlib specification file, from which can be
	built a sample run-time library.  A makefile is also provided
	to automatically generate and compile the sample library.

	Test program.  A simple test application is provided to test
	the sample run-time library.


   See the file Doc/usage for information on how to run genlib, and the
format of the specification file.


 	Copyright 1989, Sleepless Software, All Rights Reserved

  Genlib is not public domain, it is copyright by Sleepless Software.
You are free to redistribute and re-post it, so long as you keep it all
together (including source), you don't remove our original copyright
notices, and you don't charge anything for it other than reasonable
copying costs.  Also, there are no restrictions on the libraries you
generate with it.  In other words, you are free to sell a library which
you used genlib to create, but you may not sell genlib itself without
our permission.

	Sleepless Software
	45 N. Main
	North Salt Lake, Utah  84054
	(801) 292-2190

Ron Hitchens	 _____	| ronbo@vixen.uucp			- Smart uucp
  Sleepless     /(O O)\	| ...!cs.utah.edu!caeco!vixen!ronbo	- Stupid uucp
    Software      (^)	| hitchens@cs.utexas.edu		- Internet
"To be is to do" -Socrates  "To do is to be" -Sartre  "Do be do be do" -Sinatra

@//E*O*F README//
chmod u=rw,g=r,o=r README
 
echo x - MANIFEST
sed 's/^@//' > "MANIFEST" <<'@//E*O*F MANIFEST//'

	Genlib - Shipping manifest

   Genlib is posted in two shar files.  When you have unpacked both shars
you should have the following files:

Shar 1:
	README
	MANIFEST

	asmhdr.template		The four template files
	chdr.template
	library.template
	stubs.template

	main.c			Source for the Genlib program
	load.c
	dump.c
	parse.c
	getopt.c
	genlib.h

	makefile		Makefile for genlib


Shar 2:
	Doc/			Sub-directory for doc files

	Doc/usage		How to use Genlib
	Doc/programmer.notes	How to code run-time libraries
	Doc/hacker.notes	Notes for hackers of Genlib

	Sample/			Sub-directory for Sample library source

	Smaple/makefile		Makefile for sample lib and test program

	Sample/foo.gen		Spec file for the sample library

	Sample/init.c		Source for the sample library
	Sample/dialog.c
	Sample/timer.c

	Sample/test.c		Program to test the library


Ron Hitchens	 _____	| ronbo@vixen.uucp			- Smart uucp
  Sleepless     /(O O)\	| ...!cs.utah.edu!caeco!vixen!ronbo	- Stupid uucp
    Software      (^)	| hitchens@cs.utexas.edu		- Internet
"To be is to do" -Socrates  "To do is to be" -Sartre  "Do be do be do" -Sinatra
@//E*O*F MANIFEST//
chmod u=rw,g=r,o=r MANIFEST
 
echo x - asmhdr.template
sed 's/^@//' > "asmhdr.template" <<'@//E*O*F asmhdr.template//'

- ------------------------------------------------------------------------
-	Genlib - Assembler header file template
- ------------------------------------------------------------------------

+AIFND

; ------------------------------------------------------------------------
+ACOMMENT
;
+AGENTIME
;
;	This file automatically generated by genlib
;	Genlib is Copyright 1989, Sleepless Software
;	Author: Ron Hitchens (ronbo@vixen.uucp) (801) 292-2910
; ------------------------------------------------------------------------

	include 'exec/types.i'
	include 'exec/lists.i'
	include 'exec/libraries.i'


+ASTRUCT
	UBYTE		lb_Flags
	UBYTE		lb_pad
	ULONG		lb_LibA4
-	The above fields must be defined as shown, any additional
-	fields may be added to the struct following the LibA4 field
+ASIZEOF


+ALIBNAME

+AENDC
@//E*O*F asmhdr.template//
chmod u=rw,g=r,o=r asmhdr.template
 
echo x - chdr.template
sed 's/^@//' > "chdr.template" <<'@//E*O*F chdr.template//'

- ----------------------------------------------------------------------
-	Genlib - C header file template
- ----------------------------------------------------------------------
+CIFDEF

/*
 * ---------------------------------------------------------------------
+CCOMMENT
 *
+CGENTIME
 *
 *	This file automatically generated by genlib
 *	Genlib is Copyright 1989, Sleepless Software
 *	Author: Ron Hitchens (ronbo@vixen.uucp) (801) 292-2910
 * ---------------------------------------------------------------------
 */


#include <exec/types.h>
#include <exec/lists.h>
#include <exec/libraries.h>


+CSTRUCT
	struct Library		LibNode;
	unsigned char		Flags;
	unsigned char		pad;
	unsigned long		LibA4;
-	The above fields must be defined as shown, any additional
-	fields may be added to the struct following the LibA4 field
};


+CLIBNAME

+CENDIF
@//E*O*F chdr.template//
chmod u=rw,g=r,o=r chdr.template
 
echo x - library.template
sed 's/^@//' > "library.template" <<'@//E*O*F library.template//'

; -----------------------------------------------------------------
-	Genlib - library.template
- -----------------------------------------------------------------
-
-	Template for Amiga run-time library boilerplate code
+LCOMMENT
;
+AGENTIME
;
;	This file automatically generated by genlib.
;	Genlib is Copyright 1989, Sleepless Software
;	Author: Ron Hitchens (ronbo@vixen.uucp) (801) 292-2910
-
-	This code defines the data structures and interface
-	routines necessary to create a run-time library which
-	is mostly written in Manx Aztec C.  This template is
-	read as input by the genlib program, the output file
-	will have information substituted into it derived from
-	the specification file which is also input to genlib.
-
-	The output file derived from this template should then
-	be assembled to make a .o file.  This .o file should
-	then be linked with all the .o files of the C code
-	which will also be in the library.  The link should also
-	include the apprpriate Manx link libraries (-lc32, etc)
-	and the stubs created by genlib (see stubs.template)
-
-	The output of that link will be an Amiga run-time library
-	which can be placed in devs: and used via OpenLibrary.
-
- -----------------------------------------------------------------
-
-	Copyright 1989, Sleepless Software, All Rights Reserved
-
-	Permission is granted to use and redistribute, so long as
-	no fee is charged.
-
-	Author: Ron Hitchens, Sleepless Software
-	Date: Wed Sep  6 16:13:13 MDT 1989
; -----------------------------------------------------------------


	include 'exec/types.i'
	include 'exec/libraries.i'
	include 'exec/nodes.i'
	include 'exec/lists.i'
	include 'exec/alerts.i'
	include 'exec/initializers.i'
	include 'exec/resident.i'
	include 'exec/execbase.i'
	include 'libraries/dos.i'

+AINCLUDE

	far	data
	far	code


+EQU

	dseg

	public		__H1_org	; for grokking a4
	public		__H1_end
	public		__H2_org
	public		__H2_end

	public		_SysBase
	public		_DOSBase


_SysBase ds.l		1	; global ExecBase, used by Manx stubs
_DOSBase ds.l		1	; global DOSBase, used by Manx stubs
SegList  ds.l		1	; a private place to hold my seg list ptr


	cseg		; define code

;	User Defined Library Functions
+CDEFS

;	External refs to functions that must exist in library's C code
	public		_LibInitHook
	public		_LibExitHook

;	These symbols live here, make them visible to the C code
	public		_LibVersion
	public		_LibRevision
	public		_LibName
	public		_LibID

;	For small data model Manx C code
	public		_geta4


	EXTERN_LIB	OpenLibrary
	EXTERN_LIB	CloseLibrary
	EXTERN_LIB	Alert
	EXTERN_LIB	FreeMem
	EXTERN_LIB	Remove
	EXTERN_LIB	Supervisor


LIBCALL	MACRO		*
	IFNE NARG-1
		FAIL	!!!
	ENDC
	jsr		_LVO\1(a6)	; assumes a6 is already setup
	ENDM



; -----------------------------------------------------------------
;	Entry point if library is run as a program, return an
;	error code and exit

	entry	.begin
	public	.begin

@.begin
	moveq	#-1,d0
	rts


; -----------------------------------------------------------------
;	Romtag - Must live in first hunk

RomTag:
	dc.w	RTC_MATCHWORD
	dc.l	RomTag
	dc.l	EndTag
	dc.b	RTF_AUTOINIT
	dc.b	VERSION
	dc.b	NT_LIBRARY
	dc.b	PRIORITY
	dc.l	_LibName
	dc.l	_LibID
	dc.l	Init
EndTag:


; -----------------------------------------------------------------
;	Init table

Init:
+LSIZEOF
	dc.l	FuncTable
	dc.l	DataTable
	dc.l	InitRoutine


; -----------------------------------------------------------------
;	Function table

FuncTable:
	dc.l	Open
	dc.l	Close
	dc.l	Expunge
	dc.l	Null

	dc.l	_geta4

;	User Defined Library Functions
+FUNCTAB

	dc.l	-1		; end of table marker


; -----------------------------------------------------------------
;	Data table

DataTable:
	INITBYTE	LN_TYPE,NT_LIBRARY
	INITLONG	LN_NAME,_LibName
	INITBYTE	LIB_FLAGS,LIBF_SUMUSED!LIBF_CHANGED
	INITWORD	LIB_VERSION,VERSION
	INITWORD	LIB_REVISION,REVISION
	INITLONG	LIB_IDSTRING,_LibID
	dc.l		0

; -----------------------------------------------------------------
;	Constant strings that need to be defined

_LibVersion:
	dc.l		VERSION

_LibRevision
	dc.l		REVISION

_LibName:
+LIBNAME

_LibID:
+LIBID

DosName:
	DOSNAME		; define the name of the dos library

	even		; assure word alignment


; -----------------------------------------------------------------
;	Init Routine, called when library is loaded
;
;	This routine is called after the library has been allocated.
;	The library pointer is in d0.  The segment list is in a0
;	if it returns non-zero then the library will be linked into
;	the library list.
;
;	d0 = libptr, a0 = seglist, a6 = execbase

InitRoutine:
	movem.l	d1/d2/a0-a2/a4/a5,-(sp)	; save regs

	move.l	d0,a5			; get library ptr into an addr reg

	bsr	_geta4			; load our lib's a4 value
	move.l	a4,lb_LibA4(a5)		; stash it for use by linklib stubs

	clr.b	lb_Flags(a5)		; unset all our flags

	move.l	a6,_SysBase		; save pointer to Exec library
	move.l	a0,SegList		; save pointer to our loaded code

	lea	DosName(pc),a1		; Open the DOS library
	clr.l	d0			; set d0 to zero
	LIBCALL	OpenLibrary		; make a call to Exec

	move.l	d0,_DOSBase		; save pointer to DOS library
	bne.s	1$

	ALERT	AG_OpenLib!AO_DOSLib	; Can't open Dos Lib, Big Trouble

1$:
	lea	__H1_end,a1		; addr of end of init data seg
	lea	__H2_org,a2		; addr of begin of un-init data seg
	cmp.l	a1,a2			; are the two segs together?
	bne	3$			; no, don't have to clear bss
	move.w	#((__H2_end-__H2_org)/4)-1,d1	; longword size of bss
	bmi	3$			; no bss, nothing to do
	clr.l	d2			; put a zero in d2

2$:
	move.l	d2,(a1)+		; store 0 at addr in a1, bump a1 by 4
	dbra	d1,2$			; dec d1, branch if result != -1

3$:
	bsr	ffp_reset		; go try to reset the '881

	move.l	a6,-(sp)		; save a6, just in case
	move.l	a5,-(sp)		; push our lib ptr on the stack
	jsr	_LibInitHook		; call the external C routine
	addq.l	#4,sp			; pop the arg off the stack
	move.l	(sp)+,a6		; restore a6

	tst.l	d0			; check for good return from hook
	beq	4$			; all is well

;	Oh shit, the C init hook had trouble, abort the open
	move.l	_DOSBase,a1		; Get DOSBase
	LIBCALL	CloseLibrary		; Close the DOS library

	clr.l	d0			; indicate that our open failed
	bra	5$			; go return

4$:
	move.l	a5,d0			; return our lib pointer as result
5$:
	movem.l	(sp)+,d1/d2/a0-a2/a4/a5	; restore regs
	rts				; all inited now, thank you very much


; -----------------------------------------------------------------
;	If Exec says there is a 68881 present, reset it
;
;	a6 = ExecBase

	mc68881				; allow ffp intructions

ffp_reset:
	btst.b	#AFB_68881,AttnFlags(a6)	; check for 68881 flag
	bne	1$			; branch if flag is set
	rts				; trundle on back, nuthin to do

1$:
	move.l	a5,-(sp)		; save a5
	lea	2$,a5			; load address of ffp reset routine
	LIBCALL	Supervisor		; go into supervisor state to do it
	move.l	(sp)+,a5		; restore a5
	rts				; all done

2$:
	clr.l	-(sp)			; push a zero onto the stack
	frestore (sp)+			; restore 68881 state (to zero)
	rte				; return and exit supervisor state



; -----------------------------------------------------------------
; -----------------------------------------------------------------



; -----------------------------------------------------------------
;	Here begins the system interface commands.  When the user
;	calls OpenLibrary/CloseLibrary/RemLibrary, this
;	eventually get translated into a call to the following
;	routines (Open/Close/Expunge).  Exec has already put our
;	library pointer in a6 for us.  Exec has turned off task
;	switching while in these routines (via Forbid/Permit), so
;	we should not take too long in them.
; -----------------------------------------------------------------

; -----------------------------------------------------------------
;	Open - Called when a user calls OpenLibrary
;
;	Open returns the library pointer in d0 if the open was
;	successful.  If the open failed, then null is returned.
;	It might fail if we allocated memory on each open, or
;	if only one application could have the library open
;	at a time.
;
;	a6 = libptr, d0 = version

Open:
	addq.w	#1,LIB_OPENCNT(a6)		; one more opener
	bclr	#LIBB_DELEXP,lb_Flags(a6)	; prevent delayed expunges

	move.l	a6,d0				; return lib ptr
	rts


; -----------------------------------------------------------------
;	Close - Called when a user calls CloseLibrary
;
;	There are two different things that might be returned from
;	the close routine.  If the library is no longer open by anyone,
;	and there is a delayed expunge, then close should return the
;	segment list (as given to init).  Otherwise close should return
;	a null;
;
;	a6 = libptr

Close:
	clr.l	d0				; set the return value

	subq.w	#1,LIB_OPENCNT(a6)		; one fewer openers
	bne.s	1$				; branch if count non-zero

	btst	#LIBB_DELEXP,lb_Flags(a6)	; delayed expunge pending?
	beq.s	1$				; branch if no

	bsr	Expunge				; turn out the lights

1$:
	rts


; -----------------------------------------------------------------
;	Expunge - Called when a user calls RemLibrary
;
;	There are two different things that might tbe returned from the
;	expunge routine.  If the library is no longer open, then expunge
;	should return the segment list (as given to init).  Otherwise
;	expunge should set the delayed expunge flag and return null.
;
;	One other important note: because expunge is called from the
;	memory allocator, it may NEVER Wait() nor otherwise take a
;	long time to complete.
;
;	a6 = libptr

Expunge:
	tst.w	LIB_OPENCNT(a6)			; is lib open count == zero?
	beq	1$				; branch if yes

	bset	#LIBB_DELEXP,lb_Flags(a6)	; still open, set delayed flag
	clr.l	d0				; set return code = zero
	rts

1$:
	; We're leaving now, no longer open by anyone
	movem.l	d2/a4-a6,-(sp)			; save regs

	bsr	_geta4				; setup a4 for the C code
	move.l	a6,a5				; move our own pointer to a5

	move.l	a6,-(sp)			; call external C cleanup
	jsr	_LibExitHook			;  routine, pass lib ptr
	addq.l	#4,sp				; pop arg

	move.l	_SysBase,a6			; put ExecBase in a6

	bsr	ffp_reset			; reset the '881

	move.l	a5,a1				; lib ptr in a1
	LIBCALL	Remove				; unlink us from lib list

	move.l	_DOSBase,a1			; close DOS lib
	LIBCALL	CloseLibrary

	clr.l	d0				; clear all of d0
	move.l	a5,a1				; copy our lib ptr to a1	
	move.w	LIB_NEGSIZE(a5),d0		; put neg size in d0
	sub.l	d0,a1				; bias libbase down by negsize
	add.w	LIB_POSSIZE(a5),d0		; add posssize to size in d0
	LIBCALL	FreeMem				; call Exec to free lib node

	move.l	SegList,d0			; set return code (seg list)

	movem.l	(sp)+,d2/a4-a6			; restore regs
	rts					; "I'm goin' home" -Alvin Lee


; -----------------------------------------------------------------
;	Null - Doesn't do much of anything

Null:
	clr.l	d0				; zero return code
	rts

; -----------------------------------------------------------------
; -----------------------------------------------------------------

; -----------------------------------------------------------------
;	_geta4 - Called by Manx small data model code to setup a4

_geta4:
	far	data

	lea	__H1_org+32766,a4	; center of init'ed data space
	rts

; -----------------------------------------------------------------
; -----------------------------------------------------------------

	end
; -----------------------------------------------------------------
@//E*O*F library.template//
chmod u=rw,g=r,o=r library.template
 
echo x - stubs.template
sed 's/^@//' > "stubs.template" <<'@//E*O*F stubs.template//'

; -----------------------------------------------------------------
-	Genlib - stubs.template
- -----------------------------------------------------------------
-
-	Template for Amiga link library interface stubs
-
+SCOMMENT
;
+AGENTIME
;
;	This file automatically generated by genlib
;	Genlib is Copyright 1989, Sleepless Software
;	Author: Ron Hitchens (ronbo@vixen.uucp) (801) 292-2910
-
-	This code is linked with an application program
-	that wishes to make use of the matching run-time
-	library.  Defined here are the symbols for all
-	corresponding exported funtions in the run-time library.
-	When one of these funtions is called, control passes
-	to one of the stubs here, some massaging of registers
-	and the stack is done, then control jumps through
-	the proper vector into the run-time library.  When
-	control returns, the stack and registers are restored
-	and control returns to the orignal caller.
-
-	The output generated from this template should be assembled
-	to produce a .o file.  That .o file should either be put
-	into a link-library (with lb under Manx) or included on
-	the link line with the application .o files when using ln.
-
- -----------------------------------------------------------------
-
-	Copyright 1989, Sleepless Software, All Rights Reserved
-
-	Permission is granted to use and redistribute, so long as
-	no fee is charged.
-
-	Author: Ron Hitchens, Sleepless Software
-	Date: Wed Sep  6 16:13:13 MDT 1989
; -----------------------------------------------------------------



	include 'exec/types.i'
	include 'exec/libraries.i'

+AINCLUDE

	LIBINIT

	LIBDEF	_LVO_geta4
+LVO


	cseg		; define code


+BASE


;	User Defined Library Functions
+CDEFS


; -----------------------------------------------------------------
;	Link library stubs to access the run-time library routines

+STUBS

; -----------------------------------------------------------------

	end
@//E*O*F stubs.template//
chmod u=rw,g=r,o=r stubs.template
 
echo x - main.c
sed 's/^@//' > "main.c" <<'@//E*O*F main.c//'

/*
 *	Genlib - Do the grunt work to make an Amiga run-time library
 *		from some C code
 *
 *	Copyright 1989, Sleepless Software, All Rights Reserved
 *
 *	Permission is granted to use and to redistribute,
 *	provided no fee is charged for this software.
 *
 *	Author: Ron Hitchens, Sleepless Software (ronbo@vixen.uucp)
 *	Date: Sat Oct 14 15:16:37 MDT 1989
 *
 */


#include "genlib.h"



static Global			*create_global ();


#define LIB_TEMPL		0x01
#define STUB_TEMPL		0x02
#define DOTH_TEMPL		0x04
#define DOTI_TEMPL		0x08
#define ALL_TEMPLS		0x0f


typedef struct {
	int		flag;
	char		*templ_name;
	char		*suffix;
} Templ_list;

static Templ_list	template_list [] = {
	{ LIB_TEMPL,	LIB_TEMPLATE,	"_library.asm"	},
	{ STUB_TEMPL,	STUB_TEMPLATE,	"_stubs.asm"	},
	{ DOTH_TEMPL,	CHDR_TEMPLATE,	"base.h"	},
	{ DOTI_TEMPL,	AHDR_TEMPLATE,	"base.i"	}
};

#define NUM_TEMPLATES	(sizeof (template_list) / sizeof (Templ_list))



main (argc, argv)
	int		argc;
	char		**argv;
{
	Global		*global;
	char		*libname = NULL;
	char		*specfile = NULL;
	char		*tdir = TEMPLATE_DIR;
	int		template_flags = 0;
	int		errflag = FALSE;
	int		i, c;
	extern int	optind;
	extern char	*optarg;


	if (argc == 0) {
		fprintf (stderr, "can't run from workbench\n");
		exit (1);
	}

	while ((c = getopt (argc, argv, "g:d:lshia")) != EOF) {
		if (c == NULL) {		/* getopt in edlib is broke */
			libname = optarg;
			break;
		}

		switch (c) {
		case 'g':			/* set specfile name */
			specfile = optarg;
			break;

		case 'd':			/* set template directory */
			tdir = optarg;
			break;

		case 'l':			/* do library base */
			template_flags |= LIB_TEMPL;
			break;

		case 's':			/* do link stubs */
			template_flags |= STUB_TEMPL;
			break;

		case 'h':			/* do C .h file */
			template_flags |= DOTH_TEMPL;
			break;

		case 'i':			/* do asm .i file */
			template_flags |= DOTI_TEMPL;
			break;

		case 'a':			/* do all of the above */
			template_flags |= ALL_TEMPLS;
			break;

		case '?':
			errflag = TRUE;
			break;
		}
	}

	if (template_flags == 0) {
		errflag = TRUE;		/* nuthin to do */
	}

	if (libname == NULL) {
		if (optind == (argc - 1)) {
			libname = argv [optind];	/* library name */
		} else {
			errflag = TRUE;			/* it's missing */
		}
	}

	if (errflag == TRUE) {
		fprintf (stderr,
			"usage: %s -lshia [-g genfile] [-d dir] libname\n",
			argv [0]);
		exit (2);
	}


	if ((global = create_global (libname)) == (Global *)0) {;
		exit (3);
	}

	/* load the specification file */
	if (load_spec_file (global, specfile) != OK) {
		exit (4);
	}

	/* do each of the specified templates */
	for (i = 0; i < NUM_TEMPLATES; i++) {
		if ((template_list [i].flag & template_flags) != 0) {
			do_template (global, tdir,
				template_list [i].templ_name,
				template_list [i].suffix);
		}
	}

	exit (0);			/* all in a days work */
}


/*
 *	Allocate, clear and return an instance of a global data structure.
 */

static
Global *
create_global (name)
	char	*name;
{
	Global	*global;
	char	*p;

	global = (Global *) malloc (sizeof (Global));
	if (global == NULL_GLOBAL) {
		fprintf (stderr, "can't alloc any memory\n");
		return (NULL_GLOBAL);
	}

	bzero (global, sizeof (Global));

	global->lib_name = name;
	global->lib_name_upper = strdup (name);

	for (p = global->lib_name_upper; *p != '\0'; p++) {
		if ((*p >= 'a') && (*p <= 'z')) {
			*p -= 'a' - 'A';
		}
	}

	return (global);
}

	
@//E*O*F main.c//
chmod u=rw,g=r,o=r main.c
 
echo x - load.c
sed 's/^@//' > "load.c" <<'@//E*O*F load.c//'

/*
 *	GenLib - Routines to load the library specification file
 *
 *	Copyright 1989, Sleepless Software, All Rights Reserved
 *
 *	Permission is granted to use and to redistribute,
 *	provided no fee is charged for this software.
 *
 *	Author: Ron Hitchens, Sleepless Software (ronbo@vixen.uucp)
 *	Date: Sat Oct 14 15:16:37 MDT 1989
 *
 */


#include "genlib.h"

#include <time.h>



static Parse_code	spec_codes [] = {
	{ KW_EQU,		"EQU"		},
	{ KW_EQU,		"EQUALS"	},
	{ KW_EQU,		"EQUAL"		},
	{ KW_FUNC,		"FUNC"		},
	{ KW_FUNC,		"FUNCTION"	},
	{ KW_FUNC,		"PROC"		},
	{ KW_FUNC,		"PROCEDURE"	},
	{ KW_BASE,		"BASE"		},
	{ KW_BASE,		"LIBBASE"	},
	{ KW_BASE,		"BASENAME"	},
	{ KW_LIBNAME,		"LIBNAME"	},
	{ KW_LIBID,		"LIBID"		},
	{ PARSE_UNKNOWN,	(char *)0	}
};


static void			add_equ (), add_func ();

static char			*make_id_string ();



/*
 *	Load the specification file for the library, which contains
 *	various equates, and the list of functions which will be visible
 *	externally.
 */

int
load_spec_file (global, specfile)
	Global	*global;
	char	*specfile;
{
	FILE	*fp;
	char	buf [128];

	if (specfile == NULL) {
		sprintf (buf, "%s%s", global->lib_name, SPECFILE_SUFFIX);
		specfile = buf;
	}

	if ((fp = fopen (specfile, "r")) == (FILE *)0) {
		fprintf (stderr, "can't open %s for reading\n", specfile);
		return (BAD);
	}

	while (fgets (buf, sizeof (buf), fp) != NULL) {
		int	num_args;
		char	*ptrs [MAX_ARGS];

		if (feof (fp) != FALSE) {
			break;
		}

		/* ignore comment and blank lines */
		if ((buf [0] == '#') || (buf [0] == '\n')) {
			continue;
		}

		/* break the buffer into tokens */
		num_args = scan_buf (buf, ptrs, MAX_ARGS);

		if (num_args == 0) {
			continue;	/* doesn't seem to be much here */
		}

		switch (string_to_code (spec_codes, ptrs [0])) {
		case KW_EQU:
			add_equ (global, ptrs, num_args);
			break;

		case KW_FUNC:
			add_func (global, ptrs, num_args);
			break;

		case KW_BASE:
			global->lib_base = strdup (ptrs [1]);
			break;

		case KW_LIBNAME:
			global->lib_dotname = strdup (ptrs [1]);
			break;

		case KW_LIBID:
			global->lib_id = strdup (ptrs [1]);
			break;

		default:
			fprintf (stderr, "unrecognized keyword: %s\n", buf);
			break;
		}
	}

	/* if name of LibraryBase struct wasn't defined, intuit it */
	if (global->lib_base == (char *)0) {
		sprintf (buf, "%sBase", global->lib_name);
		/* capitalize first letter of library name */
		if ((buf [0] >= 'a') && (buf [0] <= 'z')) {
			buf [0] -= 'a' - 'A';
		}
		global->lib_base = strdup (buf);	/* save it */
	}

	/* if filename of final library wasn't defined, make default */
	if (global->lib_dotname == (char *)0) {
		sprintf (buf, "%s.library", global->lib_name);
		global->lib_dotname = strdup (buf);
	}

	/* if library ID string wasn't defined, make a standard one */
	if (global->lib_id == (char *)0) {
		global->lib_id = make_id_string (global);
	}

	fclose (fp);

	return (OK);
}


/*
 *	Add an equate to the list of equates.  This can be injected
 *	one at a time, or en masse into a output file.
 */

static
void
add_equ (global, ptrs, num_ptrs)
	Global		*global;
	char		**ptrs;
	int		num_ptrs;
{
	Equ_node	*node;

	if (num_ptrs < 3) {
		fprintf (stderr, "add_equ: too few args\n");
		return;
	}

	node = (Equ_node *) malloc (sizeof (Equ_node));
	if (node == NULL_EQU) {
		fprintf (stderr, "can't alloc equ node\n");
		exit (4);
	}

	node->next = NULL_EQU;
	node->name = strdup (ptrs [1]);
	node->value = strdup (ptrs [2]);

	/* chain the EQU node onto the end of the list */
	if (global->equ_first == NULL_EQU) {
		global->equ_first = global->equ_last = node;
	} else {
		global->equ_last->next = node;
		global->equ_last = node;
	}	
}


/*
 *	Add a function declaration to the list of functions.  These are
 *	used in several places to build tables and to generate glue
 *	code for the link library stubs.  The number of arguments that
 *	the function takes needs to be known so that the proper glue
 *	code can be generated in the link library for stack munging.
 */

static
void
add_func (global, ptrs, num_ptrs)
	Global		*global;
	char		**ptrs;
	int		num_ptrs;
{
	Func_node	*node;

	if (num_ptrs < 2) {
		fprintf (stderr, "add_func: too few args\n");
		return;
	}

	node = (Func_node *) malloc (sizeof (Func_node));
	if (node == NULL_FUNC) {
		fprintf (stderr, "can't alloc func node\n");
		exit (4);
	}

	node->next = NULL_FUNC;
	node->name = strdup (ptrs [1]);
	node->num_args = num_ptrs - 2;

	if (global->func_first == NULL_FUNC) {
		global->func_first = global->func_last = node;
	} else {
		global->func_last->next = node;
		global->func_last = node;
	}	
}


/*
 *	Make a library identifier string in the standard format (according
 *	to the Amiga RKM).  This string identifies the library name,
 *	version and creation date in asci string form.  The actual
 *	version information, which is checked against an OpenLibrary
 *	reguest, is defined elsewhere.
 */

static
char *
make_id_string (global)
	Global		*global;
{
	struct tm	*tm;
	long		clock;
	char		*vers, *rev;
	char		buf [64];
	static char	*month_names [] = { "Jan", "Feb", "Mar", "Apr", "May",
			    "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };

	clock = time (0L);
	tm = localtime (&clock);

	vers = equ_value (global, "VERSION");
	if (vers == (char *)0) {
		vers = "xx";
	}

	rev = equ_value (global, "REVISION");
	if (rev == (char *)0) {
		rev = "yy";
	}

	sprintf (buf, "%slib %s.%s (%d %s 19%d)", global->lib_name,
		vers, rev, tm->tm_mday, month_names [tm->tm_mon], tm->tm_year);

	return (strdup (buf));
}


@//E*O*F load.c//
chmod u=rw,g=r,o=r load.c
 
echo x - dump.c
sed 's/^@//' > "dump.c" <<'@//E*O*F dump.c//'

/*
 *	Genlib - Routines to read a template and produce the
 *		corresponding output file, substituting the magic
 *		keywords with the information from the spec file.
 *
 *	Copyright 1989, Sleepless Software, All Rights Reserved
 *
 *	Permission is granted to use and to redistribute,
 *	provided no fee is charged for this software.
 *
 *	Author: Ron Hitchens, Sleepless Software (ronbo@vixen.uucp)
 *	Date: Sat Oct 14 15:16:37 MDT 1989
 *
 */


#include "genlib.h"

#include <time.h>



static Parse_code	sub_codes [] = {
	{ KW_FUNCTABLE,		"FUNCTABLE"	},
	{ KW_FUNCTABLE,		"FUNCTAB"	},
	{ KW_FUNCTABLE,		"FUNCTIONTABLE"	},
	{ KW_FUNCTABLE,		"FUNCTIONTAB"	},
	{ KW_XDEFS,		"XDEF"		},
	{ KW_XDEFS,		"XDEFS"		},
	{ KW_CDEFS,		"CDEF"		},
	{ KW_CDEFS,		"CDEFS"		},
	{ KW_LVO,		"LVO"		},
	{ KW_LVO,		"LVOS"		},
	{ KW_STUB,		"STUB"		},
	{ KW_STUB,		"STUBS"		},
	{ KW_BASE,		"BASE"		},
	{ KW_BASE,		"LIBBASE"	},
	{ KW_AINCLUDE,		"AINCLUDE"	},
	{ KW_LIBNAME,		"LIBNAME"	},
	{ KW_LIBID,		"LIBID"		},
	{ KW_EQU,		"EQU"		},
	{ KW_EQU,		"EQUS"		},
	{ KW_AIFND,		"AIFND"		},
	{ KW_ASTRUCT,		"ASTRUCT"	},
	{ KW_ASIZEOF,		"ASIZEOF"	},
	{ KW_ALIBNAME,		"ALIBNAME"	},
	{ KW_AENDC,		"AENDC"		},
	{ KW_LSIZEOF,		"LSIZEOF"	},
	{ KW_CIFDEF,		"CIFDEF"	},
	{ KW_CSTRUCT,		"CSTRUCT"	},
	{ KW_CLIBNAME,		"CLIBNAME"	},
	{ KW_CENDIF,		"CENDIF"	},
	{ KW_ACOMMENT,		"ACOMMENT"	},
	{ KW_CCOMMENT,		"CCOMMENT"	},
	{ KW_LCOMMENT,		"LCOMMENT"	},
	{ KW_SCOMMENT,		"SCOMMENT"	},
	{ KW_AGENTIME,		"AGENTIME"	},
	{ KW_CGENTIME,		"CGENTIME"	},
	{ PARSE_UNKNOWN,	(char *)0	}
};



static void			put_equ (),
				put_all_equ (),
				put_inclusion (),
				put_stubs (),
				time_stamp ();



/*
 *	Read a template and make an output file with the proper
 *	information substituted in place of the special keywords
 *	in the template.
 */

void
do_template (global, dir, template_name, out_suffix)
	Global	*global;
	char	*dir, *template_name, *out_suffix;
{
	FILE	*in_fp, *out_fp;
	char	buf [128];

	sprintf (buf, "%s%s", dir, template_name);

	if ((in_fp = fopen (buf, "r")) == (FILE *)0) {
		fprintf (stderr, "can't open %s for reading\n", buf);
		return;
	}

	sprintf (buf, "%s%s", global->lib_name, out_suffix);

	if ((out_fp = fopen (buf, "w")) == (FILE *)0) {
		fprintf (stderr, "can't open %s for writing\n", buf);
		fclose (in_fp);
		return;
	}

	while (fgets (buf, sizeof (buf), in_fp) != NULL) {
		char	*p;

		if (feof (in_fp) != FALSE) {
			break;
		}

		switch (buf [0]) {
		case '=':
			scan_buf (&buf [1], &p, 1);
			put_equ (global, out_fp, p);
			break;

		case '+':
			scan_buf (&buf [1], &p, 1);
			put_inclusion (global, out_fp, p);
			break;

		case '-':
			break;

		default:
			fputs (buf, out_fp);
			break;
		}
	}

	fclose (in_fp);
	fclose (out_fp);
}


/*
 *	Put out the value of one EQU symbol.
 */

static
void
put_equ (global, fp, name)
	Global		*global;
	FILE		*fp;
	char		*name;
{
	char		*value = equ_value (global, name);

	if (value == (char *)0) {
		fprintf (fp, ";\t***** '%s' IS UNDEFINED *******\n", name);
	} else {
		fprintf (fp, "%s:\tEQU\t%s\n", name, value);
	}
}


/*
 *	Put out all of the EQU symbols in the list.
 */

static
void
put_all_equ (global, fp)
	Global		*global;
	FILE		*fp;
{
	Equ_node	*first = global->equ_first;
	Equ_node	*node;
	char		*name, *value;

	for (node = first; node != NULL_EQU; node = node->next) {
		fprintf (fp, "%s:\tEQU\t%s\n", node->name, node->value);
	}
}


/*
 *	Insert an inclusion into the output file.  The type and
 *	format of the included text is dependent on the type of
 *	inclusion.
 */

static
void
put_inclusion (global, fp, name)
	Global		*global;
	FILE		*fp;
	char		*name;
{
	Func_node	*first = global->func_first;
	Func_node	*node;

	switch (string_to_code (sub_codes, name)) {
	case KW_EQU:
		put_all_equ (global, fp);
		break;

	case KW_FUNCTABLE:
		for (node = first; node != NULL_FUNC; node = node->next) {
			fprintf (fp, "\tdc.l\t_%s\t; %d parms\n", node->name,
				node->num_args);
		}
		break;

	case KW_XDEFS:
		for (node = first; node != NULL_FUNC; node = node->next) {
			fprintf (fp, "\tpublic\t%s\n", node->name);
		}
		break;

	case KW_CDEFS:
		for (node = first; node != NULL_FUNC; node = node->next) {
			fprintf (fp, "\tpublic\t_%s\n", node->name);
		}
		break;

	case KW_LVO:
		for (node = first; node != NULL_FUNC; node = node->next) {
			fprintf (fp, "\tLIBDEF\t_LVO%s\n", node->name);
		}
		break;

	case KW_STUB:
		put_stubs (global, fp);
		break;

	case KW_BASE:
		fprintf (fp, "\tpublic\t_%s\n", global->lib_base);
		break;

	case KW_AINCLUDE:
		fprintf (fp, "\tinclude\t'%sbase.i'\n", global->lib_name);
		break;

	case KW_LIBNAME:
		fprintf (fp, "\t%sNAME\t; from %sbase.i\n",
			global->lib_name_upper, global->lib_name);
		break;

	case KW_LIBID:
		fprintf (fp, "\tdc.b\t\t'%s',13,10,0\n", global->lib_id);
		break;

	case KW_AIFND:
		fprintf (fp, "\tIFND\t%s_BASE_I\n%s_BASE_I\tSET\t1\n",
			global->lib_name_upper, global->lib_name_upper);
		break;

	case KW_ACOMMENT:
		fprintf (fp, ";\t%s definitions for %s\n", global->lib_base,
			global->lib_dotname);
		break;

	case KW_ASTRUCT:
		fprintf (fp, "\tSTRUCTURE\t%s,LIB_SIZE\n", global->lib_base);
		break;

	case KW_ASIZEOF:
		fprintf (fp, "\tLABEL\t\t%s_SIZEOF\n", global->lib_base);
		break;

	case KW_ALIBNAME:
		fprintf (fp, "%sNAME\tMACRO\n\tdc.b\t'%s',0\n\tENDM\n",
			global->lib_name_upper, global->lib_dotname);
		break;

	case KW_AENDC:
		fprintf (fp, "\tENDC\t; %s_BASE_I\n", global->lib_name_upper);
		break;

	case KW_LSIZEOF:
		fprintf (fp, "\tdc.l\t%s_SIZEOF\t; size of lib node to alloc\n",
			global->lib_base);
		break;

	case KW_CIFDEF:
		fprintf (fp, "#ifndef %s_BASE_H\n#define %s_BASE_H\n",
			global->lib_name_upper, global->lib_name_upper);
		break;

	case KW_CCOMMENT:
		fprintf (fp, " *\t%s definitions for %s\n", global->lib_base,
			global->lib_dotname);
		break;

	case KW_CSTRUCT:
		fprintf (fp, "struct %s {\n", global->lib_base);
		break;

	case KW_CLIBNAME:
		fprintf (fp, "#define %sNAME\t\t\"%s\"\n",
			global->lib_name_upper, global->lib_dotname);
		break;

	case KW_CENDIF:
		fprintf (fp, "#endif %s_BASE_H", global->lib_name_upper);
		break;

	case KW_LCOMMENT:
		fprintf (fp, ";\t%s Run-Time Library BoilerPlate\n",
			global->lib_dotname);
		break;

	case KW_SCOMMENT:
		fprintf (fp, ";\t%s Link-Library Interface Glue Routines\n",
			global->lib_dotname);
		break;

	case KW_AGENTIME:
		time_stamp (fp, ";\tGenerated: ");
		break;

	case KW_CGENTIME:
		time_stamp (fp, " *\tGenerated: ");
		break;

	default:
		fprintf (fp, ";\t**** '+%s' NOT RECOGNIZED *******\n", name);
		break;
	}
}


/*
 *	Put out the link library stub code for all the functions
 *	in the list.  The exact code generated depends on how many
 *	arguments are passed to the function.  When the link library
 *	glue calls off to the run-time routine, it saves registers,
 *	sets up registers as needed by the lib code, then copies up
 *	the arguments on the stack.  When the run-time library returns,
 *	the duplicate args are popped, registers are restored, and
 *	control returns to the original caller.
 *	NB: Be very sure you know what you're doing if you change this
 *	code, especially if you change the number of registers that
 *	are saved before call-thru.  The generated code uses a constant
 *	offset of the stack pointer when copying the args.  If things
 *	don't match up properly, the args will get hosed up royally.
 *	Also note that this code depends on a field in the library
 *	base struct with the name lb_LibA4, which has been set to
 *	the proper value by the library init code.  This is done
 *	correctly by the regular template.  The a4 register must be
 *	setup even if you compile everything with the large model under
 *	Manx.  
 */

static
void
put_stubs (global, fp)
	Global		*global;
	FILE		*fp;
{
	Func_node	*first = global->func_first;
	Func_node	*node;
	int		i;

	for (node = first; node != NULL_FUNC; node = node->next) {
		fprintf (fp, "\n_%s:\n", node->name);
		fprintf (fp, "\tmovem.l\td2-d7/a2-a6,-(sp)\t; save regs\n");
		for (i = 0; i < node->num_args; i++) {
			fprintf (fp,
				"\tmove.l\t%d(sp),-(sp)\t\t; copy arg %d\n",
				((node->num_args - 1) * 4) + 48,
				node->num_args - i);
		}
		fprintf (fp, "\tmove.l\t_%s,a6\t\t; libbase\n",
			global->lib_base);
		fprintf (fp, "\tmove.l\tlb_LibA4(a6),a4\t; setup lib's a4\n");
		fprintf (fp, "\tjsr\t_LVO%s(a6)\t; do it\n", node->name);
		if (node->num_args != 0) {
			fprintf (fp, "\tadd%c.l\t#%d,sp\t\t\t; pop dup args\n",
				(node->num_args <= 2) ? 'q' : 'a',
				node->num_args * 4);	
		}
		fprintf (fp, "\tmovem.l\t(sp)+,d2-d7/a2-a6\t; restore regs\n");
		fprintf (fp, "\trts\t\t\t\t; return to caller\n");
	}
}


/*
 *	Given the name of an EQU symbol, look it up in the list and
 *	return its value.
 */

char *
equ_value (global, name)
	Global		*global;
	char		*name;
{
	Equ_node	*first = global->equ_first;
	Equ_node	*node;

	for (node = first; node != NULL_EQU; node = node->next) {
		if (strcmp (name, node->name) == 0) {
			return (node->value);
		}
	}

	return ((char *)0);
}


/*
 *	Output a date/time stamp string, prepended by the given string
 *	to the given stdio stream.
 */

static
void
time_stamp (fp, p)
	FILE		*fp;
	char		*p;
{
	long		clock = time ((long *)0);
	struct tm	*tm;

	tm = localtime (&clock);

	fprintf (fp, "%s%s", p, asctime (tm));
}

	
@//E*O*F dump.c//
chmod u=rw,g=r,o=r dump.c
 
echo x - parse.c
sed 's/^@//' > "parse.c" <<'@//E*O*F parse.c//'

/*
 *	Genlib - Routines for parsing template files
 *
 *	Copyright 1989, Sleepless Software, All Rights Reserved
 *
 *	Permission is granted to use and to redistribute,
 *	provided no fee is charged for this software.
 *
 *	Author: Ron Hitchens, Sleepless Software (ronbo@vixen.uucp)
 *	Date: Sat Oct 14 15:16:37 MDT 1989
 *
 */


#include "genlib.h"

#include <ctype.h>



static char		*next_token (),
			*end_of_token ();


/*
 *	Given a pointer to a table of attribute/value pairs and an
 *	attribute, return the value corresponding to the attribute.
 */ 

int
string_to_code (code_tab, attrib)
	Parse_code	*code_tab;
	char		*attrib;
{
	int		i = 0;

	while (code_tab [i].code != PARSE_UNKNOWN) {
		if (stricmp (attrib, code_tab [i].value) == 0) {
			return (code_tab [i].code);
		}
		i++;
	}

	return (PARSE_UNKNOWN);
}


/*
 *	Scan a text buffer and stringify each token in the buffer.  Place
 *	pointers to each of the tokens into the array of pointers pointed
 *	to by ptrs, up to a maximum of num_ptrs.  Return the number of
 *	tokens found.
 *	(NB: This is in no way a generic token parser, it's destructive
 *	 of the original buffer and can be fooled by lots of things.  It's
 *	 simply a hack that served the purpose at the time)
 */

int
scan_buf (buf, ptrs, num_ptrs)
	char		*buf;
	register char	**ptrs;
	register int	num_ptrs;
{
	register char	*q, *p = buf;
	register int	n = 0;

	while (TRUE) {
		q = next_token (p);
		if (q == (char *)0) {
			return (n);
		}
		ptrs [n++] = q;
		p = end_of_token (q);
		if (p == (char *)0) {
			return (n);
		}
		*p++ = '\0';
		if (n == num_ptrs) {
			return (n);
		}
		ptrs [n] = (char *)0;
	}
}


/*
 *	Given a pointer to a string, if it is bounded by matching quote
 *	characters (two single or two double quotes), remove them from
 *	the string.  The end quote is replaced with a null, and the address
 *	of the char following the first quote is returned as the result.
 *	If the string does not seem to be quoted, or if the quotes are
 *	not balanced, then the original string is returned.
 */

char *
snuff_quotes (p)
	register char	*p;
{
	register char	*q;

	if ((*p != '"') && (*p != '\'')) {
		return (p);
	}

	q = &p [strlen (p) - 1];

	if (*q != *p) {
		return (p);
	}

	*q = '\0';
	p++;

	return (p);
}	


/*
 *	Given a pointer into a string, return the address of the beginning
 *	of the next token in the string.
 */

static
char *
next_token (p)
	char	*p;
{
	while (*p != '\0') {
		if (isspace(*p)) {
			p++;
			continue;
		}
		switch (*p) {
		case '(':
		case ')':
		case ',':
			p++;
			continue;
		}
		break;
	}

	if (*p == '\0') {
		return ((char *)0);
	}

	return (p);
}


/*
 *	Given a pointer to the beginning of a token in a buffer, find
 *	the end of the token and return the address of the character
 *	following the last character of the token.  This function will
 *	recognize quoted strings beginning with either ' or ", and ending
 *	with the same quote char, it will also handle escaped quotes
 *	within the string, such as 'It\'s too late' or "Casper says \"Boo!\""
 */

static
char *
end_of_token (p)
	char	*p;
{
	char	quote = FALSE;
	int	escaped = FALSE;

	while (*p != '\0') {
		if (quote != FALSE) {
			if ((*p == quote) && (escaped == FALSE)) {
				quote = FALSE;
			}
		} else {
			if (isspace (*p)) {
				return (p);
			}
			switch (*p) {
			case ',':
			case '=':
			case ')':
			case '(':
			case '\n':
				return (p);
			case '"':
			case '\'':
				if (escaped == FALSE) {
					quote = *p;
				}
				break;
			}
		}
		escaped = ((*p == '\\') && (escaped == FALSE)) ? TRUE : FALSE;
		p++;
	}
						
	return ((char *)0);
}


/*
 *	Duplicate the given string and return the address of the copy.
 */

char *
strdup (str)
	char	*str;
{
	char	*p;

	p = malloc (strlen (str) + 1);
	strcpy (p, str);

	return (p);
}


/*
 *	Work around brain-damaged tolower macro that unconditionally
 *	subtracts a constant from the char without first checking to
 *	see if it is upper case.
 */

#undef tolower

static
char
tolower (c)
	register char	c;
{
	return ((isupper (c)) ? ((c)-'A'+'a') : c);
}


int
stricmp (str1, str2)
	register char	*str1, *str2;
{
	register int	index = 0;

	while (str1[index] && str2[index] &&
	    tolower(str1[index]) == tolower(str2[index])) {
		++index;
	}

	return ((tolower(str1[index]) < tolower(str2[index])) ? -1 :
		((tolower(str1[index]) > tolower(str2[index])) ?  1 : 0) );
}

@//E*O*F parse.c//
chmod u=rw,g=r,o=r parse.c
 
echo x - getopt.c
sed 's/^@//' > "getopt.c" <<'@//E*O*F getopt.c//'
/*
 * edlib v1.1 Copyright 1989 Edwin Hoogerbeets
 * This code is freely redistributable as long as no charge other than
 * reasonable copying fees are levied for it.
 */

#define NULL    0
#define EOF     (-1)
#define ERR(s, c)       if(opterr){\
        extern int strlen(), write();\
        char errbuf[2];\
        errbuf[0] = c; errbuf[1] = '\n';\
        (void) write(2, argv[0], (unsigned)strlen(argv[0]));\
        (void) write(2, s, (unsigned)strlen(s));\
        (void) write(2, errbuf, 2);}

extern int strcmp();
extern char *strchr();

int     opterr = 1;
int     optind = 1;
int     optopt;
char    *optarg;

int
getopt(argc, argv, opts)
register int     argc;
char    **argv, *opts;
{
        static int sp = 1;
        register int c;
        register char *cp;

        if(sp == 1)
                if(optind >= argc)
                        return(EOF);
                else {
                    if(argv[optind][0] != '-') {
                        optarg = argv[optind++];
                        return(NULL);
                    }
                    if(strcmp(argv[optind], "--") == NULL) {
                        optind++;
                        return(EOF);
                    }
                }
        optopt = c = argv[optind][sp];
        if(c == ':' || (cp=strchr(opts, c)) == NULL) {
                ERR(": illegal option -- ", c);
                if(argv[optind][++sp] == '\0') {
                        optind++;
                        sp = 1;
                }
                return('?');
        }
        if(*++cp == ':') {
                if(argv[optind][sp+1] != '\0')
                        optarg = &argv[optind++][sp+1];
                else if(++optind >= argc) {
                        ERR(": option requires an argument -- ", c);
                        sp = 1;
                        return('?');
                } else
                        optarg = argv[optind++];
                sp = 1;
        } else {
                if(argv[optind][++sp] == '\0') {
                        sp = 1;
                        optind++;
                }
                optarg = NULL;
        }
        return(c);
}
@//E*O*F getopt.c//
chmod u=rw,g=r,o=r getopt.c
 
echo x - genlib.h
sed 's/^@//' > "genlib.h" <<'@//E*O*F genlib.h//'

/*
 *	Genlib - Do the grunt work to make an Amiga run-time library
 *		from some C code.
 *
 *	Copyright 1989, Sleepless Software, All Rights Reserved
 *
 *	Permission is granted to use and to redistribute,
 *	provided no fee is charged for this software.
 *
 *	Author: Ron Hitchens, Sleepless Software (ronbo@vixen.uucp)
 *	Date: Sat Oct 14 15:16:37 MDT 1989
 *
 */


#include <stdio.h>


#ifndef FALSE
#define TRUE		1
#define FALSE		0
#endif


#define OK		0
#define BAD		-1


/*
 *	Define the directory where templates can be found.  Note, this
 *	string is prepended as-is to the simple filename of the template,
 *	so slashes/colons must be present on the end as appropriate.
 */
#ifdef unix
#define TEMPLATE_DIR	"/usr/local/lib/genlib/"
#else
#define TEMPLATE_DIR	"genlib:"
#endif

#define LIB_TEMPLATE	"library.template"
#define STUB_TEMPLATE	"stubs.template"
#define CHDR_TEMPLATE	"chdr.template"
#define AHDR_TEMPLATE	"asmhdr.template"

#define SPECFILE_SUFFIX	".gen"

/*
 *	Define numeric equivalents of the recognizable keywords in
 *	the template files.
 */

#define PARSE_UNKNOWN	-1

#define KW_EQU		1
#define KW_FUNC		2
#define KW_FUNCTABLE	3
#define KW_XDEFS	4
#define KW_CDEFS	5
#define KW_LVO		6
#define KW_STUB		7
#define KW_BASE		8
#define KW_AINCLUDE	9
#define KW_LIBNAME	10
#define KW_LIBID	11
#define KW_AIFND	12
#define KW_ASTRUCT	13
#define KW_ASIZEOF	14
#define KW_ALIBNAME	15
#define KW_AENDC	16
#define KW_LSIZEOF	17
#define KW_CIFDEF	18
#define KW_CSTRUCT	19
#define KW_CLIBNAME	20
#define KW_CENDIF	21
#define KW_ACOMMENT	22
#define KW_CCOMMENT	23
#define KW_LCOMMENT	24
#define KW_SCOMMENT	25
#define KW_AGENTIME	26
#define KW_CGENTIME	27
#define KW_UNKNOWN	PARSE_UNKNOWN

#define MAX_ARGS	30		/* max parsable tokens per line */


typedef struct {
	int		code;
	char		*value;
} Parse_code;


typedef struct equ_s {
	struct equ_s	*next;		/* next EQU node in the list */
	char		*name;		/* the equate symbol name */
	char		*value;		/* the value of the equate */
} Equ_node;

#define NULL_EQU	(Equ_node *)0


typedef struct func_s {
	struct func_s	*next;		/* next FUNC node in the list */
	char		*name;		/* the function name */
	int		num_args;	/* the number of arguments it takes */
} Func_node;

#define NULL_FUNC	(Func_node *)0


typedef struct {
	Equ_node	*equ_first;	/* first EQU node in list */
	Equ_node	*equ_last;	/* last EQU node in list */
	Func_node	*func_first;	/* first FUNC node in list */
	Func_node	*func_last;	/* last FUNC node in list */
	char		*lib_name;	/* simple name of the library */
	char		*lib_name_upper;	/* simple name in upper case */
	char		*lib_base;	/* name of lib's "base" struct */
	char		*lib_dotname;	/* "libname.library"  */
	char		*lib_id;	/* the library indentifier string */
} Global;

#define NULL_GLOBAL	(Global *)0


#ifdef MCH_AMIGA
#define bzero(a,l)	setmem(a,l,0)
#endif

extern int		load_spec_file (),
			scan_buf ();

extern void		do_template ();

extern char		*snuff_quotes (),
			*equ_value (),
			*strdup (),
			*malloc ();
@//E*O*F genlib.h//
chmod u=rw,g=r,o=r genlib.h
 
echo x - makefile
sed 's/^@//' > "makefile" <<'@//E*O*F makefile//'

#	Genlib - makefile for the genlib program itself


PROG =		genlib

OFILES =	main.o load.o dump.o parse.o getopt.o
HFILES =	genlib.h

LIBS =		-lc32

CFLAGS =	+L



$(PROG):	$(OFILES)
		ln -o $(PROG) $(OFILES) $(LIBS)


$(OFILES):	$(HFILES)
@//E*O*F makefile//
chmod u=rw,g=r,o=r makefile
 
echo Inspecting for damage in transit...
temp=/tmp/shar$$; dtemp=/tmp/.shar$$
trap "rm -f $temp $dtemp; exit" 0 1 2 3 15
cat > $temp <<\!!!
      61     427    2665 README
      49     149    1111 MANIFEST
      34      80     814 asmhdr.template
      37      88     850 chdr.template
     439    1637   11178 library.template
      73     266    1912 stubs.template
     183     527    3374 main.c
     262     851    5517 load.c
     420    1276    9307 dump.c
     249     773    4389 parse.c
      74     215    2158 getopt.c
     138     451    2998 genlib.h
      20      37     241 makefile
    2039    6777   46514 total
!!!
wc  README MANIFEST asmhdr.template chdr.template library.template stubs.template main.c load.c dump.c parse.c getopt.c genlib.h makefile | sed 's=[^ ]*/==' | diff -b $temp - >$dtemp
if [ -s $dtemp ]
then echo "Ouch [diff of wc output]:" ; cat $dtemp
else echo "No problems found."
fi
exit 0