/* file_manip.c
 * For use with GTKeyboard
 * written by David Allen s2mdalle@titan.vcu.edu
 * http://opop.nols.com/
 *
 * This is mostly stuff to help deal with parsing the RCfile which we are
 * hopefully going to find in the user's home directory - something like
 * ~/.gtkeyboardrc and ~/.gtkeyboard-shortcuts
 */
/* GTKeyboard - A Graphical Keyboard For X
 * Copyright (C) 1999, 2000 David Allen  
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA  02111-1307, USA.
 */

#define FILE_MANIP_C
#include <stdio.h>
#include <stdlib.h>
#include "include/file_manip.h"
#include "master.h"

#ifdef DEBUGGING
#define VERBOSEMSG(fmt, arg)    if(options.VERBOSE){fprintf(Q,fmt,arg);\
                                                    fflush(Q);}
#else /* not defined(DEBUGGING) */
#define VERBOSEMSG(fmt, arg)    ;
#endif

static int create_file(const char *filename)
{
     FILE *fp;

     if(!filename)
          return(0);

     if(file_exists(filename))
     {
          fprintf(stderr,"create_file:  File \"%s\" already exists!\n",
                  filename); 
          fflush(stderr);
          return(0);
     } /* End if */

     fp = fopen(filename, "w");

     if(fp == NULL)
     {
          fprintf(stderr,"create_file: Couldn't open \"%s\" for writing: %s\n",
                  filename, g_strerror(errno));
          fflush(stderr);
          return(0);
     } /* End if */

     fprintf(Q,"Creating file at location \"%s\"\n",
             filename);
     fflush(Q);
     fprintf(fp,"# This file automatically created by GTKeyboard\n");
     fflush(fp);
     fclose(fp);
     return(1);
} /* End create_file() */

static int deprecated(char *prefix, char *varname, char *val, gpointer ptr)
{
     fprintf(stderr,"GTKeyboard Warning: Use of variable \"%s\" ",varname);
     fprintf(stderr,"is deprecated.\n");
     fflush(stderr);
     
     /* Return a true value so the rc file parsing procedures don't signal
      * an error - this is just a warning, not an error.  
      */
     return(1);
} /* End deprecated() */

int parse_redirect_policy(char *prefix, char *varname, char *val, gpointer ptr)
{
     if(g_strcasecmp(val, "implicit") == 0)
	  options.REDIRECT_POLICY_IMPLICIT = ON;
     else if(g_strcasecmp(val, "explicit") == 0)
	  options.REDIRECT_POLICY_IMPLICIT = OFF;
     else
     {
	  fprintf(stderr,"Error:  Illegal redirect policy:  \"%s\"\n",
		  val ? val : "NULL"); 
	  return(0);
     } /* End else */

     return(1);
} /* End parse_redirect_policy() */

int parse_show_hide(char *prefix, char *varname, char *val, gpointer ptr)
{
     int on_off = OFF;

     /* Since these lines are of the form "[show|hide] element_name"
      * then prefix is going to be either show/hide and varname is going
      * to hold the element name we're interested in
      */

     /* Which is it?  Show, or hide? */
     if(g_strcasecmp(prefix, "show") == 0)
	  on_off = ON;
     else if(g_strcasecmp(prefix, "hide") == 0)
	  on_off = OFF;
     else 
     {
	  /* varname obviously matched something that should use this function
	   * for parsing, but a show or hide isn't present, so bomb with an
	   * error 
	   */
	  fprintf(stderr,
		  "Error:  [show|hide] expected for screen element \"%s\".\n",
		  varname ? varname : "NULL");
	  return(0);
     } /* End else */
     
     /* Now figure out which element */
     if(g_strcasecmp(varname, "toolbar") == 0)
	  ELEMENTS.toolbar = on_off;
     else if(g_strcasecmp(varname, "menubar") == 0)
	  ELEMENTS.menubar = on_off;
     else if(g_strcasecmp(varname, "keyboard") == 0)
	  ELEMENTS.keyboard = on_off;
     else if(g_strcasecmp(varname, "status") == 0)
	  ELEMENTS.status = on_off;
     else if(g_strcasecmp(varname, "text") == 0)
	  ELEMENTS.text = on_off;
     else if(g_strcasecmp(varname, "buttonbar") == 0)
	  ELEMENTS.buttonbar = on_off;
     else
     {
	  fprintf(stderr,"Illegal [show|hide] element name: \"%s\"\n",
		  varname ? varname : "NULL");
	  fflush(stderr);
	  return(0);
     } /* End else */

     return(1);
} /* End parse_show_hide() */

/* Special parsing function to assign GUI.pulloffs_side */
int parse_pulloffs_side(char *prefix, char *varname, char *val, gpointer ptr)
{
     int position = 0;

     if(g_strcasecmp(val, "left") == 0)
	  position = 1;
     else if(g_strcasecmp(val, "right") == 0)
	  position = 2;
     else if(g_strcasecmp(val, "top") == 0)
	  position = 3;
     else if(g_strcasecmp(val, "bottom") == 0)
	  position = 4;

     switch(position){
     case 1:  GUI.PULLOFFS_SIDE = GTK_POS_LEFT; return(1);
     case 2:  GUI.PULLOFFS_SIDE = GTK_POS_RIGHT; return(1);
     case 3:  GUI.PULLOFFS_SIDE = GTK_POS_TOP; return(1);
     case 4:  GUI.PULLOFFS_SIDE = GTK_POS_BOTTOM; return(1);
     default: fprintf(stderr,"Error:  Illegal pulloff posiiton: \"%s\"\n",
		      val ? val : "NULL"); return(0);
     } /* End switch */
} /* End parse_pulloffs_side() */

/* This parses the user resource file via read_ConfigFile.  This function
 * doesn't actually do much other than define all of the structures for
 * parsing in one place and actually doing the function calls.  This is
 * where you need to tweak command options.
 */
void parse_user_resource_file(char *filename)
{
     RCVARS rc_parse_values[] = {
	  /* Toolbar statements first */
	  { "toolbar", "CUT",           RC_BOOL, &Toolbar.TB_CUT, NULL     },
	  { "toolbar", "PASTE",         RC_BOOL, &Toolbar.TB_PASTE, NULL   },
	  { "toolbar", "COPY",          RC_BOOL, &Toolbar.TB_COPY, NULL    },
	  { "toolbar", "FONT",          RC_BOOL, &Toolbar.TB_FONT, NULL    },
	  { "toolbar", "KBD_FONT",   RC_BOOL, &Toolbar.TB_KEYBOARD_FONT,NULL },
	  { "toolbar", "WINDOW_GRAB",   RC_BOOL, &Toolbar.TB_WINDOW_GRAB,NULL},
	  { "toolbar", "RAISE",         RC_BOOL, &Toolbar.TB_RAISE,NULL    },
	  { "toolbar", "LOWER",         RC_BOOL, &Toolbar.TB_LOWER,NULL    },
	  { "toolbar","WINDOW_IGNORE",RC_BOOL,&Toolbar.TB_WINDOW_IGNORE,NULL },
	  { "toolbar", "OPEN",          RC_BOOL, &Toolbar.TB_OPEN,NULL     },
	  { "toolbar", "POSITIONING",   RC_BOOL, &Toolbar.TB_POSITIONING,NULL},
	  { "toolbar", "SAVE",          RC_BOOL, &Toolbar.TB_SAVE,NULL     },
	  { "toolbar", "COLOR",         RC_BOOL, &Toolbar.TB_COLOR,NULL    },
	  { "toolbar", "NEW",           RC_BOOL, &Toolbar.TB_NEW,NULL      }, 
	  { "toolbar", "RESET_STYLE",   RC_BOOL, &Toolbar.TB_RESET_STYLE,NULL},
	  { "toolbar","STATUS_REPORT",RC_BOOL,&Toolbar.TB_STATUS_REPORT,NULL },
	  { "toolbar", "SHORTCUTS",     RC_BOOL, &Toolbar.TB_SHORTCUTS,NULL  },
	  
	  /* Now "options" structures */
	  { "set", OPT_WORD_WRAP,     RC_BOOL, &options.WORD_WRAP,      NULL },
          { "set", OPT_CACHE_FILE,    RC_STR,  &options.cache_file,     NULL },
	  { "set", OPT_XPOS,          RC_INT,  &options.xpos,           NULL },
	  { "set", OPT_YPOS,          RC_INT,  &options.ypos,           NULL },
	  { "set", OPT_BORDERLESS,    RC_BOOL, &options.BORDERLESS,     NULL },
	  { "set", OPT_WORKING_FILE,  RC_STR,  &options.workingfile,    NULL },
          /* INFO_POPUPS is now deprecated.  It doesn't do anything */
          { "set", OPT_INFO_POPUPS,   RC_PARSE_FUNC, 
            &options.INFO_POPUPS, deprecated },
	  { "set", OPT_BUMP_AMOUNT,   RC_INT,  &GUI.BUMP_AMOUNT,        NULL },
	  { "set", OPT_ASK_SAVE,      RC_BOOL,&options.ASK_SAVE_ON_EXIT,NULL },
	  { "set", OPT_SHOW_TOOLTIPS, RC_BOOL, &options.SHOW_TOOLTIPS,  NULL },
	  { "set", OPT_RANDOMIZE_XPMS, RC_BOOL, &options.RANDOMIZE_XPMS,NULL },
	  { "set", OPT_EYECANDY,    RC_BOOL,  &options.RANDOMIZE_XPMS,  NULL },
	  { "set", OPT_STATUS_LOGGING, RC_BOOL, &options.STATUS_LOGGING,NULL },
          { "set", OPT_IGNORE_LAYOUT_FILE, RC_BOOL, 
            &options.IGNORE_LAYOUT_FILE, NULL },
	  { "set", OPT_REDIRECT_POLICY, RC_PARSE_FUNC, 
	    &options.REDIRECT_POLICY_IMPLICIT, parse_redirect_policy },
	  { "set", OPT_EXTRA_FILES, RC_STR,   &options.extrafiles, NULL },
	  { "set", OPT_PULLOFFS,    RC_PARSE_FUNC, &GUI.PULLOFFS_SIDE,
	    parse_pulloffs_side },
	  { "set", OPT_KEYBOARD_FILE, RC_STR, &options.keyboard_file, NULL },
	  { "set", OPT_RAND_STRING_LENGTH, RC_INT, 
	    &options.RANDOM_STRING_CHARACTERS, NULL },
	  { "set", OPT_SEND_TO_BOTH_WINDOWS, RC_BOOL, 
	    &options.SEND_TO_BOTH_WINDOWS, NULL },

	  { "set", OPT_KEYBOARD_FONT, RC_STR,  &GUI.kfontname, NULL },
	  { "set", OPT_TEXT_FONT,     RC_STR,  &GUI.fontname,  NULL },
          { "set", OPT_HANDLEBOXES,   RC_BOOL, &GUI.use_handleboxes, NULL },

	  /* For showing/hiding elements internal to the keyboard display */
	  { "keyboard", "CURSOR_KEYS", RC_BOOL, 
	    &GUI.keyboard_elements.show_cursor_keys, NULL },
	  { "keyboard", "MAIN_KEYBOARD", RC_BOOL,
	    &GUI.keyboard_elements.show_keyboard, NULL },
	  { "keyboard", "NUMBER_PAD", RC_BOOL,
	    &GUI.keyboard_elements.show_number_pad, NULL },
	  { "keyboard", "F_KEYS", RC_BOOL,
	    &GUI.keyboard_elements.show_f_keys, NULL },

	  /* For any of the show/hide statements, don't store them anywhere,
	   * run them through parse_show_hide() -- it will figure out where
	   * to put them.
	   * The "TOOLBAR", "BUTTONBAR", etc. correspond to member names of
	   * the ELEMENTS structure (see include/adt.h struct 
	   * GTKeyboardElements)
	   */
	  { "show", "TOOLBAR",   RC_PARSE_FUNC, NULL, parse_show_hide },
	  { "show", "BUTTONBAR", RC_PARSE_FUNC, NULL, parse_show_hide },
	  { "show", "MENUBAR",   RC_PARSE_FUNC, NULL, parse_show_hide },
	  { "show", "KEYBOARD",  RC_PARSE_FUNC, NULL, parse_show_hide },
	  { "show", "STATUS",    RC_PARSE_FUNC, NULL, parse_show_hide },
	  { "show", "TEXT",      RC_PARSE_FUNC, NULL, parse_show_hide },
	  { NULL,   NULL,        RC_NONE,       NULL,            NULL }
     }; /* End rc_parse_values */

     if(!file_exists(filename))
     {
          fprintf(stderr,"Your resource file doesn't seem to exist.\n");
          fprintf(stderr,"I'll create one for you at \"%s\"\n", filename);
          fflush(stderr);
          setup_default_rcfile(filename);
     } /* End if */

     read_ConfigFile(filename, rc_parse_values, 1);
} /* End parse_user_resource_file() */

/* Read one line from the specified file pointer and return it as
 * as a pointer to char and so on.
 */
void FILE_readline(FILE *fp, char *buffer, const int maxlen)
{
     int index = 0 ;
     int x;
     char tmp;

     do
     {
          x = fread(&tmp,sizeof(char), 1, fp);
          if((x==0) || (tmp == '\n'))
	       buffer[index] = '\0'; /* Terminate the string with a NULL */
          else
	       buffer[index++] = tmp; /*  Add the character */
	  if(!(index<maxlen))
	  {
	       fprintf(Q,"Error on FILE_readline: index >= maxlen\n");
	       fflush(Q);
	       return;
	  } /* End if */
     } while ((x != 0) && (tmp !='\n'));
     return;
} /* End FILE_readline */

char *expand_relative_pathname(char *filename)
{
     char *file = NULL;

     if(!filename)
     {
	  file = g_strdup_(options.extrafiles);
	  return(file);
     } /* End if */

     file = g_new0_(char, strlen(filename) + 
		    strlen(options.extrafiles) + 2);
     sprintf(file,"%s%s",options.extrafiles, filename);
     return(file);
} /* End expand_relative_pathname */

/* Loads the shortcuts into the program from SHORTCUTS_FILENAME */
int gtkeyboard_load_shortcuts(void)
{
     FILE *fp;
     char filename[1024];
     register int x=0;
     char line[SHORTCUT_MAX_LENGTH];

     sprintf(filename,"%s/%s",options.home, SHORTCUTS_FILENAME);

     if((fp = fopen(filename,"r")) == NULL)
     {
	  gtkeyboard_error(5,"Couldn't open ",filename," for reading:  ",
			   g_strerror(errno),"\n",
			   "A shortcuts file will be created for you.");

          if(!create_file(filename))
          {
               fprintf(stderr,"Unable to create shortcuts file at %s!\n",
                       filename);
               fflush(stderr);
          } /* End if */
	  return(0);
     } /* End if */
     
     x=0;       /* Line number and array subscript for options.shortcuts[x] */
     
     while(!feof(fp) && x<SHORTCUTS)
     {
	  FILE_readline(fp, line, SHORTCUT_MAX_LENGTH);
	  if(g_strcasecmp(line,"NONE")==0)
	       options.shortcuts[x] = (char *)NULL;
	  else
	  {
	       if(line)
	       {
		    CONDFREE(options.shortcuts[x]);
		    options.shortcuts[x] = g_strdup_(line);
	       } /* End if */
	       else
		    options.shortcuts[x] = (char *)NULL;
	  } /* End else */
	  
	  if(!line)
	  {
	       gtkeyboard_message(1,"Shortcut nonexistant from file.\n");
	  } /* End if */
	  x++;
     } /* End while */
     
     fclose(fp);     /* Close the file */
     return(1);
} /* End gtkeyboard_load_shortcuts */

/* Saves shortcuts to 
 * $HOME/SHORTCUTS_FILENAME
 */
void gtkeyboard_save_shortcuts(GtkWidget *widget, gpointer data)
{
     FILE *fp;
     char filename[1024];
     register int x=0;

     /* Destroy calling widget window if needed */
     if(data)
	  gtk_widget_destroy((GtkWidget *)data);

     /* This is begging for a segfault so let's not do it. */
     if((strlen(options.home) + strlen(SHORTCUTS_FILENAME) + 2) >=1024)
     {
	  gtkeyboard_error(2,"Can't save shortcuts:  filename overflow.\n",
			   "Please report this to the maintainer.\n");
	  return;
     } /* End if */

     sprintf(filename,"%s/%s",options.home, SHORTCUTS_FILENAME);
     fprintf(Q,"Saving shortcuts to \"%s\"\n",filename);

     if((fp = fopen(filename,"w")) == NULL)
     {
	  gtkeyboard_error(5,"Cannot open ", filename, " for writing:  ",
			   g_strerror(errno), "\nShortcuts NOT SAVED.\n");
	  return;
     } /* End if */

     /* Save each shortcut on one line.  Real simple.  Hopefully */
     for(x=0; x<SHORTCUTS; x++)
     {
	  if(!options.shortcuts[x] || options.shortcuts[x] == (char *)NULL)
	       fprintf(fp,"NONE\n");
	  else
	       fprintf(fp,"%s\n",options.shortcuts[x]);
     } /* End for */

     fclose(fp);

     gtkeyboard_message(2,"Shortcuts saved to file ",filename,"\n");
} /* End gtkeyboard_save_shortcuts */

/* Copies a default rcfile from somewhere to the input filename */
int setup_default_rcfile(char *file)
{
     FILE *fp;
     FILE *dflt;
     char buffer[1024];
     char buffer2[1024];
     
     if((fp = fopen(file,"w"))==NULL)
     {
	  fprintf(Q,"Couldn't open %s for writing - cannot create ",file);
	  fprintf(Q,"default .gtkeyboardrc!\n");
	  fflush(Q);
	  return(0);
     } /* End if */
     else
     {
	  /* Try to open the distribution-provided rcfile first */
	  sprintf(buffer,"%s/%s", options.extrafiles, PROVIDED_RCFILE);
	  
	  if((dflt = fopen(buffer,"r")) == NULL)
	  {
	       /* Ok, fine, so we can't open the default one we provided 
		* with the distribution.  We'll just give the user a really
		* short and crappy one.
		*/
	       fprintf(Q,"Couldn't open %s: %s.\n",buffer,g_strerror(errno));
	       fprintf(Q,"Fine then!  We'll try something else...\n");
	       fflush(Q);

	       fprintf(fp,"%s",DEFAULT_RCFILE);
	       fclose(fp);
	       fprintf(Q,"Success writing default .gtkeyboardrc\n");
	       fprintf(Q,"You can edit it to make changes later by editing ");
	       fprintf(Q,"%s\n",file);
	       fflush(Q);
	       return(1);
	  } /* End if */
	  else
	  {
	       while(!feof(dflt))
	       {
		    FILE_readline(dflt, buffer2, 1024);   
		    /* Add a newline because FILE_readline kills them off */
		    fprintf(fp,"%s\n",buffer2);
	       } /* End while */

	       fflush(fp);
	       fclose(dflt);     
	       fclose(fp);

	       fprintf(Q,"Successfully wrote .gtkeyboardrc from ");
	       fprintf(Q,"default.  (%s: Distribution provided)\n",
                       PROVIDED_RCFILE);
	  } /* End else */
     } /* End else */
     FLUSH_EVERYTHING;
     return(1);
} /* End setup_default_rcfile */

int is_regular_file(const char *filename)
{
     struct stat file_info;
     int x;

     STAT_AND_RETURN_IF_BAD(filename, x, file_info);

     return(S_ISREG(file_info.st_mode));
} /* End is_regular_file() */

int is_fifo(const char *filename)
{
     struct stat file_info;
     int x;

     STAT_AND_RETURN_IF_BAD(filename, x, file_info);

     return(S_ISFIFO(file_info.st_mode));
} /* End is_fifo() */

int is_link(const char *filename)
{
     struct stat file_info;
     int x;

     STAT_AND_RETURN_IF_BAD(filename, x, file_info);

     return(S_ISLNK(file_info.st_mode));
} /* End is_link() */

int is_executable(const char *filename)
{
     struct stat file_info;
     int x;

     STAT_AND_RETURN_IF_BAD(filename, x, file_info);
     
     return((file_info.st_mode & S_IXUSR) ||
	    (file_info.st_mode & S_IXGRP) ||
	    (file_info.st_mode & S_IXOTH));
} /* End is_executable */

int file_exists(const char *filename)
{
     struct stat file_info;
     int x;

     STAT_AND_RETURN_IF_BAD(filename, x, file_info);

     return(1);
} /* End file_exists */
