/*
   ConvertCharSet   -- ein Programm zum Konvertieren von Zeichensätzen

      entstanden aus

   Arguments  -- eine CLI-Hülle für Programme ab Workbench 2.04
 */

/*
 *  allgemeine Include-Dateien
 */

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

/*
 *  spezielle Amiga-Include-Dateien
 */

#include <dos/dos.h>
#include <dos/dosasl.h>
#include <dos/rdargs.h>
#include <exec/memory.h>
#include <exec/types.h>
#include <exec/execbase.h>
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>

/*
 * Defines, die bei einer Kopie zu ändern sind
 */

#define PROGNAME "ConvertCharSet"     /* Default-Name des Programms     */
#define VERSION  "1.5"                /* Versions-Nummer 1.0 am Anfang! */
#define AUTHOR   "David Göhler"       /* Name des Autors                */
#define DATE     "05.08.94"           /* Datum der letzten Änderung     */
#define ENVVAR   PROGNAME             /* Name einer ENV-Variable        */

/*
 * dauerhafte define's, die kaum einer Änderung bedürfen
 */

#define NAMELEN  255                  /* Länge des Programmnamens       */
#define LINELEN  10000                /* Zeilenlänge im Makefile        */
#define BUFLEN   200                  /* Pufferlänge für Datei-Pattern  */

/*
 * Flags für GetNextFileName
 */

#define GNF_NODIR  1                  /* keine Verzeichnisse zurückgeben */
#define GNF_ALL    2                  /* auch Unterverzeichnisse */

/*
 * spezielle Defines nur für dieses Programm
 */

#define LF         0x0A               /* linefeed */
#define CR         0x0D               /* Carriage Return */

#define TABLETEMPLATE    "s:%s.tab"

/*
 * falls ein GCC zum Einsatz kommt, DICE-Spezialitäten abwürgen
 */

#ifdef __GNUC__
#define __stkargs                     /* kennt der GCC nicht            */
#endif

const char VersionID      []="$VER: " PROGNAME " " VERSION " - © " AUTHOR " (" DATE ")";
const char TemplateString []="FILES/A/M,TABLE/A,EXT=EXTENSION/K,DEST=DESTINATION/K,STRIPLF/S,STRIPCR/S,LF2CR/S,CR2LF/S,QUIET/S,NOENV/S";
enum       TemplateEnum     { FILES,TABLE,EXTENSION,DESTINATION,STRIPLF,STRIPCR,LF2CR,CR2LF,QUIET,NOENV };

BOOL    somethingfound = FALSE;

typedef struct {
        char *filenames;      /* Datei oder Pattern der umzuwandelnden Dateien */
        char *table;          /* Convertierungstabelle */
        char *extension;      /* Extension, die anzuhängen ist */
        char *destination;    /* Zielname, falls gewünscht */
        LONG striplf;
        LONG stripcr;
        LONG lf2cr;
        LONG cr2lf;
        LONG quiet;           /* keine Ausgaben, wenn TRUE */
        LONG noenv;           /* ENV-Variable nicht auswerten wenn TRUE */
} Template;                   /* Für die Kommandozeile */

/*
 * globale Variablen, die die Einstellungen per Kommandozeile darstellen
 */

char  EnvString [BUFLEN] = "";        /* für eine eventuelle Env-Variable */
char  progname  [NAMELEN]= PROGNAME;  /* Für Ausgaben bei Fehlermeldungen */
char  infile    [NAMELEN]= "";        /* Dateiname des Files zum Lesen    */
char  inext     [50]     = "";        /* Extension der in-Datei           */
char  outfile   [NAMELEN]= "";        /* Dateiname des Files zum Schreiben*/
char  outext    [50]     = "";        /* Extension für out-Datei          */

Template CommandLineVars = {          /* Template-Struktur für ReadArgs   */
     0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L
};

unsigned char tt[256];                /* Translate table space*/

char    TempBuf[BUFLEN];
struct  RDArgs  EnvArgs   = {
        {  EnvString, BUFLEN, 0 },
        0, TempBuf, BUFLEN,
        0L,           // Extra Help Text, currently zero
        RDAF_NOALLOC | RDAF_NOPROMPT
};

/*
 * externe Referenzen
 */

extern struct ExecBase *SysBase;   /* Für den Versionstest */

/*
 * Funktionen, die im folgenden erst Definiert werden
 */

char *GetNextFileName(Template *, LONG);
void initchars(char *TABLENAME, long lf2cr, long cr2lf, long quiet);
BOOL ConvertFile(char *SrcName,char *DestName,long striplf, long stripcr);
char *GetFileName(char *FileName,BOOL extension);

/*
 * main-Kopf, für GCC-Nutzer mit Extrawurst
 */

//__stkargs void _main()          /* hier beginnt das Programm */
int main(int argc, char *argv[])
{
   struct RDArgs *rda;              /* nicht notwendigerweise global */
   long           rc  = RETURN_OK;  /* Return-Code von main          */

   /* dies Programm läuft nur ab Version 2.04 */
   if (SysBase->LibNode.lib_Version < 37)
   {  Write(Output(),"Wrong kickstart version, must 37.175 or higher\n",47);
      _exit(RETURN_FAIL);
   }

   /* wie heiße ich denn ? */
   if (!GetProgramName(progname,NAMELEN-1))   /* bei Fehlversuch */
   {  strncpy(progname,PROGNAME,NAMELEN-1); } /* Default kopieren */


   if (CommandLineVars.noenv == FALSE)
   {  // vor der KommandoZeile die ENV-Variable aus ENVVAR auswerten
      if (GetVar(ENVVAR,EnvString,BUFLEN,0) > 0)
      {  // muß sein, möglicherweise ein Bug in ReadArgs
         strcat(EnvString," "); // kennt jemand dieses Problem ?

         EnvArgs.RDA_Source.CS_Length = strlen(EnvString);
         if (rda = ReadArgs(TemplateString,(LONG *) &CommandLineVars,&EnvArgs))
         {  FreeArgs(rda); }
      }
   }

   /* nun die Kommandozeilenauswertung erledigen - per Systemaufruf */
   if (rda = ReadArgs (TemplateString,(LONG *) &CommandLineVars,NULL))
   {
      char *FileName = 0L;           /* Zeigt auf einen Dateinamen */
      char tablename[50] = "";

      /*
       * Zuerst den Tabellennamen herausfinden.
       */
      
      sprintf(tablename,TABLETEMPLATE,CommandLineVars.table);
      initchars(tablename,CommandLineVars.lf2cr,
                          CommandLineVars.cr2lf,
                          CommandLineVars.quiet);

      /* CommandLineVars.filenames zeigt auf ein Array von StrPrtn */
      /* Diese werden mit einem NULL-Pointer abgeschlossen         */
      /* statt diese nun per Hand auszuwerten, rufe ich mit dem    */
      /* Zeiger auf CommandLineVars eine Funktion auf, die jeweils */
      /* bei einem Aufruf einen Zeiger auf einen String=Dateiname  */
      /* oder NULL zurückliefert. Im NULL-Fall ist per IoErr()     */
      /* auf einen Fehler zu prüfen.                               */


      while (FileName = GetNextFileName(&CommandLineVars,GNF_NODIR))
      {
        char *pos = 0L;
        
        somethingfound = TRUE;

        if (CommandLineVars.extension != 0L)
        {  
           if (*CommandLineVars.extension == '.')
           {  strcpy(outext,CommandLineVars.extension); 
           }
           else
           {  strcpy(outext,".");
              strcat(outext,CommandLineVars.extension);
           }
        }
        else
        {  if (pos = strrchr(CommandLineVars.table,'2'))
           {  *pos = '.';
              strcpy(outext,pos);
              *pos = '2';
           }
           else
           {  strcpy(outext,".conv"); }
        }

        if (CommandLineVars.destination != 0L)
        {  char *pos2;
           if (pos2 = strstr(pos=CommandLineVars.destination,"#?"))
           {
              strncpy(pos2,"%s",2);
              sprintf(outfile,pos,GetFileName(FileName,FALSE));
              strncpy(pos2,"#?",2);
           }
           else
           {  strcpy(outfile,CommandLineVars.destination);
           }
        }
        else
        {
           if (pos = strrchr(FileName,'.'))
           {  *pos = 0;
              strcpy(outfile,FileName);
              *pos = '.';
              strcat(outfile,outext);
           }
           else
           {  strcpy(outfile,FileName);
              strcat(outfile,outext);
           }
        }

        if (!CommandLineVars.quiet)
        {  printf("Converting '%s' to '%s' using '%s' ...",
                   FileName,outfile,tablename);
           fflush(stdout);
        }
        if (ConvertFile(FileName,outfile,
                        CommandLineVars.striplf,
                        CommandLineVars.stripcr))
        {  if (!CommandLineVars.quiet)  puts("O.K."); }
        else
        {  if (!CommandLineVars.quiet)  puts("not O.K."); }
      }

      /* Wenn nicht alle Einträge gelesen wurde, liegt ein Fehler vor */
      if (IoErr() != ERROR_NO_MORE_ENTRIES)
      {  rc = RETURN_FAIL; }

      /* alloziertes wieder freigeben */
      FreeArgs(rda);
   }
   else
   {  rc = RETURN_ERROR; }

   /* bei einem Fehler, den Fehler ausgeben */
   if (rc != RETURN_OK) PrintFault(IoErr(),progname);

   if ((rc == RETURN_OK) && (somethingfound == FALSE))
   {  rc = RETURN_WARN; }

   return rc;
}

BOOL IsFile(struct FileInfoBlock *fib)
{
   return ((fib->fib_DirEntryType == ST_FILE) ||
       (fib->fib_DirEntryType == ST_LINKFILE) ||
       (fib->fib_DirEntryType == ST_SOFTLINK));
}

/*
 * für eine Funktionsbeschreibung s.o.
 *
 * all noch nicht implementiert 25. Oktober 1992
 */

char *GetNextFileName(Template *CLV, LONG flags)
{
   /* für den Vergleich, ob ich zum ersten Mal aufgerufen wurde */
   static Template lastcallCLV = {0,0,0};
   static char **arraypointer = 0;   /* zeigt auf das aktuelle Pattern */
   static struct AnchorPath *AP = 0; /* Struktur für Match-Funktionen  */
   static BOOL FirstTimeThisName = FALSE;
   int    retcode = NULL;

   if (lastcallCLV != *CLV)  /* erster Aufruf, alles richten */
   {  arraypointer = CLV->filenames;  /* Zeigt auf das erste Pattern */
      FirstTimeThisName = TRUE;       /* Dieses Pattern zum ersten Mal */
      lastcallCLV = *CLV;             /* jetzt gleich ! */
      if (AP != NULL)                 /* zeigt noch auf was ??? */
      {  FreeMem(AP,sizeof(*AP)+NAMELEN); AP = NULL; }

      /* nun erst einmal die nötige Struktur allozieren */
      if (!(AP = AllocMem(sizeof(*AP)+NAMELEN,MEMF_PUBLIC | MEMF_CLEAR)))
      {
         SetIoErr(ERROR_NO_FREE_STORE);
         return NULL;
      }

      /* das System soll mir gefälligst die vollen Namen liefern */
      AP->ap_Strlen    = NAMELEN;

      /* und auf CTRL-C soll es auch selbst aufpassen */
      AP->ap_BreakBits = SIGBREAKF_CTRL_C;
   }

   while (*arraypointer != NULL) /* noch ein Pattern da */
   {
      if (FirstTimeThisName)  /* dann MatchFirst benutzen */
      {
         FirstTimeThisName = FALSE;
         if (!(retcode = MatchFirst(*arraypointer,AP))) /* 0 = Is OK!!!!! */
         {  if (flags & GNF_NODIR)
            {  if (!IsFile(&(AP->ap_Info)))
               {  continue; }
            }
            return AP->ap_Buf;
         }
         else
         {  SetIoErr(retcode);
            MatchEnd(AP);                          /* ob das sein muß ?? */
            FreeMem(AP,sizeof(*AP)+NAMELEN); AP = NULL;
            return NULL;
         }
      }
      else
      {
         if (!(retcode = MatchNext(AP))) /* 0 = Is OK!!!!! */
         {  if (flags & GNF_NODIR)
            {  if (!IsFile(&(AP->ap_Info)))
               {  continue; }
            }
            return AP->ap_Buf;
         }
         else
         {  if (retcode == ERROR_NO_MORE_ENTRIES) /* nächstes Pattern */
            {  arraypointer ++;
               MatchEnd(AP); /* AP so lassen, hat ja gepaßt */
               FirstTimeThisName = TRUE;
            }
            else
            {  SetIoErr(retcode);
               MatchEnd(AP); /* ob das hier sein muß ? */
               FreeMem(AP,sizeof(*AP)+NAMELEN); AP = NULL;
               return NULL;
            }
         }
      }
   }

   if (AP != NULL)                 /* zeigt noch auf was ??? */
   {  FreeMem(AP,sizeof(*AP)+NAMELEN); AP = NULL; }

   SetIoErr(ERROR_NO_MORE_ENTRIES);
   return NULL;
}


void initchars(char *TABLENAME, long lf2cr, long cr2lf, long quiet)
{
   int i;
   FILE *in;
   BOOL ok = FALSE;

   for (i=0; i < 256; i++)
   {  tt[i] = i; }

   if (in = fopen(TABLENAME,"rb"))
   {  if (fread(tt,256,1,in) > 0)
      {  ok = TRUE; }
      else
      {  if (!quiet) 
         {  printf("Read from '%s' failed\n",TABLENAME);
         }
      }

      fclose(in);
   }
   else
   {  if (!quiet)
      {  printf("Conversion table '%s' not found - use builtin \"Mac to Amiga\"\n",
              TABLENAME);
      }
   }


   if (! ok)
   {
      tt['ä'] = 0x8a;
      tt['ö'] = 0x9a;
      tt['ü'] = 0x9f;
      tt['ß'] = 167;
      tt['Ä'] = 0x80;
      tt['Ö'] = 0x85;
      tt['Ü'] = 0x86;
      tt['»'] = 200;
      tt['«'] = 199;
      tt['é'] = 0x8e;
   }

   if (lf2cr)  tt[LF]  = CR;
   if (cr2lf)  tt[CR]  = LF;
}

BOOL ConvertFile(char *SrcName,char *DestName, 
                 long striplf, long stripcr)
{
   FILE *in,*out;
   BOOL retcode = FALSE;

   if (in = fopen(SrcName,"rb"))
   {
      if (out = fopen(DestName,"wb"))
      {
         register int inchar;
         while ((inchar = fgetc(in)) != EOF)
         {
            switch (inchar) {
               case LF: if (striplf)
                        {  if ((inchar = fgetc(in)) != EOF)
                           {  if (inchar == LF)
                              {  fputc(tt[LF],out); } // doppeltes Linefeed
                              else
                              {  fputc(' ',out);     // Space
                                 fputc(tt[inchar],out); // und den Buchstaben
                              }
                           }
                           else
                           {  fputc(tt[LF],out); } // war letzter Buchstabe
                        }
                        else
                        {  fputc(tt[LF],out); }
                        break;
               case CR: if (stripcr)
                        {  if ((inchar = fgetc(in)) != EOF)
                           {  if (inchar == CR)
                              {  fputc(tt[CR],out); } // doppeltes Linefeed
                              else
                              {  fputc(' ',out);     // Space
                                 fputc(tt[inchar],out); // und den Buchstaben
                              }
                           }
                           else
                           {  fputc(tt[CR],out); } // war letzter Buchstabe
                        }
                        else
                        {  fputc(tt[CR],out); }
                        break;
               default: fputc(tt[inchar],out);
                        break;
            }
         }
         retcode = TRUE;
         fclose(out);
      }
      else
      {  printf("Konnte %s nicht zum Schreiben nicht öffnen\n",DestName);
      }
      fclose(in);
   }
   else
   {  printf("Konnte %s nicht zum Lesen nicht öffnen\n",SrcName);
   }

   return retcode;
}

char *GetFileName(char *FileName,BOOL extension)
{
   static char buf[100];
   char *pos = 0;
   char *ext = 0;

   if ((pos = strrchr(FileName,'/')) == 0)
   {  if ((pos = strrchr(FileName,':')) == 0)
      {  pos = FileName; }
      else
      {  pos++; }
   }
   else
   {  pos++; }

   if (!extension)
   {  if (ext = strrchr(FileName,'.'))
      {  *ext = '\0'; }
   }

   strncpy(buf,pos,100);

   if (ext != 0L)
   {  *ext = '.'; }

   return buf;
}
