/****************************************************/
/*                                                  */
/* Copyright (c) 1993 SAS Institute, Inc.           */
/*                                                  */
/* Support: walker                                  */
/* Script:  mkmkam                                  */
/* Date:    07/12/93                                */
/*                                                  */
/****************************************************/
#include "MKMK.h"
#include "time.h"

#define MAXLINELEN 77   // Maximum line size (not counting backslash)

static const char verstring[] = "$VER: MKMK " TOOLVER TOOLDATE;

/* File types */
#define FT_UNK -1  // Unknown
#define FT_C    0  // C source file
#define FT_CXX  1  // C++ source file
#define FT_ASM  2  // Assembler source file
#define FT_H    3  // Header file

static const struct SUFFIXES
{
   char suffix[5];
   char val;
} suffixes[] =
{
   {".a",   FT_ASM},
   {".asm", FT_ASM},
   {".c",   FT_C},
   {".cc",  FT_CXX},
   {".cpp", FT_CXX},
   {".cxx", FT_CXX},
   {".h",   FT_H},
   {".s",   FT_ASM},
};
#define NUMSUFFIXES (sizeof(suffixes)/sizeof(struct SUFFIXES))

/* return the file type (as defined above) for a given source file */
int filetype(char *name)
{
   int i, j;
   name = suffix(name);

   for(i=0; i<NUMSUFFIXES; i++)
   {
      j = stricmp(name, (char *)suffixes[i].suffix);
      if(j == 0) return suffixes[i].val;
   }
   return FT_UNK;
}

/* Return the suffix (portion after the '.') for a source file */
/* Note that no memory is allocated, the return pointer points */
/* =into= the argument string.                                 */
char *suffix(char *s)
{
   char *p;
   for(p=s+strlen(s); p>s && *p != '.'; p--);
   return p;
}

/* Return the prefix (portion before the '.') for a source file. */
/* This function DOES allocate memory to copy the prefix into.   */
char *prefix(char *s)
{
   char c;
   char *p = suffix(s);
   if(p != s)
   {
      c = *p;
      *p = 0;
      s = scopy(s);
      *p = c;
      return s;
   }
   else
      return scopy("");
}

/* Take a filename as input, and return the object filename */
/* that would correspond to it.  I.E. take "foo.c" as input */
/* and put "foo.o" out.  Note that this functions DOES      */
/* allocate a new copy of the string.                       */
char *objout(char *s)
{
   char *p;
   char *t;
   long l;
   p = suffix(s);
   if(*p != '.') return NULL;
   t = malloc((unsigned long)p-(unsigned long)s+3);
   if(!t) panic("No Memory!");
   l = (unsigned long)p - (unsigned long)s;
   memcpy(t,s,l);
   strcpy(t+l, ".o");
   return t;
}

/* Expand file dependancies */
/* We copy the dependancies of all our children into ourself. */
/* If the dependancy is already there, nothing will be added. */
/* Otherwise, it gets added onto the end and fd->ndirect gets */
/* bumped.  When we get there, that dependancy will also be   */
/* expanded.  The result is that all dependancies, no matter  */
/* how far removed, end up on the list for fd.                */

void ExpandDeps(struct FileDesc *fd)
{
   int i, j;
   for(i=0; i<fd->ndirect; i++)
   {
      for(j=0; j<fd->Direct[i]->ndirect; j++)
      {
         AddDep(fd, fd->Direct[i]->Direct[j]);
      }
   }
}

#define HEADER_COMMENT \
"#\n" \
"# Makefile automatically generated by MKMK V" TOOLVER "\n" \
"# %s" \
"#\n\n"

char header_comment[sizeof(HEADER_COMMENT)+26];

/* All data has been accumulated;  generate the smakefile */
void GenSmake(FileList *fl)
{
   BPTR fp;
   struct FileDesc *fd;
   char *p;
   int i, j, ft;
   int tmplen, curlen, indent;
   char filename[100];
   long t;
   time(&t);

   chkabort();

   strcpy(filename, makefile);

   if(!force)
   {
      for(i=0; i<100; i++)
      {
         fp = Lock(filename, SHARED_LOCK);
         UnLock(fp);
         if(!fp) break;
         mysprintf(filename, "%s.%02.2ld", makefile, i);
      }
      if(fp)
         panic("Can't determine name for output file!\n");
   }

   if(!(fp = Open(filename, MODE_NEWFILE)))
   {
      myfprintf(out, "Can't open output file \"%s\"!\n", filename);
      panic(NULL);
   }

   myfprintf(out, "Writing file \"%s\"...", filename);

   myfprintf(fp, HEADER_COMMENT, asctime(localtime(&t)));

   myfprintf(fp, "OBJS=");
   curlen = indent = 5;

   // Put out the list of object files
   for(fd=fl->head; fd; fd=fd->next)
   {
      ft = filetype(fd->name);
      if(ft == FT_C || ft == FT_CXX || ft == FT_ASM)
      {
         if(p = objout(fd->name))
         {
            tmplen = strlen(p) + 1;
            if(curlen + tmplen > MAXLINELEN)
            {
               // Over MAXLINELEN bytes, continue on the next line
               myfprintf(fp, " \\\n");
               for(j=0; j<indent; j++) FPutC(fp, ' ');
               curlen = indent + tmplen;
            }
            else
               curlen += tmplen;
            myfprintf(fp, " %s", p);
               
            /* The first file named will name the project */
            if(target == NULL) target = prefix(p);

            free(p);
         }
      }
      else if(ft == FT_UNK)
      {
         /* It's not a file that produces an object file. */
         /* we don't really know what it is, but add it   */
         /* to the dependancy list anyway.  It might be   */
         /* a library or .o file, for example.            */
         myfprintf(fp, " %s", fd->name);
      }
   }

   if(!target) target = "Project";

   /* Put out the linker command */
   myfprintf(fp, "\n\n%s: $(OBJS)\n   sc link to %s with <<\n$(OBJS)\n<\n\n",
      target, target);

   /* Put out the individual file dependancy lists */
   for(fd=fl->head; fd; fd=fd->next)
   {
      ft = filetype(fd->name);
      if(ft == FT_C || ft == FT_CXX || ft == FT_ASM)
      {
         if(!(p = objout(fd->name))) break;

         myfprintf(fp, "%s: %s", p, fd->name);
         indent = strlen(p) + 1;
         curlen = indent + strlen(fd->name) + 1;
         free(p);

         /* Expand the dependancies of the current file.  This will  */
         /* copy all second, third, .... nth level dependancies into */
         /* the current file descriptor.                             */
         ExpandDeps(fd);

         for(i=0; i<fd->ndirect; i++)
         {
            tmplen = strlen(fd->Direct[i]->name) + 1;
            if(curlen + tmplen > MAXLINELEN)
            {
               /* Over MAXLINELEN bytes, continue on a new line */
               myfprintf(fp, " \\\n");
               for(j=0; j<indent; j++) FPutC(fp, ' ');
               curlen = indent + tmplen;
            }
            else
               curlen += tmplen;
            myfprintf(fp, " %s", fd->Direct[i]->name);
         }
         myfprintf(fp, "\n");
      }
      myfprintf(fp, "\n");
   }

   Close(fp);   

   myfprintf(out, "Done\n");

   makeicon(filename);
}

#define DEFROOT "sc:icons/def_"

//Copy the correct .info file for a given filename
void makeicon(char *name)
{
   int i, len;
   char *xname;
   struct Library *IconBase;
   char data[300];
   struct DiskObject *dobj;

   if(!(IconBase = OpenLibrary("icon.library",0)))
      return;

   len = strlen(name);
   if(len > sizeof(data)-1) return;
   memcpy(data, name, len+1);

   if(iconexists(data)) goto doexit;

   for(i=len-1; i>=0; i--)
      if(name[i] == '.' || name[i] == '/' || name[i] == ':')
         break;

   /* If no '.', i is -1, which means use the whole name */

   xname = name + (i+1);

   // xname now points to the extension
   if(!stricmp(xname, "info"))
      goto doexit;  // Don't do icons for .info files!

   strcpy(data, DEFROOT);
   strcpy(data + strlen(DEFROOT), xname);

   if(!iconexists(data))
      strcpy(data, "sc:icons/def_smakefile");

   if(!(dobj = GetDiskObject(data))) goto doexit;  // hmm... bad icon?
   PutDiskObject(name, dobj);
   FreeDiskObject(dobj);
doexit:
   CloseLibrary(IconBase);
   return;
}

// Determine if an icon already exists for the name
int iconexists(char *name)
{
   BPTR lock;
   char fullname[256];
   int len;

   len = strlen(name);
   if(len>sizeof(fullname)-6) return(-1);
   memcpy(fullname, name, len);
   memcpy(fullname+len, ".info", 6);

   if(lock=Lock(fullname, SHARED_LOCK)) UnLock(lock);

   return(lock != NULL);
}
