/****************************************************/
/*                                                  */
/* Copyright (c) 1993 SAS Institute, Inc.           */
/*                                                  */
/* Support: walker                                  */
/* Script:  splatam                                 */
/* Date:    07/12/93                                */
/*                                                  */
/****************************************************/

#include "MKMK.h"

#define TEMPLATE "TARGET/K,FORCE/S,MAKEFILE/K,PATTERN/M"

#define OPT_TARGET   0
#define OPT_FORCE    1
#define OPT_MAKEFILE 2
#define OPT_PATTERN  3

#define OPT_COUNT    4

BPTR out;         // Location for output messages to go to
char *makefile;   // Name of makefile to generate
char *target;     // Name of program generated by the makefile
int force;        // Overwrite existing makefile?

// This routine is called when we have an emergency exit for some reason
// It simply prints a message and terminates the program
void panic(char *msg)
{
   if(msg && out)
   {
      Write(out, msg, strlen(msg));
      Write(out, "\n", 1);
   }
   if(out && Output() == NULL) Close(out);
   out = NULL;
   __exit(20);
}

// This routine is called after parsing the arguments.  If optional parameters
// were omitted, it fills them in with default values.
static void GetDefaultArgs(long *opts)
{
   static const char *statargv[] = 
   {"#?.c", "#?.cxx", "#?.cpp", "#?.cc", "#?.a", NULL};
   makefile = (opts[OPT_MAKEFILE] ? (char *)opts[OPT_MAKEFILE] : "smakefile");
   target   = (char *)opts[OPT_TARGET];
   force    = opts[OPT_FORCE];
   if(opts[OPT_PATTERN] == NULL)
      opts[OPT_PATTERN]  = (long)statargv;
}

// Add all files matching the pattern "pattern" to the FileList "fl"
void AddPattern(FileList *fl, char *pattern)
{
   struct FileInfoBlock __aligned fib;

   if(dfind(&fib, pattern, 0)) return;

   do
   {
      AddFile(fl, scopy(fib.fib_FileName));
   }
   while(!dnext(&fib));
}

// Find a file in a FileList
struct FileDesc *FindFile(FileList *fl, char *file)
{
   struct FileDesc *fd;
   for(fd=fl->head; fd && stricmp(file, fd->name); fd=fd->next);
   return fd;
}

// Add a single file (not a pattern) to a FileList
struct FileDesc *AddFile(FileList *fl, char *file)
{
   struct FileDesc *fd;

   // Check to make sure it's not already on the list
   if(fd = FindFile(fl, file)) return fd;

   if(!(fd = malloc(sizeof(*fd)))) panic("No memory!\n");

   memset(fd, 0, sizeof(*fd));

   // Link the new FileDesc structure into the FileList
   fd->prev = fl->tail;
   if(fl->tail) fl->tail->next = fd;
   else         fl->head = fd;
   fl->tail = fd;

   fd->name = file;

   return fd;
}

// Return the descriptor for the next file in the FileList
struct FileDesc *NextFile(FileList *fl)
{
   if(fl->cur == NULL) return fl->cur = fl->head;
   else return fl->cur = fl->cur->next;
}

int main(void)
{
   struct FileList fl;
   struct FileDesc *file;
   long opts[OPT_COUNT];
   char **argptr;
   struct RDArgs *rdargs;

   memset(&fl, 0, sizeof(fl));
   memset(opts, 0, sizeof(opts));

   if(!(out=Output()))
   {
      // No default Output() window;  open one
      if(!(out = Open("CON:0/0/500/100/GENSMAKE/AUTO/CLOSE/WAIT", MODE_NEWFILE)))
         panic(NULL);
   }

   if(SysBase->LibNode.lib_Version < 37)
   {
      panic("Requires AmigaDOS version 2.04 or higher\n");
   }
   
   if(!(rdargs = ReadArgs(TEMPLATE, opts, NULL)))
   {
      // rdargs failed for some reason.  Use PrintFault to print the
      // reason, then quit.
      PrintFault(IoErr(), NULL);
      panic(NULL);
   }

   // If the user omitted some arguments, fill them in with default values
   GetDefaultArgs(opts);

   // Add all the files specified by the user's patterns
   for(argptr=(char **)opts[OPT_PATTERN]; *argptr; argptr++)
   {
      AddPattern(&fl, *argptr);
   }

   // Walk the file list, processing each file.  Note that during file processing, it is
   // possible that other files will be added to the end of the list.  (For example, if a
   // .c file on the list includes a .h file, we add the .h file to the end of the FileList).
   // Since they are added to the end, we stay in this loop until ALL files have been
   // processed, even the ones that weren't on the list when the loop started.
   for(file=NextFile(&fl); file; file=NextFile(&fl))
      DoFile(&fl, file);

   // We have now processed all source files, all files included by the source files, all
   // files included by those files, etc.  We have all the information we need to generate
   // the smakefile.
   GenSmake(&fl);

   // If we opened our own window previously, close it now.  It was opened with /WAIT, so
   // it won't actually close until the user selects the close gadget.
   if(out != Output()) Close(out);

   return 0;
}

// Use RawDoFmt to simulate fprintf()
int myfprintf(BPTR file, char *ctl, ...)
{
   char buffer[256];
   va_list args;

   va_start(args, ctl);

   // The string constant below is actually the hexadecimal value for two 
   // assembly-language instructions.  The two instructions implement the
   // necessary callback routine to copy the format characters into the buffer
   RawDoFmt(ctl, args, (void (*))"\x16\xc0\x4e\x75", buffer);

   va_end(args);

   FWrite(file, buffer, strlen(buffer), 1);

   return 0;
}


// Simulate sprintf using RawDoFmt
int mysprintf(char *buffer, char *ctl, ...)
{
   va_list args;

   va_start(args, ctl);

   RawDoFmt(ctl, args, (void (*))"\x16\xc0\x4e\x75", buffer);

   va_end(args);

   return 0;
}


