/****** utilities/rcsrev *****************************************************

    NAME  
        RCSRev - Convert a RCS ID into #?_rev.? Files

    VERSION
        $Id: rcsrev.c,v 2.3 1994/03/08 00:36:12 ppessi Exp $      

    TEMPLATE       
        RCSREV NAME/A SOURCE/A POSTFIX ASM=ASMINCLUDE/S DATE/K
 
    FUNCTION
        RCSRev is intended to generate a BumbRev-compatible revision
        include files from RCS ID. 

        The arguments are used as follows:

        NAME/A       - name for program (generate file NAME_rev.h).
        SOURCE/A     - source file name
        PREFIX/K     - optional prefix to program name (eg. AmiTCP/IP_)
        POSTFIX/K    - optional string postfixed to revision string
        ASMINCLUDE/S - create also an assembler include file (generate file
                       NAME_rev.i).

        DATE/K       - specify the date format used.  The date formats
                       available are as follows:
            CURRENT   - use current date (default) 
            RCS       - use date from RCS ID
            SASC      - use preprocessor macro __AMIGADATE__
            DICE      - use preprocessor macro __COMMODORE_DATE__

    NOTES
        The macro DATE is not defined in the revision file with SASC or
        COMMODORE date formats.  The macro VSTRING is not defined in
        revision file with the SASC format.  (See sources and macro
        FIXED_AMIGADATE).

        The SASC or DICE formats can not be used with the ASMINCLUDE option.

    BUGS
        RCSRev doesn't recognize other RCS ident string except Id.

        Maximum allowed line length is fixed.

    AUTHOR
        ppessi <Pekka.Pessi@hut.fi>
 
    COPYRIGHT 
        Copyright © 1993 AmiTCP/IP Group, <AmiTCP-group@hut.fi>, 
        Helsinki University of Technology, Finland.

*****************************************************************************
*
*/

#include <proto/dos.h>

#include <clib/exec_protos.h>
extern struct ExecBase* SysBase;
#include <pragmas/exec_sysbase_pragmas.h>
#include <exec/memory.h>

#include <dos/dos.h>

#include <string.h>
#include <stdlib.h>

#ifndef MAXLINELENGTH 
#define MAXLINELENGTH 1024
#endif

#ifndef MAXPATHLEN 
#define MAXPATHLEN 1024
#endif

#ifndef BOOTSTRAP
#include "rcsrev_rev.h"

static const char version[] = VERSTAG;
#endif

static const char copyright[] =
  "Copyright © 1994 Pekka Pessi\n";

static char *strsep(register char **stringp, register const char *delim);

LONG rcsrev(void)
{
  LONG retval = 128;
  struct ExecBase *SysBase = *(struct ExecBase**)4;
  struct DosLibrary *DOSBase = (struct DosLibrary *)
    OpenLibrary("dos.library", 37L);
  STRPTR buffer = AllocVec(MAXLINELENGTH + 1, MEMF_PUBLIC);
  STRPTR fnbuf = AllocVec(MAXPATHLEN + 1, MEMF_PUBLIC);
  
  if (DOSBase && buffer) {
    const char *template = 
      "Name/A,Source/A,Prefix/K,Postfix/K,ASM=ASMINCLUDE/S,DATE/K";
    struct {
      UBYTE *a_name;
      UBYTE *a_file;
      UBYTE *a_prefix;
      UBYTE *a_postfix;
      LONG   a_ifile;
      UBYTE *a_date;
    } args[1] = { 0 };
    struct RDArgs *rdargs = NULL;
    UBYTE date[LEN_DATSTRING];
    struct DateTime dt[1];
    UBYTE *year, *month, *day;

    retval = RETURN_ERROR;

    /* Get current date */
    bzero(dt, sizeof(*dt));
    dt->dat_Format = FORMAT_CDN;
    dt->dat_StrDate = date;
    (void)DateStamp(&dt->dat_Stamp);
    (void)DateToStr(dt);

    day = date; month = date + 3; year = date + 6;
    date[2] = '\0';
    date[5] = '\0';

    if (rdargs = ReadArgs((UBYTE *)template, (LONG *)args, NULL)) {
      enum { 
	err_e = -1, current_e = 0, rcs_e, sasc_e, dice_e,
      } datefmt = current_e;
      BPTR infile; 
      LONG line = 0;

      buffer[MAXLINELENGTH] = '\0';

      /* Get the date format to be used */
      if (args->a_date) {
	datefmt = FindArg("CURRENT,RCS,SASC,DICE", args->a_date);
	if (args->a_ifile && (datefmt == sasc_e || datefmt == dice_e))
	  datefmt = err_e;
      }

      if (datefmt != err_e && (infile = Open(args->a_file, MODE_OLDFILE))) { 
	while (FGets(infile, buffer, MAXLINELENGTH)) {
	  UBYTE *s = buffer;
	  line++;

	  while (s = strchr(s, '$')) {
	    /*
	     * RCS Id consists of
	     * "¤Id: file,v major.minor mm/dd/yy time author state [locker] ¤"
	     */
	    if (strncmp("$" "Id: ", s, 5) == 0) {
	      /*
	       * Probably parsing should be done with sscanf()
	       */
	      UBYTE *sp = s + 5;
	      UBYTE *path = strsep(&sp, " ");
	      UBYTE *ver = strsep(&sp, ".");
	      UBYTE *rev = strsep(&sp, " ");
	      UBYTE *rcs_year = strsep(&sp, "/");
	      UBYTE *rcs_month = strsep(&sp, "/");
	      UBYTE *rcs_day = strsep(&sp, " ");
	      UBYTE *rcs_time = strsep(&sp, " ");
	      UBYTE *datemacro =
		datefmt == sasc_e ? (UBYTE *)"__AMIGADATE__" :
		  (datefmt == dice_e ? (UBYTE *)"__COMMODORE_DATE__" : 
		   (UBYTE *)NULL);
	      UBYTE *postfix = args->a_postfix ? args->a_postfix : (UBYTE *)"";

	      if (path && ver && rev && 
		  rcs_year && rcs_month && rcs_day && rcs_time) {
		BPTR hfile = 0;

		if (datefmt == rcs_e) {
		  year  = rcs_year;
		  month = rcs_month;
		  day   = rcs_day;
		}

		if (strlen(year) == 4)
		  year += 2;
		if (month[0] == '0')
		  month++;
		if (day[0] == '0')
		  day++;

		strncpy(fnbuf, args->a_name, MAXPATHLEN);
		strncat(fnbuf, "_rev.h", MAXPATHLEN);

		if (hfile = Open(fnbuf, MODE_NEWFILE)) {
		  FPrintf(hfile, "#define\tVERSION\t\t%s\n", ver);
		  FPrintf(hfile, "#define\tREVISION\t%s\n", rev);
		  if (!datemacro) {
		    FPrintf(hfile, "#define\tDATE\t\"%s.%s.%s\"\n", 
			    day, month, year);
		  }
		  FPrintf(hfile, "#define\tVERS\t\"%s%s %s.%s\"\n", 
			  args->a_prefix ? args->a_prefix : "", 
			  args->a_name, ver, rev);
#ifndef FIXED_AMIGADATE
		  if (datefmt != sasc_e)
#endif
		    {
		      if (datemacro) 
			FPrintf(hfile, "#define\tVSTRING\t\"%s%s %s.%s \""
				" %s \"\\n\\r\"\n", 
				args->a_prefix ? args->a_prefix : "", 
				args->a_name, ver, rev, datemacro);
		      else
			FPrintf(hfile, "#define\tVSTRING\t\"%s%s %s.%s "
				"(%s.%s.%s)\\n\\r\"\n", 
				args->a_prefix ? args->a_prefix : "", 
				args->a_name, ver, rev, day, month, year);
		    }
		  if (datemacro) 
		    FPrintf(hfile, "#define\tVERSTAG\t"
			    "\"\\0$VER: %s%s %s.%s%s \""
			    " %s\n", 
			    args->a_prefix ? args->a_prefix : "", 
			    args->a_name, ver, rev, postfix, datemacro);
		  else
		    FPrintf(hfile, "#define\tVERSTAG\t"
			    "\"\\0$VER: %s%s %s.%s%s "
			    "(%s.%s.%s)\"\n", 
			    args->a_prefix ? args->a_prefix : "", 
			    args->a_name, ver, rev, postfix, day, month, year);
		  Close(hfile);
		} else {
		  PrintFault(IoErr(), fnbuf);
		  goto found;
		}

		if (args->a_ifile) {
		  strncpy(fnbuf, args->a_name, MAXPATHLEN);
		  strncat(fnbuf, "_rev.i", MAXPATHLEN);

		  if (hfile = Open(fnbuf, MODE_NEWFILE)) {
		    FPrintf(hfile, "VERSION \tEQU\t%s\n", ver);
		    FPrintf(hfile, "REVISION\tEQU\t%s\n", rev);
		    FPrintf(hfile, 
			    "DATE\tMACRO\n"
			    "\t\tdc.b\t'%s.%s.%s'\n"
			    "\tENDM\n", 
			    day, month, year);
		    FPrintf(hfile, 
			    "VERS\tMACRO\n"
			    "\t\tdc.b\t'%s%s %s.%s'\n"
			    "\tENDM\n",
			    args->a_prefix ? args->a_prefix : "", 
			    args->a_name, ver, rev);
		    FPrintf(hfile,
			    "VSTRING\tMACRO\n"
			    "\t\tdc.b\t'%s%s %s.%s (%s.%s.%s)',13,10,0\n"
			    "\tENDM\n", 
			    args->a_prefix ? args->a_prefix : "", 
			    args->a_name, ver, rev, day, month, year);
		    FPrintf(hfile, 
			    "VERSTAG\tMACRO\n"
			    "\t\tdc.b\t0,'$VER: %s%s %s.%s%s (%s.%s.%s)',0\n"
			    "\tENDM\n", 
			    args->a_prefix ? args->a_prefix : "", 
			    args->a_name, ver, rev, postfix, day, month, year);
		    
		    Close(hfile);
		    retval = RETURN_OK;
		  } else {
		    PrintFault(IoErr(), fnbuf);
		  }
		} else {
		  retval = RETURN_OK;
		}
	      } else {
		Printf("%s:%ld: parse error with RCS $" "Id" "$\n",
		       args->a_file, line);
	      }

	      goto found;
	    } else {
	      s = s + 1;
	    }
	  }
	}
	Printf("%s: can not found RCS \"$" "Id" "$\"\n", args->a_file);

      found:
	Close(infile);
      } else {
	if (datefmt == err_e) {
	  PrintFault(ERROR_OBJECT_WRONG_TYPE, "RCSRev DATE");
	} else {
	  PrintFault(IoErr(), args->a_file);
	}
      }
    } else {
      PrintFault(IoErr(), "rcsrev");
      retval = RETURN_ERROR;
    } 
  }

  if (buffer) {
    FreeVec(buffer);
  } 
  if (fnbuf) {
    FreeVec(fnbuf);
  }
  if (!buffer || !fnbuf) {
    PrintFault(ERROR_NO_FREE_STORE, "rcsrev");
  }

  if (DOSBase)
    CloseLibrary((struct Library *)DOSBase);

  return retval;
}

/*
 * Get next token from string *stringp, where tokens are nonempty
 * strings separated by characters from delim.  
 *
 * Writes NULs into the string at *stringp to end tokens.
 * delim need not remain constant from call to call.
 * On return, *stringp points past the last NUL written (if there might
 * be further tokens), or is NULL (if there are definitely no more tokens).
 *
 * If *stringp is NULL, strtoken returns NULL.
 */
static char *strsep(register char **stringp, register const char *delim)
{
  register char *s;
  register const char *spanp;
  register int c, sc;
  char *tok;

  if ((s = *stringp) == NULL)
    return (NULL);
  for (tok = s;;) {
    c = *s++;
    spanp = delim;
    do {
      if ((sc = *spanp++) == c) {
	if (c == 0)
	  s = NULL;
	else
	  s[-1] = 0;
	*stringp = s;
	return (tok);
      }
    } while (sc != 0);
  }
}
