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