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

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

    This is part 2 of 2 of the Genlib posting.

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:41:04 MDT 1989
# Contents:  Doc/ Sample/ Doc/usage Doc/programmer.notes Doc/hacker.notes
#	Sample/makefile Sample/foo.gen Sample/init.c Sample/dialog.c
#	Sample/timer.c Sample/test.c
 
echo mkdir - Doc
mkdir Doc
chmod u=rwx,g=rx,o=rx Doc
 
echo x - Doc/usage
sed 's/^@//' > "Doc/usage" <<'@//E*O*F Doc/usage//'

	Genlib - How to Create a Run-Time Library with Genlib


   Genlib gets most of the information about the library to be built from
the specification file that it reads as input.  It also takes several
command line arguments, one of which defines the name of the library.


Command Line Syntax

   Genlib takes Unix-like command line arguments and switches (the author
is a unix hacker, and genlib was developed on a Sun and A2000 with ethernet).
Command line syntax is as follows:

	genlib -lshia [-g genfile] [-d templatedir] libname

   At least one of the switches l, s, h, i or a is required, as well as
the library name.  The g and d arguments are optional and are used to
override defaults.

	The -l switch indicates that the run-time library boilerplate file
	is to be produced.  This file defines the required interface
	routines which allow Exec to properly load the library and connect
	it to the system list of run-time libraries.  It also defines
	several tables that Exec needs to dynamically build a jump table
	into the library code.  This file contains the initial entry and
	cleanup routines, it basically takes the place of the startup
	code and main() when linked with the other code modules that
	make up the library.
	The name of this file will be <libname>_library.asm

	The -s switch indicates that the link-library interface stubs file
	is to be produced.  This file defines the glue routines which are
	linked with an application program that wishes to use the run-time
	library.  These glue routines each have the same name as their
	counterparts in the run-time library and are the actual code that
	is called when an application calls one of the run-time library
	routines.  Each of the glue routines does some register and stack
	munging to get things in the proper state for execution in the
	run-time library environment, then makes a call via the run-time
	library's jump table to the appropriate routine.  When control
	returns form the run-time library, the stack and registers are
	restored before returning to the application.
	The name of this file will be <libname>_stubs.asm

	The -h switch indicates that the C language header file is to
	be produced.  This file defines the Library node data structure,
	a pointer to which is returned by the OpenLibrary() function.
	The name of this file will be <libname>base.h

	The -i switch indicates that the assembler header file is to be
	produced.  This file defines the same Library node data structure
	as is defined in the C header above, but in assembler syntax.
	If you modify the template files to add fields to this structure,
	be sure that both the assembler and C versions match.  The assembler
	header is used by the boilerplate code to define how much space
	is to be allocated for the Library node.
	The name of this file will be <libname>base.i

	The -a option means to generate all four of the output files.

	More than one of the options may be specified at a time,
	for instance:  genlib -ls foo  will generate the boilerplate
	and the stubs files.

	The libname argument is required, it defines the name of the
	library to be built.  Several permutations of the basic name
	will be generated to give names to various files and symbols
	needed to build the library.  For example, running genlib as
	follows:  genlib -a foo  will define a run-time library
	named foo.library, define a FooBase struct in the header
	files foobase.h and foobase.i, etc.

	The header files will generally only need to be generated once,
	then copied to the appropriate include directory.  The stubs
	and boilerplate files will need to be re-generated each time
	a change is made to the specification file (such as adding or
	deleting functions), so that the library tables and link stubs
	match the real code in the library.  Genlib may be run from a
	makefile to do this automatically (see the makefile provided for
	the sample library code).

	The -g is optional, it specifies an alternate name for the genlib
	specification file.  By default genlib looks for a file named
	<libname>.gen in the current directory.

	The -d option is also optional, it specifies an alternate
	directory where the template files can be found.  The default
	directory is genlib:, which would usually be a logical assign.


Syntax of the Specification File

   The specification file contains lines consisting of a keyword, followed
by one or more parameters.  There are only five recognized keywords, three
of which are optional.  These keywords are: EQU, FUNC, LIBBASE, LIBNAME
and LIBID.

	EQU name value

	The EQU keyword defines a symbol by name and a value for that
	symbol.  There are three symbols which must be defined with EQU
	lines: VERSION, REVISION and PRIORITY.  Other equates may be
	defined, and all will be injected into the boilerplate assembly
	source file, but the provided template only makes use of these
	three.

	FUNC name (arg1, arg2, ... argn )

	The FUNC keyword defines a function that will be available in
	the run-time library.  Only the functions listed will be
	visible externally.  Any functions present in the code which
	makes up the library, but which do not have a FUNC line in
	the specification file, will be visible to other routines
	in the run-time library, but will not be callable by an
	application program which wishes to use the library.  All
	functions declared in the specification file must exist,
	otherwise there will be unresolved symbols at link time.
	The function names specified by the FUNC lines are used in
	several places, to build the jump table into the library, to
	generate the link-library stubs, etc.
	When a function is declared with the FUNC keyword, it is also
	necessary to specify the arguments that the function expects.
	This argument list is necessary to properly generate the glue
	code which passes the arguments on to the run-time library
	routines.  The names of the arguments are not important, but
	specifying the correct number is.  The argument names are not
	retained, they are only counted.  You can (and normally would)
	simply copy the C source line from the function declaration
	into the specification file and prepend FUNC.  The parenthesis
	and commas are optional, they are ignored.  These two lines
	can be considered identical:
		FUNC foo_func1 (fro, boz, bap)
		FUNC foo_func1 a b c

 	NOTE: All arguments are considered to be long ints or pointers
	(ie, four bytes).  All routines written for the run-time library
	should by compiled with the Manx +L option, or at least be
	prepared to accept four byte arguments.  Likewise, all application
	program calls to the library routines should provide four byte
	arguments when calling routines in the run-time library.

	LIBBASE basename

	This keyword is optional, and if provided specifies the name of
	the Library node base data structure for the library.  This
	is the data structure which OpenLibrary() returns a pointer
	to when the library is successfully opened.  The first field
	in this data structure is a Library node, which Exec uses to
	connect the library into the system list of available run-time
	libraries.  Following this may be other fields that the code
	in the library might wish to make publicly visible to an
	application which has a pointer to the library.  The library
	boilerplate code defines two fields in this data structure, one
	for status flags, and one to provide the library's a4 register
	to the link library glue code.
	The default name of this data structure will be the basic library
	name, with the first letter capitalized, and Base appended.  Hence,
	specifying a library name of "foo" will yield "FooBase".  This
	is the conventional form.

	LIBNAME libraryfilename

	This keyword is optional, and if provided specifies the filename
	of the final run-time library.  This is the name the library will
	have in the devs: directory, and which is used as a paramenter
	to the OpenLibrary() call.  This name is used to define the
	symbolic name of the library in the C and assembler header files,
	and to define the library's internal name to Exec.
	The default name of the library will be the basic library name
	with ".library" appended.  So a library name of "foo" will
	become "foo.library".

	LIBID "library ID string"

	This keyword is optional, and if provided specifies the library
	identifier string for the library.  This string consists of
	the library name, date and revision information in ascii form.
	A pointer to this string is in one of the tables available to
	Exec when it loads the library.  Note that if you specify this
	string yourself, you'll need to quote it if there is any
	imbedded whitespace.  You won't generally want to specify this
	string explicitly, genlib will automatically generate the proper
	string, including the date the file was generated.


	Lines in the specification file which begin with '#', or which
	are blank, are considered comments and are ignored.


	That's all there is to it.  See the specification file (foo.gen)
	included with the sample library code for an example of a generic
	genlib configuration.


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 Doc/usage//
chmod u=rw,g=r,o=r Doc/usage
 
echo x - Doc/programmer.notes
sed 's/^@//' > "Doc/programmer.notes" <<'@//E*O*F Doc/programmer.notes//'

	Genlib - Programming Notes


   These are some of the things you'll need to know about and watch out
for when writing code in a run-time library.


Special Symbols in the Boilerplate Code

   There are several symbols defined by the standard bolilerplate template
that your library code may make use of, and two special functions that you
must provide in your code.

   Four variables are defined in the boilerplate with constant names and
which are exported so that other code modules may reference them as extern
variables.  These are:

	LibVersion - a long integer, set to the value of the VERSION
		equate from the specification file.

	LibRevision - a long integer set to the value of the REVISION
		equate from the spec file.

	LibName - a null-terminated character array, set to the same value
		as the library name string defined in the header files
		produced by genlib (<libname>.library, or LIBNAME keyword).

	LibID - a null-terminated character array, set to the value of
		the ID string generated by genlib (or possibly the string
		specified by the LIBID keyword in the spec file).  Note
		that the standard ID string contains a trailing newline.


   Your code must provide two functions, named LibInitHook and LibExitHook,
which will be called by the boilerplate code.  Each of these is passed
one parameter, the pointer to the Library base node of the library.

	LibInitHook - This function is called from the initialization
		code when the library is first loaded.  It will be
		called only once, not each time the library is opened.
		This function should do any necessary startup that
		other functions in the library will need, such as
		opening other libraries (graphics, intuition, etc).
		This function must return a long integer, a return
		value of 0 indicates success, a non-zero value will
		indicate to the boilerplate that the initialization
		failed and that the library open is to be aborted.
		If the open is aborted, the application will receive
		a null pointer result to its OpenLibrary() call.

	LibExitHook - This function is called from the expunge routine
		in the boilerplate.  It will be called when the library
		is about to be removed from memory, not on each close.
		This function should do any necessary cleanup, such
		as closing libraries that the init hook opened, etc.
		This is a void function and returns no value.


Reentrancy Considerations

   In many respects, writing a run-time library is a lot like writing a
a conventional application program.  In fact a run-time library IS an
application program, it's in the same executable file format and can be
run from the CLI (but it will promptly exit without doing anything).

   However, a run-time library varies from a regular program in that
any of the functions in it can be called at ANY time (or rather any of
the functions listed in the jump table).  This means that the code in
the run-time library must be reentrant.  The term "reentrant" means
that the code must be capable of being reentered (called again) at any
time by another control thread (another task) without corrupting any
data or getting things mixed up between the multiple threads.

   When writing reentrant code in C, this basically boils down to not
using any global variables.  If a reentrant function where to store a
value in a global variable, then be interrupted and reentered, the second
thread would store (a possibly different) value to the same global variable.
When the first thread resumed running, the global variable that it had
previously set would not contain the same value that it had stored
there.  This is a Bad Thing.  Reentrant code must be aware of this sort
of situation, including calling library routines that use hidden globals,
like stdio and malloc.  Using only stack-based (automatic) variables,
and being aware of the side-effects of sub-routines, will avoid 99% of
reentrancy problems.   The only time it is acceptable to use global
variables is when they are are pre-initialized and read-only.


Developing Run-Time Libraries

   When you are developing and debugging run-time libraries, remember
that the library is normally loaded once and then left in memory, so
that the ram copy is used on subsequent opens of that library.  If you
make a change to the library, you'll need to get rid of the ram-resident
copy.  The easiest way to do this is to run WorkBench with the debug
option (LoadWB -debug), which will add a hidden menu to its menu bar
that will let you flush unused libraries from memory.  Then you can
copy the newly recompiled library to libs: and it will be reloaded on
the next OpenLibrary() call.

   You can also do this sequence of calls in your application:
	OpenLibrary()
	RemLibrary()
	CloseLibrary()
which will cause the run-time library to unload from memory when the
CloseLibrary() is done (this is called a delayed expunge).  The provided
boilerplate handles this properly.  This is not guaranteed to work properly
for all run-time libraries, since the library code can choose to go away
on the RemLibrary() call, or not go away on the CloseLibrary() call,
or an OpenLibrary() call by another task between you RemLibrary() and your
CloseLibrary() will cancel the expunge.  But this will work properly in
most cases with the provided boilerplate code.

   It can also be dificult to debug run-time library code, since you can't
do printf()'s from within the library code (remember those naughty hidden
globals?).  Debuggers, such as db, are also difficult to use, because
execution passes out of the code space that the debugger knows about from
the symbol table in the application program.  So you've got to exercise
good coding practices.  Make each of your library routines as self-
contained and atomic as possible.


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 Doc/programmer.notes//
chmod u=rw,g=r,o=r Doc/programmer.notes
 
echo x - Doc/hacker.notes
sed 's/^@//' > "Doc/hacker.notes" <<'@//E*O*F Doc/hacker.notes//'

	Genlib - Notes to Hackers


   The provided template files were developed and tested with Manx Aztec C,
version 3.6a.  No attempt has been made to provide Lattice compatibility.
This should not be too difficult, but I don't own Lattice so I haven't even
tried.  The initialization code in the boilerplate template might have to be
changed somewhat, I don't know what Lattice does at startup.  The assembler
syntax might even be different for Lattice, I don't know.

   Support for floating point hardware is not provided, other than to
reset the 68881/68882 state on entry and exit like the regular Manx
startup code.  If one makes a library that uses floating point, then
there may be some extra chip setup that needs to be done during the
initialization.  But this could probably be done in the external LibInitHook
function which is called by the standard boilerplate, since this is where
you'd probably open the necessary math libraries anyway.  The reset done
by the boilerplate may even be a problem, if the application has already
done some chip setup before the library is opened.

   Nothing is done to detect or take any special action when running on
different CPUs (68020, etc).  This is probably the right thing to do,
a library really shouldn't diddle with CPU cache states and such anyway.

   Genlib will run as-is on Unix, which may or may not be useful.  I
do most of my editing on my Sun, then compile on the A2000 across the
ethernet with NFS, so that is useful for me.  The output will be identical,
but of course the result will still need to be compiled on the Amiga.

   Everything is edited with 8 column tabs unless otherwise noted.


   If anyone comes up with Lattice versions of the templates, I'd like
to get copies of them.  I'd also naturally like to know of any bugs or
reasonable enhancements to genlib.  I do NOT plan to support genlib, or
develop newer versions etc.  I'm posting it one time and don't expect
to do anything more to it unless somebody shows me something brilliant and 
incredibly useful.  It does what it was intended to do, and that's all it
should be expected to do.

   I decided to write genlib after playing around with an old package called
mklib by Edwin Hoogerbeets.  That was very useful for figuring out how
to build a run-time library.  But it was basically a quick hack for
demonstration purposes, and I needed to create a real, production library.
None of the code from mklib is in genlib, but most of the example library
code from the blue Rom Kernel Manual is in the boilerplate template,
optimized quite a bit and expanded a lot.  Edwin's version of getopt() is
also included (incompatible with AT&T's, grrr...), and my version of his
stricmp() routine (hacked to be source compatible with my Sun).

   Thanks also to the designers of the Amiga, who had the vision and
good sense to create an OS for a personal micro by *actually designing it*,
rather than welding together a bunch of hacks, like countless others I
have seen.  Good work guys.


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 Doc/hacker.notes//
chmod u=rw,g=r,o=r Doc/hacker.notes
 
echo mkdir - Sample
mkdir Sample
chmod u=rwx,g=rwx,o=rwx Sample
 
echo x - Sample/makefile
sed 's/^@//' > "Sample/makefile" <<'@//E*O*F Sample/makefile//'

#	Makefile to gen and compile the sample run-time and link libraries
#
#	WARNING!  This is a fairly complicated makefile, you may need
#	to increase the stack size before running make, or it could crash.
#	A stack size of 6000 should be sufficient, 4000 may not be enough.

# set this to the name of the library you want to create
LIBNAME = foo

# set this to the list of object modules that make up your library
RTCOBJS = init.o timer.o dialog.o

# set these to your favorite settings.  Do keep in mind that the run-time
# library routines are expecting long arguments.  The list of libs here
# should be whatever is needed by the object modules above.
CFLAGS = +L
LIBS = -lc32

# Change this dir to whereever you put your link libraries.
# The stubs link-library created in this makefile must be visible later on
# when the test program is linked.  You'll probably want to make sure this
# directory is included in the CLIB environment var before starting.
# You'll also need to make sure that the INCLUDE path is setup correctly
# so that the .h and .i headers can be found when the boilerplate and
# stubs code is assembled.  Either include the current directory in the
# INCLUDE var, or gen the header files by hand and copy them into the
# proper directories.
LDIR = manx:mylibs

# this is where the final runtime library should be put, must be libs:
# for Amiga Exec to see it.
RDIR = libs:

# set this to the directory where the genlib templates are
GENLIBDIR = genlib:

# set this to be the path of the genlib executable
GENLIBPROG = $(GENLIBDIR)genlib

###################################################

# The stuff below should not need to be changed.  It defines various names,
# permuted from the base library name above.

SPECFILE = $(LIBNAME).gen

LIBBASEHDR = $(LIBNAME)base.i
LIBCHDR = $(LIBNAME)base.h

RTLIB = $(LIBNAME).library
RTBASE = $(LIBNAME)_library.o
RTBASESRC = $(LIBNAME)_library.asm
RTOBJS = $(RTBASE) $(RTCOBJS)

LNKLIB = $(LIBNAME)32.lib
LNKBASE = $(LIBNAME)_stubs.o
LNKBASESRC = $(LIBNAME)_stubs.asm
LNKCOBJS = 
LNKOBJS = $(LNKBASE) $(LNKCOBJS)

# full paths of the template files, for dependency testing
RTLIBTEMPL = $(GENLIBDIR)library.template
LNKLIBTEMPL = $(GENLIBDIR)stubs.template
CHDRTEMPL = $(GENLIBDIR)chdr.template
ASMHDRTEMPL = $(GENLIBDIR)asmhdr.template

###################################################


TESTPROG = test
TESTOBJ = test.o


all: $(LIBNAME) $(TESTPROG)

$(LIBNAME): $(RDIR)$(RTLIB) $(LDIR)$(LNKLIB)
	@echo "$(LIBNAME) library builds done"


# to actually install the run-time library when it's built, change
# the echo line to do the real copy
$(RDIR)$(RTLIB): $(RTLIB)
	@echo "You need to do: copy $(RTLIB) $(RDIR) "
	@echo "$(LIBNAME) Run-Time Library Built"

$(RTLIB): $(RTOBJS)
	ln -o $(RTLIB) $(RTOBJS) $(LIBS)

# to actually install the link library when it's built, change
# the echo line to do the real copy
$(LDIR)$(LNKLIB): $(LNKLIB)
	@echo "You need to do: copy $(LNKLIB) $(LDIR) "
	@echo "$(LIBNAME) Link Library Built"

$(LNKLIB): $(LNKOBJS)
	lb $(LNKLIB) $(LNKOBJS)

#######

$(TESTPROG): $(LIBCHDR) $(TESTPROG).o
	ln -o $(TESTPROG) $(TESTOBJ) -l$(LIBNAME)32 $(LIBS)

###################################################

# the dependency rules for generating source files from the templates

$(RTBASESRC): $(SPECFILE) $(LIBBASEHDR) $(RTLIBTEMPL)
	$(GENLIBPROG) -l $(LIBNAME)

$(LNKBASESRC): $(SPECFILE) $(LIBBASEHDR) $(LNKLIBTEMPL)
	$(GENLIBPROG) -s $(LIBNAME)

$(LIBBASEHDR): $(SPECFILE) $(ASMHDRTEMPL)
	$(GENLIBPROG) -i $(LIBNAME)

$(LIBCHDR): $(SPECFILE) $(CHDRTEMPL)
	$(GENLIBPROG) -h $(LIBNAME)

$(RTBASE): $(RTBASESRC)

$(LNKBASE): $(LNKBASESRC)

$(RTCOBJS) $(LNKCOBJS): $(LIBCHDR)

@//E*O*F Sample/makefile//
chmod u=rw,g=r,o=r Sample/makefile
 
echo x - Sample/foo.gen
sed 's/^@//' > "Sample/foo.gen" <<'@//E*O*F Sample/foo.gen//'

#	Genlib - Spec file for sample run-time library


equ	VERSION		1
equ	REVISION	0
equ	PRIORITY	0


# init.c (also LibInitHook and LibExitHook)
func	Foo_LibName ()
func	Foo_LibID ()
func	Foo_LibVersion ()
func	Foo_LibRevision ()

# timer.c
func	create_timer ()
func	delete_timer (iob)

# dialog.c
func	get_response (window, body, pos, neg, icon)

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

/*
 *	Genlib - Sample library C code
 *
 *	Initialization and expunge hooks called from assembler library
 *	code.  Also a few trivial routines to return information defined
 *	in the run-time library boilerplate code.
 */

#include <exec/execbase.h>
#include <devices/timer.h>
#include <functions.h>

#include <foobase.h>



/*
 *	These global variables are defined in the assembler boilerplate.
 *	They are given C-like names and are exported so that this C code
 *	can see them.
 */

extern long			LibVersion;
extern long			LibRevision;
extern char			LibName [];
extern char			LibID [];


/*
 *	These variables are the global definitions for other C code
 *	in this library that may use Intuition or Graphics library
 *	calls.  These are only visible to code in this library, not
 *	in the original calling program's data space.
 *
 *	Note that these are global variables, and we're going to modify
 *	them.  This is an exception to the no globals rule. We'll only
 *	touch them at startup and exit, and Exec is protecting us at those
 *	times so we don't need to worry about reentrance.
 */

struct IntuitionBase		*IntuitionBase = (struct IntuitionBase *)0;
struct GfxBase			*GfxBase = (struct GfxBase *)0;


/*
 *	Called by the assembler base code in the library when the
 *	library is initially loaded (NOT on each OpenLibrary).
 *	The assembler code has already opened the Exec and DOS libraries
 *	for us.  This hook should open any other needed libraries and
 *	do any other one-time startup initialization.
 *	Task switching is disabled while we're in here, and Exec is
 *	waiting for us to return, so we shouldn't take very long.
 *
 *	This function should return zero if all goes well.  If it returns
 *	a non-zero value, then the library open will be aborted and the
 *	original caller will be returned a null library pointer.
 */

long
LibInitHook (FooBase)
	struct FooBase	*FooBase;
{
	IntuitionBase = (struct IntuitionBase *)
		OpenLibrary ("intuition.library", 0L);

	if (IntuitionBase == (struct IntuitionBase *)0) {
		return (-1);
	}

	GfxBase = (struct GfxBase *) OpenLibrary ("graphics.library", 0L);

	if (GfxBase == (struct GfxBase *)0) {
		CloseLibrary (IntuitionBase);
		IntuitionBase = (struct IntuitionBase *)0;
		return (-1);
	}

	return (0);		/* indicate success */
}


/*
 *	Called when the library is about to be expunged (not on each Close).
 *
 */

void
LibExitHook (FooBase)
	struct FooBase	*FooBase;
{
	if (GfxBase != (struct GfxBase *)0) {
		CloseLibrary (GfxBase);
		GfxBase = (struct GfxBase *)0;
	}

	if (IntuitionBase != (struct IntuitionBase *)0) {
		CloseLibrary (IntuitionBase);
		IntuitionBase = (struct IntuitionBase *)0;
	}
}


/*
 *	Library info hooks, simply return info defined in lib globals
 */

char *
Foo_LibName ()
{
	return (LibName);
}


char *
Foo_LibID ()
{
	return (LibID);
}


long
Foo_LibVersion ()
{
	return (LibVersion);
}

long
Foo_LibRevision ()
{
	return (LibRevision);
}

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

/* (ts=4)
 * ------------------------------------------------------------------------
 * getresponse.c -- Copyright 1989 Joe Hitchens All Rights Reserved
 * ------------------------------------------------------------------------
 *
 * Name    : getresponse()
 * Syntax  : response = getresponse( window, bodytxt, postxt, negtxt, icon )
 *         :   BOOL              response
 *         :   struct Window    *window
 *         :   char             *bodytxt
 *         :   char             *postxt
 *         :   char             *negtxt
 *         :   char              icon
 * Descrip : Get user response via a fancy TRUE/FALSE requester.
 *         : "window" is any window in the Screen in which you would like the
 *         : requester to appear.  If "window" is NULL, then the requester
 *         : will appear on the WorkBench screen.
 *         : "bodytxt" is the text of the message that will appear in the
 *         : requester.  Newline characters are supported and will continue
 *         : the text one text line down.
 *         : "postxt" is a short string like OK or YES which will appear in
 *         : the gadget for a POSITIVE response. If "postxt" is NULL, then
 *         : there the requester will have only one button and will function
 *         : more like a simple alert mechanism.  See the docs for the
 *         : Intuition function AutoRequest(), which has a similar behavior.
 *         : "negtxt" is the text for the NEGATIVE response button.
 *         : The optional "icon" argument is a single char which causes one
 *         : of the special icons to appear on the right side of the
 *         : requester.  For yes/no type requesters, the recommended icon
 *         : is '?'.  For alert type requesters, use '!'.
 * Icons   : The only icons supported so far are '?' and '!'.
 * See Also: AutoRequest()
 *
 */

#include <exec/memory.h>
#include <intuition/intuition.h>

#include <functions.h>
#include <clib/macros.h>


extern char		*strchr();
extern char		*malloc();

#define TWICE(n)		((n) << 1)
#define THRICE(n)		((n) * 3)
#define ATHIRDA(n)		((n) / 3)
#define HALFA(n)		((n) >> 1)

#define BORD_W			2		/* Size of the border lines. 2 is for HIRES */
#define BORD_H			1
#define ELBOW_W			8		/* Space between borders and stuff inside */
#define ELBOW_H			3
#define ICON_W			32		/* Size of the "!", "?" etc. icons */
#define ICON_H			32
#define ICON_D			2
#define ICON_SIZE		(RASSIZE(ICON_W,ICON_H)*ICON_D)
#define WIN_MIN_W		((BORD_W * 4) + TWICE(ELBOW_W))
#define WIN_MIN_H		((BORD_H * 8) + THRICE(ELBOW_H) + 16)

#define GETWMSG(win)	(struct IntuiMessage *)GetMsg(win->UserPort)

#define bzero(a,l)		setmem(a,l,0)


typedef struct {
	short				x1, y1, x2, y2;
} rect;


/*
 * " ! " icon, Image size = 32 x 32 x 2
 */
unsigned short		exclamation_idata[] = {
	/* Bit plane #0 */
	0xffff, 0xffff, 0xffff, 0xffff, 0xfff8, 0x1fff, 0xfff8, 0x1fff,
	0xfff8, 0x1fff, 0xfff8, 0x1fff, 0xfff8, 0x1fff, 0xfff8, 0x1fff,
	0xfff8, 0x1fff, 0xfff8, 0x1fff, 0xfff8, 0x1fff, 0xfff8, 0x1fff,
	0xfff8, 0x1fff, 0xfff8, 0x1fff, 0xfff8, 0x1fff, 0xfff8, 0x1fff,
	0xfff8, 0x1fff, 0xfff8, 0x1fff, 0xfff8, 0x1fff, 0xfff8, 0x1fff,
	0xfff8, 0x1fff, 0xfff8, 0x1fff, 0xfff8, 0x1fff, 0xffff, 0xffff,
	0xffff, 0xffff, 0xffff, 0xffff, 0xfff8, 0x1fff, 0xfff8, 0x1fff,
	0xfff8, 0x1fff, 0xfff8, 0x1fff, 0xffff, 0xffff, 0xffff, 0xffff,
	/* Bit plane #1 */
	0x0000, 0x0000, 0x0000, 0x0000, 0x0007, 0xe000, 0x0007, 0xe000,
	0x0007, 0xe000, 0x0007, 0xe000, 0x0007, 0xe000, 0x0007, 0xe000,
	0x0007, 0xe000, 0x0007, 0xe000, 0x0007, 0xe000, 0x0007, 0xe000,
	0x0007, 0xe000, 0x0007, 0xe000, 0x0007, 0xe000, 0x0007, 0xe000,
	0x0007, 0xe000, 0x0007, 0xe000, 0x0007, 0xe000, 0x0007, 0xe000,
	0x0007, 0xe000, 0x0007, 0xe000, 0x0007, 0xe000, 0x0000, 0x0000,
	0x0000, 0x0000, 0x0000, 0x0000, 0x0007, 0xe000, 0x0007, 0xe000,
	0x0007, 0xe000, 0x0007, 0xe000, 0x0000, 0x0000, 0x0000, 0x0000,
};

/*
 * " ? " icon, Image size = 32 x 32 x 2
 */
unsigned short		question_idata[] = {
	/* Bit plane #0 */
	0xffff, 0xffff, 0xffff, 0xffff, 0xff00, 0x007f, 0xfe00, 0x003f,
	0xfc00, 0x001f, 0xfc00, 0x000f, 0xfc1f, 0xfc07, 0xfc1f, 0xfe07,
	0xffff, 0xfe07, 0xffff, 0xfe07, 0xffff, 0xfc07, 0xfffc, 0x000f,
	0xfff8, 0x001f, 0xfff0, 0x003f, 0xffe0, 0x007f, 0xffe0, 0x1fff,
	0xffe0, 0x3fff, 0xffe0, 0x7fff, 0xffe0, 0x7fff, 0xffe0, 0x7fff,
	0xffe0, 0x7fff, 0xffe0, 0x7fff, 0xffe0, 0x7fff, 0xffff, 0xffff,
	0xffff, 0xffff, 0xffff, 0xffff, 0xffe0, 0x7fff, 0xffe0, 0x7fff,
	0xffe0, 0x7fff, 0xffe0, 0x7fff, 0xffff, 0xffff, 0xffff, 0xffff,
	/* Bit plane #1 */
	0x0000, 0x0000, 0x0000, 0x0000, 0x00ff, 0xff80, 0x01ff, 0xffc0,
	0x03ff, 0xffe0, 0x03ff, 0xfff0, 0x03e0, 0x03f8, 0x03e0, 0x01f8,
	0x0000, 0x01f8, 0x0000, 0x01f8, 0x0000, 0x03f8, 0x0003, 0xfff0,
	0x0007, 0xffe0, 0x000f, 0xffc0, 0x001f, 0xff80, 0x001f, 0xe000,
	0x001f, 0xc000, 0x001f, 0x8000, 0x001f, 0x8000, 0x001f, 0x8000,
	0x001f, 0x8000, 0x001f, 0x8000, 0x001f, 0x8000, 0x0000, 0x0000,
	0x0000, 0x0000, 0x0000, 0x0000, 0x001f, 0x8000, 0x001f, 0x8000,
	0x001f, 0x8000, 0x001f, 0x8000, 0x0000, 0x0000, 0x0000, 0x0000,
};



long
strnumc ( s, c )
	register char		*s;
	register char		c;
{
	register long		n;

	n = 0;
	while ( *s ) {
		if ( *s == c ) {
			n++;
		}
		s++;
	}
	return( n );
}


long
strmaxcols ( s )
	register char		*s;
{
	register long		n;
	register long		max;

	n = max = 0;
	while ( *s ) {
		if ( *s != '\n' ) {
			n++;
		} else {
			n = 0;
		}
		if ( n > max ) {
			max = n;
		}
		s++;
	}
	return( max );
}


BOOL
GetWBScreenData ( buff )
	register char		*buff;
{
	return( GetScreenData(buff, sizeof(struct Screen), WBENCHSCREEN, NULL) );
}


static void
FillRect( rp, r )
	register struct RastPort	*rp;
	register rect				*r;
{
	RectFill( rp, r->x1, r->y1, r->x2, r->y2 );
}


static void
ShrinkRect( rp, r, c )
	register struct RastPort	*rp;
	register rect				*r;
	register UBYTE				c;
{
	SetAPen( rp, c );
	SetDrMd( rp, JAM1 );
	FillRect( rp, r );
	r->x1 += BORD_W;
	r->y1 += BORD_H;
	r->x2 -= BORD_W;
	r->y2 -= BORD_H;
}


long
linelen ( s )
	register char		*s;
{
	register long		n;

	n = 0;
	while ( *s ) {
		if ( *s == '\n' ) {
			break;
		}
		n++;
		s++;
	}
	return( n );
}


char *
nextline ( s )
	register char		*s;
{
	while ( *s ) {
		if ( *s == '\n' ) {
			return( s + 1 );
		}
		s++;
	}
	return( (char *) 0 );
}


void
flash_gadget ( w, g )
	register struct Window		*w;
	register struct Gadget		*g;
{
	register rect				r;
	register struct RastPort	*rp;

	r.x1 = g->LeftEdge;
	r.y1 = g->TopEdge;
	r.x2 = (r.x1 + g->Width) - 1;
	r.y2 = (r.y1 + g->Height) - 1;
	rp = w->RPort;
	SetDrMd( rp, COMPLEMENT );
	FillRect( rp, &r );
	Delay( 5 );
	FillRect( rp, &r );
	Delay( 5 );
}
		

void
redraw_window ( w, t, defgad, icon )
	struct Window				*w;
	char						*t;
	struct Gadget				*defgad;
	char						icon;
{
	rect						r;
	register struct RastPort	*rp;
	struct Gadget				*g;
	short						th;

	rp = w->RPort;

	/* Clear window and draw edging */
	r.x1 = 0;
	r.y1 = 0;
	r.x2 = (w->Width - 1);
	r.y2 = (w->Height - 1);
	ShrinkRect( rp, &r, 1 );
	ShrinkRect( rp, &r, 2 );
	ShrinkRect( rp, &r, 1 );
	if ( icon != '\0' ) {
		register USHORT				*p;
		struct Image				image;

		image.LeftEdge =
			((w->Width - 1) - ((2 * BORD_W) + (ELBOW_W) + (ICON_W)));
		image.TopEdge = ((2 * BORD_H) + ELBOW_H);
		image.Width = ICON_W;
		image.Height = ICON_H;
		image.Depth = ICON_D;
		image.PlanePick = 0x03;
		image.PlaneOnOff = 0x00;
		image.NextImage = NULL;
		switch( icon ) {
			case '?':
				p = question_idata;
				break;
			case '!':
			default:
				p = exclamation_idata;
				break;
		}
		image.ImageData = (USHORT *) AllocMem( ICON_SIZE, MEMF_CHIP );
		if ( image.ImageData ) {
			CopyMem( p, image.ImageData, ICON_SIZE );
			DrawImage( rp, &image, 0L, 0L );
			FreeMem( image.ImageData, ICON_SIZE );
		}
	}

	/* Draw body text */
	th = (rp->TxHeight + TWICE( BORD_H ));
	Move( rp,
		((2 * BORD_W) + ELBOW_W),
		(((2 * BORD_H) + ELBOW_H) + rp->TxBaseline) );
	SetAPen( rp, 2 );
	while ( t ) {
		Text( rp, t, linelen( t ) );
		Move( rp,
			((2 * BORD_W) + ELBOW_W),
			(rp->cp_y + th) );
		t = nextline( t );
	}

	g = w->FirstGadget;
	while ( g ) {
		UBYTE			c;

		r.x1 = (g->LeftEdge - (3 * BORD_W));
		r.y1 = (g->TopEdge - (3 * BORD_H));
		r.x2 = (g->LeftEdge + g->Width + ((2 * BORD_W) + 1));	/* KLUDGE */
		r.y2 = (g->TopEdge + g->Height + (2 * BORD_H));
		ShrinkRect( rp, &r, ((g == defgad) ? (2) : (1)) );
		ShrinkRect( rp, &r, ((g == defgad) ? (3) : (1)) );
		ShrinkRect( rp, &r, 2 );
		ShrinkRect( rp, &r, 1 );
		SetAPen( rp, 2 );
		Move( rp, r.x1, r.y1 + rp->TxBaseline);
		Text( rp, g->UserData, linelen( g->UserData ) );
		g = g->NextGadget;
	}

	RefreshGadgets( w->FirstGadget, w, NULL );
}


BOOL
get_response ( window, body, pos, neg, icon )
	struct Window		*window;
	char				*body, *pos, *neg;
	char				icon;
{
	struct Screen		*screen;
	struct Screen		wbscreen;
	struct NewWindow	nw;
	struct Gadget		pos_gad, neg_gad, *def_gad;
	struct Window		*rw;
	struct RastPort		*rp;
	short				done, num_lines, num_columns;
	rect				r;
	BOOL				result;

	if ( window == NULL ) {
		if ( GetWBScreenData( &wbscreen ) == FALSE ) {
			return( FALSE );
		}
		screen = &wbscreen;
		rp = &(screen->RastPort);
	} else {
		screen = window->WScreen;
		rp = window->RPort;
	}
	bzero( &nw, sizeof( struct NewWindow ) );
	num_columns = strmaxcols( body );
	nw.Width = WIN_MIN_W + (num_columns * rp->TxWidth);
	num_lines = 1 + strnumc( body, '\n' );
	nw.Height = WIN_MIN_H + (num_lines * (rp->TxHeight + 2));
	if ( icon != '\0' ) {
		nw.Width += ICON_W + ELBOW_W;
		nw.Height += ICON_H;
	}
	if ( nw.Width > screen->Width ) {
		nw.Width = screen->Width;
	}
	if ( nw.Height > screen->Height ) {
		nw.Height = screen->Height;
	}
	nw.LeftEdge = HALFA( screen->Width - nw.Width );
	nw.TopEdge = ATHIRDA( screen->Height - nw.Height );
	nw.IDCMPFlags = (REFRESHWINDOW | VANILLAKEY | GADGETUP);
	nw.Flags = (BORDERLESS | ACTIVATE | RMBTRAP | SIMPLE_REFRESH);
	if ( window == NULL ) {
		nw.Type = WBENCHSCREEN;
	} else {
		nw.Screen = screen;
		nw.Type = CUSTOMSCREEN;
	}
	nw.FirstGadget = &neg_gad;

	bzero( &neg_gad, sizeof( struct Gadget ) );
	neg_gad.Width = ((linelen( neg ) * rp->TxWidth) + TWICE( BORD_W ));
	neg_gad.Height = (rp->TxHeight + TWICE( BORD_H ));
	neg_gad.Flags = GADGHCOMP;
	neg_gad.Activation = RELVERIFY;
	neg_gad.GadgetType = BOOLGADGET;
	neg_gad.TopEdge =
		(nw.Height - (6 * BORD_H) - ELBOW_H - neg_gad.Height);
	neg_gad.UserData = (APTR) neg;
	neg_gad.GadgetID = FALSE;
	if ( pos ) {
		neg_gad.LeftEdge =
			(nw.Width - neg_gad.Width - ((5 * BORD_W) + ELBOW_W));
		neg_gad.NextGadget = &pos_gad;
		bzero( &pos_gad, sizeof( struct Gadget ) );
		pos_gad.Width = ((linelen( pos ) * rp->TxWidth) + TWICE( BORD_W ));
		pos_gad.Height = neg_gad.Height;
		pos_gad.Flags = neg_gad.Flags;
		pos_gad.Activation = neg_gad.Activation;
		pos_gad.GadgetType = neg_gad.GadgetType;
		pos_gad.LeftEdge = ((5 * BORD_W) + ELBOW_W);
		pos_gad.TopEdge = neg_gad.TopEdge;
		pos_gad.UserData = (APTR) pos;
		pos_gad.GadgetID = TRUE;
	} else {
		neg_gad.LeftEdge = (HALFA( nw.Width ) - HALFA( neg_gad.Width ));
	}
	def_gad = &neg_gad;

	rw = OpenWindow( &nw );
	if ( rw == NULL ) {
		return( FALSE );
	}

	redraw_window( rw, body, def_gad, icon );
	done = 0;
	while ( ! done ) {
		struct IntuiMessage		*m; 

		WaitPort( rw->UserPort );
		while((m = GETWMSG( rw )) != NULL ) {
			struct Gadget			*g;

			switch( m->Class ) {
				case VANILLAKEY:
					if ( m->Code == '\r' ) {
						flash_gadget( rw, def_gad );
						result = def_gad->GadgetID;
						done = 1;
					}
					break;
				case GADGETUP:
					g = (struct Gadget *)(m->IAddress);
					result = (BOOL) g->GadgetID;
					done = 1;
					break;
				case REFRESHWINDOW:
					BeginRefresh( rw );
					redraw_window( rw, body, def_gad, icon );
					EndRefresh( rw, 1 );
					break;
			}
			ReplyMsg( m );
		}
	}

	CloseWindow( rw );
	return( result );
}


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

/*
 *	Genlib - Sample run-time library C code
 *
 *	Routines to create and delete timer I/O blocks
 */


#include <exec/execbase.h>
#include <devices/timer.h>
#include <functions.h>


#define NULL_PORT	(struct MsgPort *)0
#define NULL_TIMER	(struct timerequest *)0



/*
 *	These two routines don't even know that they are in a run-time
 *	library.  But since they don't use any globals, and behave
 *	nicely, they work just dandy.
 */


struct timerequest *
create_timer ()
{
	struct MsgPort		*port;
	struct timerequest	*iob;

	port = CreatePort (0, 0);
	iob = (struct timerequest *) CreateExtIO (port,
		sizeof (struct timerequest));

	if ((iob == (struct timerequest *)0) || (port == NULL_PORT)) {
		return (NULL_TIMER);
	}

	if (OpenDevice ("timer.device", UNIT_VBLANK, iob, 0)) {
		DeleteExtIO (iob, sizeof (struct timerequest));
		DeletePort (port);
		return (NULL_TIMER);
	}

	return (iob);
}


void
delete_timer (iob)
	struct timerequest	*iob;
{
	struct MsgPort		*port;

	if (iob == NULL_TIMER) {
		return;
	}

	if (CheckIO (iob) == 0) {
		WaitIO (iob);
	}

	port = iob->tr_node.io_Message.mn_ReplyPort;
	DeleteExtIO (iob, sizeof (struct timerequest));
	DeletePort (port);
}

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


#include <stdio.h>
#include <functions.h>
#include <exec/execbase.h>
#include <intuition/intuition.h>

#include <foobase.h>


struct FooBase		*FooBase = (struct FooBase *)0;

extern int		Foo_Version (), Foo_Revision ();
extern char		*Foo_LibID ();


main (argc, argv)
	int	argc;
	char	**argv;
{
	int	rc;

	FooBase = (struct FooBase *) OpenLibrary (FOONAME, 0L);
	if (FooBase == (struct FooBase *)0) {
		fprintf (stderr, "Can't open %s\n", FOONAME);
		fprintf (stderr, "Is it in libs: ?");
		exit (1);
	}

	printf ("%s is version %d, revision %d\n", FOONAME,
		Foo_LibVersion (), Foo_LibRevision ());
	printf ("ID String is: %s\n", Foo_LibID ());

	rc = get_response ((struct Window *)0,
		"Would you like a poke in the eye\n with a sharp stick?",
		" Yes Please ", " No Thanks ", '?');

	if (rc == TRUE) {
		(void) get_response ((struct Window *)0,
			"You are a sick individual", (char *)0,
			" Blech ", '!');
	} else {
		(void) get_response ((struct Window *)0,
			"Wise choice.", (char *)0, "(Bite Me)", '\0');
	}

	CloseLibrary (FooBase);

	exit (0);
}
		
@//E*O*F Sample/test.c//
chmod u=rw,g=r,o=r Sample/test.c
 
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 <<\!!!
     200    1536    9368 usage
     126     984    6032 programmer.notes
      63     563    3288 hacker.notes
     125     531    3672 makefile
      22      53     346 foo.gen
     133     467    2940 init.c
     461    1827   11838 dialog.c
      68     166    1185 timer.c
      51     156    1063 test.c
    1249    6283   39732 total
!!!
wc  Doc/usage Doc/programmer.notes Doc/hacker.notes Sample/makefile Sample/foo.gen Sample/init.c Sample/dialog.c Sample/timer.c Sample/test.c | 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