/*
 * config.c   V1.5
 *
 * configuration file handling
 *
 * (c) 1991 by Stefan Becker
 *
 */
#include "ToolManager.h"

/* Tool type keywords in the configuration file, must end with a ':'!! */
static char IconKey[]="ICON:";
static char DockKey[]="DOCK:";
static char DummyKey[]="DUMMY:";
static char CLIKey[]="CLI:";
static char WBKey[]="WB:";
#define CONFIGBLOCKEND '#'

/* Config entry keywords */
static char AssignKey[]=" = ";
char AliasKey[]="Alias   ";
char RealKey[]="RealName";
static char DirKey[]="WorkDir ";
static char PathKey[]="Path    ";
static char OFileKey[]="OutFile ";
char MenuKey[]="Menu    ";
char HotKKey[]="HotKey  ";
char StackKey[]="Stack   ";
static char ArgsKey[]="Args    ";
static char OutputKey[]="Output  ";
static char WBFrontKey[]="WBFront ";
static char NameKey[]="IconName";
static char ITypeKey[]="IconType";
char IFileKey[]="IconFile";
static char IPosKey[]="IconPos ";
static char DockOnKey[]="ShowDock";
static char SIconKey[]="SameIcon";
static char DTypeKey[]="DockType";
char DFileKey[]="DockFile";
static char OffKey[]="OFF";
static char OnKey[]="ON";
char BrushKey[]="Brush";
char DObjKey[]="Icon";

/* Data for syntax error requester */
static struct EasyStruct SyntaxErrES={sizeof(struct EasyStruct),0,MyName,
                                      "Syntax error in configuration file\n"\
                                      "%s, line %ld\n'%s'","Ok"};

/* External variables for Dock */
extern UWORD DockXPos,DockYPos;
extern UWORD DockXSize,DockYSize;
extern BOOL DockVertical;
extern BOOL DockToBack;

/* Set the name of the config file */
void SetConfigFileName(char *s)
{
 if (ConfigName) free(ConfigName);
 ConfigName=strdup(s);
}

/* Init a config block struct */
void InitConfigBlock(struct ConfigBlock *cb)
{
 cb->cb_Type=255;
 cb->cb_Flags=TNFLAGS_MENU|TNFLAGS_DOBJ|TNFLAGS_DDOB;
 cb->cb_Stack=STACKMIN;
 cb->cb_IconX=NO_ICON_POSITION;
 cb->cb_IconY=NO_ICON_POSITION;
 cb->cb_Alias[0]='\0';
 cb->cb_Alias[BUFLEN-1]='\0';
 cb->cb_RealName[0]='\0';
 cb->cb_RealName[BUFLEN-1]='\0';
 cb->cb_WorkDir[0]='\0';
 cb->cb_WorkDir[BUFLEN-1]='\0';
 cb->cb_Path[0]='\0';
 cb->cb_Path[BUFLEN-1]='\0';
 cb->cb_OutFile[0]='\0';
 cb->cb_OutFile[BUFLEN-1]='\0';
 cb->cb_HotKey[0]='\0';
 cb->cb_HotKey[BUFLEN-1]='\0';
 cb->cb_IconFile[0]='\0';
 cb->cb_IconFile[BUFLEN-1]='\0';
 cb->cb_DockFile[0]='\0';
 cb->cb_DockFile[BUFLEN-1]='\0';
}

/* Read one config file entry */
static void ReadConfigBlock(FILE *fh, struct ConfigBlock *cb, char *name,
                            int *lines, struct Window *w)
{
 register char *cp=cb->cb_TempLine;

 while (!feof(fh)) /* if not end of file, read one line into buffer */
  {
   (*lines)++;

   if (fgets(cp,BUFLEN+20,fh) && (strlen(cp)>1)) /* Skip empty lines */
    if (*cp==CONFIGBLOCKEND) return;             /* End of entry reached */
    else
     {
      register char *parm;

      /* Remove trailing newline */
      if (cp[strlen(cp)-1]=='\n') cp[strlen(cp)-1]='\0';

      /* Scan line for '=' */
      parm=strchr(cp,'=');

      /* Skip white space */
      if (parm)
       {
        parm++;
        while (*parm && isspace(*parm)) parm++;
       }

      /* Valid parameter? */
      if (!parm && !*parm)
       {
        /* No --> Syntax error */
        EasyRequest(w,&SyntaxErrES,NULL,name,*lines,cp);

        /* Next line */
        continue;
       }

      /* Extract parameter value */
      if (!strnicmp(cp,AliasKey,5))                  /* Alias    = <string> */
       strncpy(cb->cb_Alias,parm,BUFLEN-1);
      else if (!strnicmp(cp,RealKey,8))              /* RealName = <string> */
            strncpy(cb->cb_RealName,parm,BUFLEN-1);
      else if (!strnicmp(cp,DirKey,7))               /* WorkDir  = <string> */
            strncpy(cb->cb_WorkDir,parm,BUFLEN-1);
      else if (!strnicmp(cp,PathKey,4))              /* Path     = <string> */
            {
             if (cb->cb_Type==TNTYPE_CLI) /* CLI tools only! */
              strncpy(cb->cb_Path,parm,BUFLEN-1);
            }
      else if (!strnicmp(cp,OFileKey,7))             /* OutFile  = <string> */
            {
             if (cb->cb_Type==TNTYPE_CLI) /* CLI tools only! */
              {
               cb->cb_Flags|=TNFLAGS_COUT;           /* Set output flag */
               strncpy(cb->cb_OutFile,parm,BUFLEN-1);
              }
            }
      else if (!strnicmp(cp,HotKKey,6))              /* HotKey   = <string> */
            strncpy(cb->cb_HotKey,parm,BUFLEN-1);
      else if (!strnicmp(cp,IFileKey,8))             /* IconFile = <string> */
            {
             cb->cb_Flags|=TNFLAGS_ICON;             /* Set icon flag */
             strncpy(cb->cb_IconFile,parm,BUFLEN-1);
            }
      else if (!strnicmp(cp,StackKey,5))             /* Stack    = <number> */
            {
             char *cp1;

             cb->cb_Stack=strtol(parm,&cp1,10);
            }
      else if (!strnicmp(cp,ArgsKey,4))              /* Args     = OFF */
            {
             if (!stricmp(parm,OffKey)) cb->cb_Flags|=TNFLAGS_NARG;
            }
      else if (!strnicmp(cp,OutputKey,6))            /* Output   = ON */
            {
             if (!stricmp(parm,OnKey) && (cb->cb_Type==TNTYPE_CLI))
              cb->cb_Flags|=TNFLAGS_COUT;            /* Set output flag */
            }
      else if (!strnicmp(cp,WBFrontKey,7))           /* WBFront  = ON */
            {
             if (!stricmp(parm,OnKey)) cb->cb_Flags|=TNFLAGS_WBTF;
            }
      else if (!strnicmp(cp,MenuKey,4))              /* Menu     = OFF */
            {
             if (!stricmp(parm,OffKey)) cb->cb_Flags&=~TNFLAGS_MENU;
            }
      else if (!strnicmp(cp,NameKey,6))              /* Name     = OFF */
            {
             cb->cb_Flags|=TNFLAGS_ICON;             /* Set icon flag */
             if (!stricmp(parm,OffKey)) cb->cb_Flags|=TNFLAGS_NNAM;
            }
      else if (!strnicmp(cp,ITypeKey,8))             /* IconType = Brush */
            {
             cb->cb_Flags|=TNFLAGS_ICON;             /* Set icon flag */
             if (!stricmp(parm,BrushKey)) cb->cb_Flags&=~TNFLAGS_DOBJ;
            }
      else if (!strnicmp(cp,IPosKey,7))              /* IconPos  = <x,y> */
            {
             char *cp1;

             cb->cb_Flags|=TNFLAGS_ICON;             /* Set icon flag */
             cb->cb_IconX=strtol(parm,&cp1,10);
             if (cp1) cb->cb_IconY=strtol(cp1+1,&cp1,10);
            }
      else if (!strnicmp(cp,DockOnKey,8))            /* Dock     = ON */
            {
             if (!stricmp(parm,OnKey)) cb->cb_Flags|=TNFLAGS_DOCK;
            }
      else if (!strnicmp(cp,SIconKey,8))             /* SameIcon = ON */
            {
             cb->cb_Flags|=TNFLAGS_DOCK;             /* Set dock flag */
             if (!stricmp(parm,OnKey)) cb->cb_Flags|=TNFLAGS_SICN;
            }
      else if (!strnicmp(cp,DTypeKey,8))             /* DockType = Brush */
            {
             cb->cb_Flags|=TNFLAGS_DOCK;             /* Set dock flag */
             if (!stricmp(parm,BrushKey)) cb->cb_Flags&=~TNFLAGS_DDOB;
            }
      else if (!strnicmp(cp,DFileKey,8))             /* DockFile = <string> */
            {
             cb->cb_Flags|=TNFLAGS_DOCK;             /* Set dock flag */
             cb->cb_Flags&=~TNFLAGS_SICN;            /* Clear same image flag */
             strncpy(cb->cb_DockFile,parm,BUFLEN-1);
            }
      else                                   /* Comment or unknown keyword */
       if (*cp!=';')
        {
         cp[strlen(cp)-1]='\0'; /* remove trailing \n */
         EasyRequest(w,&SyntaxErrES,NULL,name,*lines,cp);
        }
     }
  }
}

/* Read configuration file */
void ReadConfigFile(char *name, BOOL complete)
{
 struct ConfigBlock *cb;  /* Memory block for one config file entry */
 FILE *fh;                /* Filehandle for config file */
 int lines=0;             /* Line counter */
 struct Screen *pubsc;
 struct Window *w=NULL;   /* Pointer to Window for EasyRequest() */

 if (pubsc=LockPubScreen(WBScreenName))
  w=pubsc->FirstWindow;

 if (cb=malloc(sizeof(struct ConfigBlock)))  /* Get memory */
  {
   register char *cp=cb->cb_TempLine;

   if (name && (fh=fopen(name,"r"))) /* Scan config file */
    {
     while (!feof(fh)) /* if not end of file, read one line into buffer */
      {
       lines++;

       if (fgets(cp,BUFLEN,fh) && (strlen(cp)>1)) /* Skip empty lines */
        {
         InitConfigBlock(cb);

         /* Extract tool type */
         if (!strnicmp(cp,IconKey,sizeof(IconKey)-1))        /* ICON: */
          {
           if (complete)
            {
             char *cp1;

             /* ICON: <num>,<num> */
             IconXPos=strtol(cp+sizeof(IconKey)-1,&cp1,10);
             if (cp1) IconYPos=strtol(cp1+1,&cp1,10);
            }
          }
         else if (!strnicmp(cp,DockKey,sizeof(DockKey)-1))   /* DOCK: */
          {
           if (complete)
            {
             char *cp1;

             /* DOCK: <num>,<num>,<num>,<num>,(H|V)(F|B) */
             DockXPos=strtol(cp+sizeof(DockKey)-1,&cp1,10);
             if (cp1) DockYPos=strtol(cp1+1,&cp1,10);
             if (cp1) DockXSize=strtol(cp1+1,&cp1,10)+1;
             if (DockXSize<=0) DockXSize=41;
             if (cp1) DockYSize=strtol(cp1+1,&cp1,10)+1;
             if (DockYSize<=0) DockYSize=41;
             if (cp1) DockVertical=(cp1[1]=='V');
             if (cp1) DockToBack=(cp1[2]=='B');
            }
          }
         else if (!strnicmp(cp,DummyKey,sizeof(DummyKey)-1)) /* DUMMY: */
          cb->cb_Type=TNTYPE_DUMMY;
         else if (!strnicmp(cp,CLIKey,sizeof(CLIKey)-1))     /* CLI: */
          cb->cb_Type=TNTYPE_CLI;
         else if (!strnicmp(cp,WBKey,sizeof(WBKey)-1))       /* WB: */
          cb->cb_Type=TNTYPE_WB;
         else if (*cp!=';')              /* Comment or Syntax error */
          {
           /* Syntax error, display error requester */
           cp[strlen(cp)-1]='\0'; /* remove trailing \n */
           EasyRequest(w,&SyntaxErrES,NULL,name,lines,cp);

           /* Try to skip to end of current block */
           while (!feof(fh))
            {
             lines++;
             fgets(cp,BUFLEN,fh);
             if (*cp==CONFIGBLOCKEND) break;
            }
          }

         /* Does a valid config block follow? */
         if (cb->cb_Type!=255)
          {
           /* Read in one config file entry */
           ReadConfigBlock(fh,cb,name,&lines,w);

           /* Special check for empty dummy tool entries */
           if ((cb->cb_Type==TNTYPE_DUMMY) && (cb->cb_Alias[0]=='\0') &&
               (cb->cb_RealName[0]=='\0'))
            strcpy(cb->cb_Alias," ");

           /* Add tool */
           AddToolNode(cb,StartupCD);
          }
        }
      }
     fclose(fh);
    }

   free(cb);
  }

 if (pubsc) UnlockPubScreen(NULL,pubsc);
}

/* Tiny long to string conversion routine */
static void ltoa(char *s, long n)
{
 long i=1000000000;     /* Divisor */
 BOOL inumber=FALSE;    /* Flag */

 if (n==-2147483648)    /* Handle special case 2^31*/
  {
   strcpy(s,"-2147483648");
   return;
  }

 if (n<0)               /* Handle negativ numbers */
  {
   n=-n;
   *s++='-';
  }

 if (n==0) *s++='0';    /* Zero is a special case */
 else while (i)         /* Every other numer goes here */
       {
        *s=n/i+'0';     /* Retrieve leading digit */
        if (*s!='0') inumber=TRUE; /* Suppress leading zero's */
        if (inumber) s++;
        n%=i;           /* Remove digit from number */
        i/=10;          /* next divisor */
       }

 *s='\0';               /* Append string terminator */
}

/* Write one parameter keyword */
static void WriteParamKey(char *cp, FILE *fh)
{
 fputs(cp,fh);        /* Write keyword */
 fputs(AssignKey,fh); /* Write " = " */
}

/* Write one complete config line */
static void WriteConfigLine(char *key, char *parm, FILE *fh)
{
 WriteParamKey(key,fh); /* Write keyword */
 fputs(parm,fh);        /* Write parameter */
 fputc('\n',fh);        /* Write newline */
}

/* Write configuration file */
BOOL WriteConfigFile(struct Window *w, BOOL noreq)
{
 BOOL error=FALSE;
 BOOL rc=TRUE;     /* Means user pressed cancel */
 char *cp;
 char number[20];

 /* ConfigName was NOT set because of low mem situation --> break */
 if (!ConfigName) goto wce0;

 /* Show file requester */
 cp=ConfigName;
 if (noreq || !ShowFileRequester(w,"Save Configuration",&cp,FREQ_SAVE))
  {
   /* User selected a file */
   FILE *fh;

   /* Set new name */
   if (!noreq) SetConfigFileName(cp);

   if (fh=fopen(cp,"w"))             /* Open config file */
    {
     register struct ToolNode *tn;

     /* Write icon position line */
     if ((IconXPos!=NO_ICON_POSITION) || (IconYPos!=NO_ICON_POSITION))
      {
       fputs(IconKey,fh);
       ltoa(number,IconXPos);
       fputs(number,fh);
       fputc(',',fh);
       ltoa(number,IconYPos);
       fputs(number,fh);
       fputc('\n',fh);
      }

     /* Write dock window position line */
     fputs(DockKey,fh);
     ltoa(number,DockXPos);
     fputs(number,fh);
     fputc(',',fh);
     ltoa(number,DockYPos);
     fputs(number,fh);
     fputc(',',fh);
     ltoa(number,DockXSize-1);
     fputs(number,fh);
     fputc(',',fh);
     ltoa(number,DockYSize-1);
     fputs(number,fh);
     fputc(',',fh);
     if (DockVertical)
      fputc('V',fh);
     else
      fputc('H',fh);
     if (DockToBack)
      fputc('B',fh);
     else
      fputc('F',fh);
     fputc('\n',fh);

     for (tn=GetHead(&ToolList); tn; tn=GetSucc(tn))
      {
       /* Write tool type */
       switch(tn->tn_Type)
        {
         case TNTYPE_DUMMY:
          fputs(DummyKey,fh);
          break;
         case TNTYPE_CLI:
          fputs(CLIKey,fh);
          break;
         case TNTYPE_WB:
          fputs(WBKey,fh);
          break;
        }
       fputc('\n',fh);

       /* Write alias name */
       WriteConfigLine(AliasKey,tn->tn_Node.ln_Name,fh);

       /* Write real name */
       if (tn->tn_RealName)
        WriteConfigLine(RealKey,tn->tn_RealName,fh);

       /* Write working directory */
       if (tn->tn_WorkDir)
        WriteConfigLine(DirKey,tn->tn_WorkDir,fh);

       /* Write path string */
       if (tn->tn_Path)
        WriteConfigLine(PathKey,tn->tn_Path,fh);

       /* Write stack size */
       if (tn->tn_Stack>STACKMIN)
        {
         WriteParamKey(StackKey,fh);
         ltoa(number,tn->tn_Stack);
         fputs(number,fh);
         fputc('\n',fh);
        }

       /* Write argument passing state */
       if (tn->tn_Flags&TNFLAGS_NARG)
        WriteConfigLine(ArgsKey,OffKey,fh);

       /* Write output file state */
       if (tn->tn_Flags&TNFLAGS_COUT)
        if (tn->tn_OutFile)
         WriteConfigLine(OFileKey,tn->tn_OutFile,fh);
        else
         WriteConfigLine(OutputKey,OnKey,fh);

       /* Write Workbench to front state */
       if (tn->tn_Flags&TNFLAGS_WBTF)
        WriteConfigLine(WBFrontKey,OnKey,fh);

       /* Write menu entry status */
       if (!(tn->tn_Flags&TNFLAGS_MENU))
        WriteConfigLine(MenuKey,OffKey,fh);

       /* Write HotKey description */
       if (tn->tn_HotKey)
        WriteConfigLine(HotKKey,tn->tn_HotKey,fh);

       /* Write icon stuff */
       if (tn->tn_Flags&TNFLAGS_ICON)
        {
         if (tn->tn_Flags&TNFLAGS_NNAM)
          WriteConfigLine(NameKey,OffKey,fh);

         if (!(tn->tn_Flags&TNFLAGS_DOBJ))
          WriteConfigLine(ITypeKey,BrushKey,fh);

         if (tn->tn_IconFile)
          WriteConfigLine(IFileKey,tn->tn_IconFile,fh);

         if ((tn->tn_Icon->do_CurrentX!=NO_ICON_POSITION) ||
             (tn->tn_Icon->do_CurrentY!=NO_ICON_POSITION))
          {
           WriteParamKey(IPosKey,fh);
           ltoa(number,tn->tn_Icon->do_CurrentX);
           fputs(number,fh);
           fputc(',',fh);
           ltoa(number,tn->tn_Icon->do_CurrentY);
           fputs(number,fh);
           fputc('\n',fh);
          }
        }

       /* Write dock stuff */
       if (tn->tn_Flags&TNFLAGS_DOCK)
        if (tn->tn_Flags&TNFLAGS_SICN) WriteConfigLine(SIconKey,OnKey,fh);
        else if (tn->tn_DockFile)
              {
               WriteConfigLine(DFileKey,tn->tn_DockFile,fh);
               if (!(tn->tn_Flags&TNFLAGS_DDOB))
                WriteConfigLine(DTypeKey,BrushKey,fh);
              }
        else WriteConfigLine(DockOnKey,OnKey,fh);

       fputc(CONFIGBLOCKEND,fh);     /* Append config entry terminator */
       fputc('\n',fh);               /* Append a new line */
      }

     fclose(fh);                      /* Close the config file */
     ConfigChanged=FALSE;             /* Config saved */
     rc=FALSE;                        /* All OK */
    }
   else error=TRUE; /* Couldn't open file */

   if (!noreq) free(cp); /* free buffer from ShowFileRequester */
  }

wce0: if (error) DisplayBeep(NULL);
      return(rc);
}
