home *** CD-ROM | disk | FTP | other *** search
- /************************************************************************
- * *
- * Copyright (c) 1985, Fred Fish *
- * All Rights Reserved *
- * *
- * This software and/or documentation is released into the *
- * public domain for personal, non-commercial use only. *
- * Limited rights to use, modify, and redistribute are hereby *
- * granted for non-commercial purposes, provided that all *
- * copyright notices remain intact and all changes are clearly *
- * documented. The author makes no warranty of any kind with *
- * respect to this product and explicitly disclaims any implied *
- * warranties of merchantability or fitness for any particular *
- * purpose. *
- * *
- ************************************************************************
- */
-
-
- /*
- * cc -- C compiler front end for Amiga and Lattice C.
- *
- * Somewhat AMIGA/Lattice dependent, but can probably be adapted to
- * other systems with a minimum of work. I have attempted to keep
- * portability in mind as much as possible.
- *
- */
-
- char _sccsid[] = "@(#)cc.c 1.8";
-
- #include <stdio.h>
-
- /*
- * The following allow use on systems that don't have my macro based
- * debugging package. The default, for now, is to assume it is not
- * available.
- */
-
- #ifdef DBUG
- # include <local/dbug.h>
- #else /* !DBUG */
- # define DBUG_ENTER(a)
- # define DBUG_RETURN(a) return(a)
- # define DBUG_VOID_RETURN return
- # define DBUG_2(a,b)
- # define DBUG_3(a,b,c)
- # define DBUG_4(a,b,c,d)
- # define DBUG_5(a,b,c,d,e)
- # define DBUG_PUSH(a)
- #endif /* DBUG */
-
- /*
- * IMPLEMENTATION NOTES
- *
- * Some of the builtin (artificial) limits could be removed by
- * using dynamically allocated data structures, such as keeping the
- * operand list as a linked list with space for each link supplied
- * by malloc. This would certainly increase the code space, while
- * reducing the statically allocated data space. The net result
- * would probably be a program requiring about the same amount of
- * total memory for most day to day usages. When source is not
- * available to the end user, maximum flexibility is a must and
- * dynamic allocation is the only way to go. In this case however
- * it is not clear that the added complexity is worth it, since
- * source should be available for anyone wishing to expand the
- * limits.
- *
- * One last note, if you are going to have builtin limits then
- * check the @#$%&* things for overflow. Running off the end of
- * an array with no indication that something is wrong, other
- * than a crash, if definitely unfriendly!
- *
- */
-
-
- /*
- * Manifest constants which can be tuned to fit requirements.
- */
-
- #define CMDBUFFERSIZE (1024) /* Size of command buffer for CLI command */
- #define MAXOPERANDS (64) /* Maximum number of operands on cmd line */
- #define MAXDEFINES (32) /* Maximum number of -D<name> args */
- #define MAXUNDEFINES (32) /* Maximum number of -U<name> args */
- #define MAXINCDIRS (16) /* Maximum number of -I<filename> args */
- #define MAXLIBS (16) /* Maximum number of -l<lib> args */
- #define ARGSIZE (64) /* Size of temp args for cmd line */
-
- #define LOCALSYMTABSIZE (100) /* Local symbol table size in entries */
- #define EXPRTABSIZE (200) /* Expression table size */
- #define CASETABSIZE (256) /* Case table size */
- #define STRTABSIZE (5 * 1024) /* String table size */
-
- /*
- * Define QUADDEV to be the default place where you want the compiler
- * intermediate files (quad files) to be created. For systems with
- * 512K or more, the preferred place is the ram disk. However,
- * really big compiles may need to be done on a hard disk.
- * In either case, the default can be overridden with the -q option.
- */
-
- #define QUADDEV "ram:" /* Keep intermediate files in ram */
- /* #define QUADDEV "" */ /* Keep intermediate files in current dir */
- #define TEMPNAME "tempxxxx"
-
- /*
- * Manifest constants which are generally the same on all systems.
- */
-
- #define EOS '\000' /* End of string character */
-
- /*
- * Command line arguments that represent files to be compiled, assembled,
- * or linked, are kept track of via an "Operand" array. If, for example,
- * the file name is "df0:mydir/junk.c", then the Rootname is
- * "df0:mydir/junk", the Basename is "junk", and the Suffix is "c".
- * String suffixes are used, rather than single character suffixes, to
- * allow use of names with multicharacter suffixes.
- */
-
- struct Operand { /* Info about each operand (non option) */
- char *Rootname; /* Name minus any suffix */
- char *Basename; /* Name minus any prefix or suffix */
- char *Suffix; /* Suffix of operand */
- };
-
- static struct Operand Operands[MAXOPERANDS]; /* Table of operands */
- static int NOperands = 0; /* Number of operands found */
- static char *Defines[MAXDEFINES]; /* Table of defines */
- static int NDefines = 0; /* Number of defines */
- static char *UnDefines[MAXUNDEFINES]; /* Table of undefines */
- static int NUnDefines = 0; /* Number of undefines */
- static char *UserInc[MAXINCDIRS]; /* Table of include dirs */
- static int NUserInc = 0; /* Number of include dirs */
- static char *Libs[MAXLIBS]; /* Table of library args */
- static int NLibs = 0; /* Number of library args */
-
- /*
- * The command line buffer for child commands is allocated statically,
- * rather than as an automatic, to forstall problems with stack limits
- * in initial implementations. Hopefully, automatic stack growth will
- * be implemented in future release of the C compiler. If nothing
- * else, someday I will read the manuals and figure out how to explicitly
- * grow the stack...
- *
- */
-
- static char Command[CMDBUFFERSIZE]; /* Command line buffer */
- static char *EndCommand = Command; /* End of current command */
-
- /*
- * Macros to determine the suffix type of a file given a pointer to
- * its operand structure.
- */
-
- #define CFILE(op) (strcmp(op->Suffix,"c")==0)
- #define SFILE(op) (strcmp(op->Suffix,"s")==0)
- #define OFILE(op) (strcmp(op->Suffix,"o")==0)
- extern int strcmp ();
-
- /*
- * Now some macros to map from unix names to the AMIGA equivalents,
- * and to enable abort on control-C.
- */
-
- #ifdef amiga
- # define system(a) (Execute(a,0,0))
- # define ENABLE_ABORT (Enable_Abort = 1)
- # define DISABLE_ABORT (Enable_Abort = 0)
- # define CHECK_ABORT Check_Abort()
- extern int Enable_Abort; /* Enable abort on CNTL-C */
- static void Check_Abort (); /* Test for abort requested */
- #else
- # define ENABLE_ABORT /* Null expansion */
- # define DISABLE_ABORT /* Null expansion */
- # define CHECK_ABORT /* Null expansion */
- #endif /* amiga */
-
- /*
- * Set list of places to search for various executables, libraries, etc.
- * Searched in order, first match wins, null string is current directory.
- * Note that these names are used as prefixes exactly as given, so
- * device names must end in ':' and directory names must end in '/'.
- *
- */
-
- static char *Devices[] = {
- "",
- "ram:",
- "df0:",
- "df1:",
- NULL
- };
-
- static char *BinDirs[] = {
- "",
- "ram:c/",
- "df0:c/",
- "df1:c/",
- NULL
- };
-
- static char *LibDirs[] = {
- "",
- "ram:lib/",
- "df0:lib/",
- "df1:lib/",
- NULL
- };
-
- static char *IncDirs[] = {
- "ram:include/",
- "df0:include/",
- "df1:include/",
- NULL
- };
-
- /*
- * Flags set by command line arguments/
- */
-
- static int cflag; /* -c flag given */
- static int Hflag; /* -H flag given */
- static int Pflag; /* -P flag given */
- static int Sflag; /* -S flag given */
- static int Vflag; /* -V flag given (non-standard) */
-
- static int ErrCount = 0; /* Count of compile/assemble errors */
- static char *outfile = "a.out"; /* Output file name from linker */
- static char *QuadDev = QUADDEV; /* Where to keep quad files */
-
- static char *predefines = "-Damiga -Dm68000 -Dmanx";
-
- static char *Locate (); /* Find a file */
- static void AddToCommand (); /* Add argument to command buffer */
- static void InitCommand (); /* Initialize command buffer */
- static void Fatal (); /* Quit with fatal error */
- static void Warning (); /* Issue warning message */
- static void AddOperandToList (); /* Add .c, .s, or .o file to list */
- static void CleanObjects (); /* Remove .o for link and go mode */
- static void MakeObjects (); /* Reduce to list of object files */
- static void ParseCommandLine (); /* Deal with command line */
- static void Compile (); /* Translate from .c to .o */
- static int Assemble (); /* Translate from .s to .o */
- static void Link (); /* Gather .o's into executable */
-
- extern void exit (); /* See exit(2) */
-
- /*
- * Main entry point. Note that despite common usage where main is
- * essentially of type void, we declare it explicitly to return
- * an int, and actually return a value. In most implementations,
- * the value returned from main is the exit status of the program.
- * Whether this applies to Lattice C or not, I'm not sure yet.
- */
-
- int main (argc, argv)
- int argc;
- char *argv[];
- {
- DBUG_ENTER ("main");
- ENABLE_ABORT;
- ParseCommandLine (argc, argv);
- MakeObjects ();
- if (!cflag && !Pflag && !Sflag && ErrCount == 0) {
- Link ();
- CleanObjects ();
- }
- DBUG_RETURN (0);
- }
-
- /*
- * The following macro is used to allow optional whitespace between
- * an option and it's argument. Argp is left pointing at the option
- * and argv and argc are adjusted accordingly if necessary.
- *
- * Note that there is no check for missing option arguments. In
- * particular, -o -V will blindly take -V as the output file name.
- *
- */
-
- #define XARG(argc,argv,argp) {if(*++argp==EOS){argp=(*argv++);argc--;}}
-
- static void ParseCommandLine (argc, argv)
- int argc;
- char **argv;
- {
- register char *argp;
-
- DBUG_ENTER ("ParseCommandLine");
- argc--;
- argv++;
- while (argc-- > 0) {
- CHECK_ABORT;
- argp = *argv++;
- if (*argp != '-') {
- AddOperandToList (argp);
- } else {
- switch (*++argp) {
- case '#':
- XARG (argc, argv, argp);
- DBUG_PUSH (argp);
- break;
- case 'c':
- cflag++;
- break;
- case 'D':
- XARG (argc, argv, argp);
- if (NDefines >= MAXDEFINES) {
- Fatal ("too many -D args (%d max)", MAXDEFINES);
- }
- Defines[NDefines++] = argp;
- break;
- case 'E':
- Warning ("-E unimplemented, converted to -P instead");
- Pflag++;
- break;
- case 'f':
- break; /* NOP for now, just eat it */
- case 'g':
- break; /* NOP for now, just eat it */
- case 'H':
- Hflag++;
- break;
- case 'I':
- XARG (argc, argv, argp);
- if (NUserInc >= MAXINCDIRS) {
- Fatal ("too many -I args (%d max)", MAXINCDIRS);
- }
- UserInc[NUserInc++] = argp;
- break;
- case 'l':
- XARG (argc, argv, argp);
- if (NLibs > MAXLIBS) {
- Fatal ("too many -l args (%d max)", MAXLIBS);
- }
- Libs[NLibs++] = argp;
- break;
- case 'O':
- break; /* NOP for now, just eat it */
- case 'o':
- XARG (argc, argv, argp);
- outfile = argp;
- break;
- case 'P':
- Pflag++;
- break;
- case 'q': /* Warning, non-standard */
- XARG (argc, argv, argp);
- QuadDev = argp;
- break;
- case 'S':
- Sflag++;
- break;
- case 's':
- break; /* NOP for now, just eat it */
- case 'U':
- XARG (argc, argv, argp);
- if (NUnDefines >= MAXUNDEFINES) {
- Fatal ("too many -U args (%d max)", MAXUNDEFINES);
- }
- UnDefines[NUnDefines++] = argp;
- break;
- case 'V':
- Vflag++;
- break;
- default:
- Warning ("unknown option '%c'", (char *) *argp);
- break;
- }
- }
- }
- DBUG_VOID_RETURN;
- }
-
- /*
- * For each operand, do compilation or assembly as necessary, to
- * reduce to an object file in the current directory.
- */
-
- static void MakeObjects ()
- {
- register int index;
- register struct Operand *op;
- auto char buffer[ARGSIZE];
-
- DBUG_ENTER ("MakeObjects");
- for (index = 0; index < NOperands; index++) {
- CHECK_ABORT;
- op = &Operands[index];
- if (NOperands > 1 && (CFILE (op) || SFILE (op))) {
- printf ("%s.%s:\n", op -> Rootname, op -> Suffix);
- }
- if (CFILE (op)) {
- Preprocess (op);
- Compile (op);
- sprintf (buffer, "%s%s.c", QuadDev, TEMPNAME);
- if (!DeleteFile (buffer)) {
- Warning ("can't delete '%s'", buffer);
- }
- if (!Sflag) {
- Assemble (op);
- sprintf (buffer, "%s%s.s", QuadDev, op -> Basename);
- if (!DeleteFile (buffer)) {
- Warning ("can't delete '%s'", buffer);
- }
- }
- } else if (SFILE (op)) {
- Assemble (op);
- }
- }
- DBUG_VOID_RETURN;
- }
-
- /*
- * Note that commands to cc of the form "-l<name>" get interpreted
- * to mean use a library called "name.lib" from the library
- * directory.
- */
-
- static void Link ()
- {
- register int index;
- register struct Operand *op;
- register char *name;
- auto char buffer[ARGSIZE];
-
- DBUG_ENTER ("Link");
- InitCommand ();
- AddToCommand ("%s", Locate ("ln", BinDirs));
- AddToCommand (" -o %s", outfile);
- for (index = 0; index < NOperands; index++) {
- op = &Operands[index];
- if (OFILE (op)) {
- name = op -> Rootname;
- } else {
- name = op -> Basename;
- }
- AddToCommand (" %s.o", name);
- }
- for (index = 0; index < NLibs; index++) {
- if (!Hflag) {
- sprintf (buffer, "%s32.lib", Libs[index]);
- } else {
- sprintf (buffer, "%s.lib", Libs[index]);
- }
- AddToCommand (" %s", Locate (buffer, LibDirs));
- }
- if (!Hflag) {
- AddToCommand (" %s", Locate ("c32.lib", LibDirs));
- } else {
- AddToCommand (" %s", Locate ("c.lib", LibDirs));
- }
- (void) RunCommand ();
- DBUG_VOID_RETURN;
- }
-
- /*VARARGS1*/
- static void Warning (fmt, arg1, arg2, arg3)
- char *fmt;
- char *arg1;
- char *arg2;
- char *arg3;
- {
- fprintf (stderr, "cc -- warning: ");
- fprintf (stderr, fmt, arg1, arg2, arg3);
- fprintf (stderr, "\n");
- (void) fflush (stderr);
- }
-
- /*VARARGS1*/
- static void Fatal (fmt, arg1, arg2, arg3)
- char *fmt;
- char *arg1;
- char *arg2;
- char *arg3;
- {
- fprintf (stderr, "cc -- fatal error: ");
- fprintf (stderr, fmt, arg1, arg2, arg3);
- fprintf (stderr, "\n");
- (void) fflush (stderr);
- exit (1);
- }
-
- /*
- * Split an operand name into rootname, basename, and suffix
- * components. The rootname is the full name, minus any suffix,
- * but including any prefix. The basename is the rootname minus
- * any prefix. The suffix is anything after the last '.' character.
- * Only the suffix is allowed to be the null string.
- */
-
- static void AddOperandToList (filename)
- char *filename;
- {
- register char *split;
- register struct Operand *op;
- extern char *strrchr ();
-
- DBUG_ENTER ("AddOperandToList");
- DBUG_3 ("ops", "add file '%s' to operand list", filename);
- if (NOperands >= MAXOPERANDS) {
- Fatal ("too many files (%d max)\n", MAXOPERANDS);
- }
- op = &Operands[NOperands];
- op -> Rootname = filename;
- if ((split = strrchr (filename, '/')) == NULL) {
- split = strrchr (filename, ':');
- }
- if (split == NULL) {
- op -> Basename = filename;
- } else {
- op -> Basename = ++split;
- }
- if ((split = strrchr (filename, '.')) == NULL) {
- op -> Suffix = "";
- } else {
- *split++ = EOS;
- op -> Suffix = split;
- }
- DBUG_3 ("ops", "rootname '%s'", op -> Rootname);
- DBUG_3 ("ops", "basename '%s'", op -> Basename);
- DBUG_3 ("ops", "suffix '%s'", op -> Suffix);
- NOperands++;
- DBUG_VOID_RETURN;
- }
-
- /*
- * Compile one operand from a C source program to an object module.
- */
-
- static void Compile (op)
- struct Operand *op;
- {
- DBUG_ENTER ("Compile");
- Pass1 (op);
- CHECK_ABORT;
- DBUG_VOID_RETURN;
- }
-
- /*
- * Note that because of brain-damage in the fact that -p to lc1 removes
- * all predefined defs, we must add them so replacing -c with -P in the
- * cc command line will result in the same set of predefined symbols.
- * This is rather ugly and leaves a hole for future problems if we
- * get out of sync with respect to what names the compiler predefines.
- */
-
- static int Pass1 (op)
- register struct Operand *op;
- {
- register int status;
- register int index;
-
- DBUG_ENTER ("Pass1");
- InitCommand ();
- AddToCommand ("%s", Locate ("ccom", LibDirs));
- AddToCommand (" -B");
- AddToCommand (" -L%d", LOCALSYMTABSIZE);
- AddToCommand (" -E%d", EXPRTABSIZE);
- AddToCommand (" -Y%d", CASETABSIZE);
- AddToCommand (" -Z%d", STRTABSIZE);
- if (!Hflag) {
- AddToCommand (" +L");
- }
- if (Sflag) {
- AddToCommand (" -A -T -o %s.s", op -> Basename);
- } else {
- AddToCommand (" -A -o %s%s.s", QuadDev, op -> Basename);
- }
- AddToCommand (" %s%s.c", QuadDev, TEMPNAME);
- status = RunCommand ();
- DBUG_RETURN (status);
- }
-
- static int Preprocess (op)
- register struct Operand *op;
- {
- register int status;
- register int index;
-
- DBUG_ENTER ("Preprocess");
- InitCommand ();
- AddToCommand ("%s", Locate ("cpp", LibDirs));
- AddToCommand (" %s", predefines);
- for (index = 0; index <NUserInc; index++) {
- AddToCommand (" -I%s", UserInc[index]);
- }
- for (index = 0; index <NUnDefines; index++) {
- /*************************
- AddToCommand (" -u%s", UnDefines[index]);
- **************************/
- Warning ("-U%s ignored! (unimplemented)", UnDefines[index]);
- }
- for (index = 0; index <NDefines; index++) {
- AddToCommand (" -D%s", Defines[index]);
- }
- AddToCommand (" -I%s", Locate ("include", Devices));
- AddToCommand (" %s.%s", op -> Rootname, op -> Suffix);
- AddToCommand (" %s%s.c", QuadDev, TEMPNAME);
- status = RunCommand ();
- DBUG_RETURN (status);
- }
-
- /*
- * I have not yet had occasion to use the macro assembler, so this
- * part is not yet implemented. If anyone wants to send me the
- * appropriate code, I will be glad to install it.
- */
-
- static int Assemble (op)
- struct Operand *op;
- {
- register int status;
- register int index;
-
- DBUG_ENTER ("Assemble");
- InitCommand ();
- AddToCommand ("%s", Locate ("as", BinDirs));
- AddToCommand (" -o %s.o", op -> Basename);
- if (CFILE (op)) {
- AddToCommand (" %s%s.s", QuadDev, op -> Basename);
- } else {
- AddToCommand (" %s.%s", op -> Rootname, op -> Suffix);
- }
- status = RunCommand ();
- DBUG_RETURN (status);
- }
-
- /*
- * As far as I can tell, the child status is not returned, only
- * whether or not the child could be run. So, how do we find out
- * whether there was an error or not? It's probably in the manuals
- * somewhere, I just haven't had time to dig yet.
- *
- * Note that because Lattice printf is not capable of printing more
- * than 200 characters at a time, we must spit them out one at a time
- * to make sure the entire command line gets printed when -V is used.
- *
- */
-
- static int RunCommand ()
- {
- int status;
- register char *cmdp;
-
- DBUG_ENTER ("RunCommand");
- DBUG_3 ("cmd", "execute '%s'", Command);
- if (Vflag) {
- for (cmdp = Command; *cmdp != EOS; cmdp++) {
- putchar (*cmdp); /* see above */
- }
- putchar ('\n');
- (void) fflush (stdout);
- }
- CHECK_ABORT;
- status = system (Command);
- DBUG_3 ("sys", "subcommand returns status %d", status);
- if (!status) {
- ErrCount++;
- }
- DBUG_RETURN (status);
- }
-
- /*
- * Look through the list of paths pointed to by "vec" until we find
- * a file with name given pointed to by "namep". If none is found,
- * the name pointed to by namep is returned.
- */
-
- static char *Locate (namep, vec)
- char *namep;
- char **vec;
- {
- static char namebuf[ARGSIZE];
-
- DBUG_ENTER ("Locate");
- while (*vec != NULL) {
- (void) sprintf (namebuf, "%s%s", *vec, namep);
- DBUG_3 ("try", "look for '%s'", namebuf);
- if (Readable (namebuf)) {
- namep = namebuf;
- break;
- }
- vec++;
- }
- DBUG_RETURN (namep);
- }
-
- /*
- * Check to see if the file exists and is readable.
- */
-
- #ifdef unix
- # include <fcntl.h>
- #else
- # include <libraries/dos.h>
- #endif
-
- static int Readable (name)
- char *name;
- {
- register int status = 0;
- register int fildes;
-
- DBUG_ENTER ("Readable");
- #ifdef unix
- fildes = open (name, O_RDONLY);
- if (fildes >= 0) {
- (void) close (fildes);
- status = 1;
- }
- #else
- fildes = Lock (name, ACCESS_READ);
- if (fildes != 0) {
- UnLock (fildes);
- status = 1;
- }
- #endif
- DBUG_RETURN (status);
- }
-
- /*
- * Do explicit check for abort. When Enable_Abort is non-zero,
- * Chk_Abort() cause program termination if CNTRL-C or CNTRL-D has
- * been received. Thus, we temporarily set it back to zero while we
- * do the explicit test, so we can do our own clean up and exit.
- * Note that if the -V flag was used, we spit out a confirming message
- * that we are quitting.
- *
- * Since we previously set Check_Abort to non-zero, this routine may be
- * overkill.
- */
-
- #ifdef amiga
- static void Check_Abort ()
- {
- extern int Chk_Abort ();
-
- DBUG_ENTER ("Check_Abort");
- DBUG_2 ("abort", "do explicit test for CNTRL-C");
- DISABLE_ABORT;
- if (Chk_Abort () != 0) {
- if (Vflag) {
- printf ("cc - terminated by request\n");
- }
- exit (1);
- }
- ENABLE_ABORT;
- DBUG_VOID_RETURN;
- }
- #endif
-
- /*
- * Initialize the command line buffer and associated variables to
- * discard any previous command line.
- */
-
- static void InitCommand ()
- {
- Command[0] = EOS;
- EndCommand = Command;
- }
-
- /*
- * Build string to add to end of current command line, checking
- * for overflow in the command buffer and maintaining the pointer
- * to the end of the current command.
- *
- * Note that we are a "printf type" of command, and can be called
- * with up to three "char *" arguments. There is a portability
- * problem here, but Lattice hasn't yet made "varargs" a standard
- * part of their distribution.
- *
- * Also, note that the return argument of sprintf is supposed to be
- * the number of characters to be added to the buffer. This is
- * not always true for some C implementations. In particular,
- * sprintf in BSD4.1 returns a pointer. Thus we don't use the
- * return argument.
- *
- */
-
- /*VARARGS1*/
- static void AddToCommand (fmt, arg1, arg2, arg3)
- char *fmt;
- char *arg1, *arg2, *arg3;
- {
- register int length;
- auto char buffer[ARGSIZE];
-
- (void) sprintf (buffer, fmt, arg1, arg2, arg3);
- length = strlen (buffer);
- if ((EndCommand - Command) + length >= sizeof (Command)) {
- Fatal ("command line too long (%d char max)", sizeof (Command));
- } else {
- (void) strcat (EndCommand, buffer);
- EndCommand += length;
- }
- }
-
- /*
- * If an executable is made from a single C file, the normal behavior
- * for the unix environment is to treat the .o file as an intermediate
- * file and remove it, so we follow suit.
- */
-
- static void CleanObjects ()
- {
- auto char buffer[ARGSIZE];
- register struct Operand *op;
-
- DBUG_ENTER ("CleanObjects");
- if (NOperands == 1) {
- op = &Operands[0];
- if (CFILE (op) || SFILE (op)) {
- sprintf (buffer, "%s.o", op -> Basename);
- if (!DeleteFile (buffer)) {
- Warning ("can't delete '%s'", buffer);
- }
- }
- }
- DBUG_VOID_RETURN;
- }
-
- #if defined(amiga) && defined(manx) /* In Lattice C lib, but not Manx's */
- char *strrchr (s, c)
- char *s;
- char c;
- {
- register char *scan = s;
-
- DBUG_ENTER ("strrchr");
- while (*scan++ != EOS) {;}
- while (scan > s && *(--scan) != c) {;}
- DBUG_RETURN (*scan == c ? scan : NULL);
- }
- #endif /* amiga && manx */
-