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