/*
 * GPR
 * Copyright (C) 2006 A. Mennucci
 *
 * Copyright (C) 2000 CompuMetric Labs, Inc.
 *
 * For more information contact:
 *        Thomas Hubbell
 *        CompuMetric Labs, Inc.
 *        5920 Grelot Road, Suite C-2
 *        Mobile, AL 36609
 * 
 * Voice: (334) 342-2220
 *   Fax: (334) 343-2261
 * Email: thubbell@compumetric.com
 *   Web: http://www.compumetric.com
 *
 * 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 General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *    
 *        File: Callbacks.c
 * 
 * Description:
 */


#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <sys/stat.h>
#include <gtk/gtk.h>
#include <gettext.h>
#include <string.h>
#include <stdlib.h>

#include "callbacks.h"
#include "interface.h"
#include "support.h"
#include "tab_categories.h"

void choice_process(PpdOption * option, PpdChoice * choice, GtkWidget * combo,
		    int interator, gboolean inst_opt, GtkWidget * combo_item,
		    int count, gpointer ppd);

#define MASTER_PPDDIR "/usr/share/postscript/ppd"

/* keywords used to assign data to widgets with gtk_object_set_data */
#define OT "option text"
#define OK "option keyword"
#define CT "choice text"
#define CK "choice keyword"
#define IN "index"
/* keyword to assigne a pointer to */
#define OC "option combo"


#define MAX_TABS 25
#define NUM_STANDARD_TABS 3



/* puts data exactly in position 
   (basilarly, the original authors should have used an array) */
static GSList* my_g_slist_replace(GSList *list,
				 gpointer data,
				 gint position)
{
  GSList* obj=g_slist_nth(list,position);
  g_assert( position <= g_slist_length(list)  );
  if(obj) {
    if(obj->data) g_free(obj->data);
    obj->data=data;    
  } else
    list=g_slist_insert(list,data,position);
  return list;
}


void revalidate_utf8(gchar *str)
{
  extern int debug_on;
  gchar *end_valid;
  if ( ! g_utf8_validate (str,  -1,  &end_valid ) ) {
    gsize bytes_read, bytes_written; GError *error=NULL;
    if (debug_on) printf(" Non UTF8 string  '%s' at char '%c' ",str, *end_valid );
    gchar * new = g_locale_to_utf8(str, -1, &bytes_read, &bytes_written, &error );
    //truncate originalstring
    *end_valid=0;
    if (debug_on)
      printf(" Non UTF8 string truncated  to '%s' \n", str);
    if (debug_on) printf(" truncated to '%s' ; may be converted to '%s' \n ",str,new);
    //FIXME this code is now mostly useless:
    //  should hook to encoding in PPD file, and really convert....
    //*str=new;
  }
}

typedef struct tNotebookTab {
  char *title;
  GtkWidget *vbox;
  int pos;
} tNotebookTab, *ptrNotebookTab;

tNotebookTab extra_tabs[MAX_TABS] = {{NULL,NULL}};

extern char *cmdname;
extern ptrTab *globalTabList;

/* local functions */
static GtkWidget *gpr_hbox_make(GtkWidget * opt_vbox);
static GtkWidget *gpr_label_make(gchar * str, GtkWidget * hbox);
static GtkWidget *gpr_optcombo_make(GtkWidget * hbox);
static GtkWidget *gpr_combo_make(ppd_struct * local_ppd, int option_count,
				PpdOption * option_ptr, GtkWidget * option_combo,
				 gboolean inst_opt, GtkSignalFunc signal_func_ptr);
static void gpr_combo_choices_make(int idx, ppd_struct * local_ppd,
				  int option_count, PpdOption * option_ptr,
				   GtkWidget * choice_combo, gboolean inst_opt);//GtkSignalFunc signal_func_ptr);
static void gpr_set_ppd_saved_options(ppd_struct * local_ppd);
static gboolean ensure_remove_hash_item(gpointer key, gpointer value,
					gpointer user_data);
static int in_path(gchar * filename);
static int is_file(gchar * filename);

/*
 * BEGIN INIT AND CONTROL CALLBACKS
 */

/*
 * Assign filename in entrybox as the file to print in the ppd structure
 */
void grab_file_to_print(GtkWidget * entrybox, gpointer ppd)
{
  char *tmp;			/* temporary string */
  ppd_struct *local_ppd;	/* local copy of ppd struct */

  local_ppd = ppd;

  /* redirect stderr to dialog */
  g_set_printerr_handler((GPrintFunc) error_box);

#ifdef USE_GNOMEFILEENTRY
  tmp = gtk_entry_get_text(GTK_ENTRY(entrybox));
#else
  tmp = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(entrybox));
#endif

  if (tmp == NULL) {
#ifdef GNOME
    g_printerr(_("Unable to load file to print\n"));
#endif
    return;
  }

  local_ppd->file_to_print = tmp;
}


/*
 * Updates the default to appear in the browse for ppdfile window
 */
void update_ppd_path(GtkWidget * fileentry, gpointer ppd)
{
  ppd_struct *local_ppd;	/* local copy of ppd struct */

  local_ppd = ppd;
#ifdef USE_GNOMEFILEENTRY
  gnome_file_entry_set_default_path(GNOME_FILE_ENTRY(fileentry),
				    local_ppd->spool_dir);
#else
  g_warning("should set fileentry default path to '%s'",local_ppd->spool_dir);
#endif
}


/*
 * Get the name of the ppd file from the box
 */
void grab_ppd_file(GtkWidget * entrybox, gpointer ppd)
{
  char *tmp;			/* temporary string */
  ppd_struct *local_ppd;	/* local copy of ppd struct */
  gchar *user_ppd_path;		/* path to user's default ppd for this printer */
  FILE *user_ppd;		/* user's local ppd config file */

  local_ppd = ppd;

  /* redirect stderr to dialog */
  g_set_printerr_handler((GPrintFunc) error_box);

#ifdef USE_GNOMEFILEENTRY
  tmp = gtk_entry_get_text(GTK_ENTRY(entrybox));
#else
  tmp = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER( entrybox));
#endif

  if (tmp == NULL) {
#ifdef GNOME
    g_printerr     (_("ERROR!\n\nUnable to load ppd file.\nPlease select another PPD file."));
#endif
    return;
  }

  local_ppd->ppd_file = tmp;

  /* 
   * erase old local copy of ppd file and replace with selected one
   * File extension must be all lower case, i.e. ".ppd" not ".PPD"
   */
  if (strstr(tmp, ".ppd") != NULL) {
    user_ppd_path = g_strconcat(local_ppd->config_dir, "/ppd.conf", NULL);
    user_ppd = fopen((char *)user_ppd_path, "w");
    if (user_ppd) {
      tmp = (char *)g_strconcat(tmp, "\n", NULL);
      fputs(tmp, user_ppd);
      fclose(user_ppd);
    }
  }
}

/*
 * Grabs the default PPD from the user's config dir or the postscript.conf 
 * file and places the name in the ppd entry box
 */
void grab_default_ppd(GtkWidget * menu_item, gpointer ppd)
{
  gchar *spool_path;		/* the spool directory */
  gchar *psconf_path;		/* path to the postscript.cfg file */
  gchar *user_ppd_path = NULL;	/* path to the local ppd config file */
  gchar *ppd_file_path = NULL;	/* path to the ppd file */
  gchar *default_ppd_file_path = NULL;	/* path to the default ppd file */
  gchar *ppd_file = NULL;	/* ppd file name */
  gchar *default_ppd_file = NULL;	/* system wide default ppd file */
  gchar *printcap_ppd_file = NULL;
  ppd_struct *local_ppd;	/* local copy of ppd struct */
  FILE *psconfig;		/* file pointer to postscipt.cfg file */
  FILE *user_ppd;		/* user's local default ppd file (also last
				   used) */
  char curr_line[100];		/* line of input from postscript.cfg file */
  char *temp;			/* temporary string */
  char *end;			/* pointer to the new end of string */

  /* redirect stderr to dialog */
  g_set_printerr_handler((GPrintFunc) error_box);

  local_ppd = ppd;

  spool_path = g_strdup(local_ppd->spool_dir);
  psconf_path = g_strconcat(spool_path, "/postscript.cfg", NULL);

  printcap_ppd_file = g_strdup(gtk_object_get_data(GTK_OBJECT(menu_item), PF));

  /* First look for a system-wide default ppd in postscript.cfg */
  psconfig = fopen((char *)psconf_path, "r");
  if (psconfig != NULL) {
    /* parse the postscript.cfg file, looking for a default ppd */
    for (;;) {
      if (fgets(curr_line, sizeof(curr_line), psconfig)
	  == NULL)		/* end of file */
	break;
      if (curr_line[0] == '#')	/* catch case where PPDFILE commented out */
	continue;
      temp = strstr(curr_line, "PPDFILE=");	/* search for "PPDFILE=" */
      if (temp != NULL) {
	temp += 8;
	end = strstr(curr_line, "\n");
	*end = '\0';
	default_ppd_file = g_strdup(temp);
	break;
      } else
	continue;
    }
    fclose(psconfig);
  }

  if(local_ppd->spooler==SPOOLER_CUPS && default_ppd_file == NULL) {
    default_ppd_file=g_strconcat("/etc/cups/ppd/",local_ppd->printer_name,".ppd",NULL);
    struct stat buf;
    if ( 0!=stat(default_ppd_file, &buf ) ) {      
      g_warning("PPD not found in usual CUPS place: %s \n",default_ppd_file);
      g_free( default_ppd_file);
      default_ppd_file=NULL;
    }
  }

  /* Check the local default after looking for the "global" default */
  user_ppd_path = g_strconcat(local_ppd->config_dir, "/ppd.conf", NULL);
  user_ppd = fopen((char *)user_ppd_path, "r");
  if (user_ppd != NULL) {
    for (;;) {
      if (fgets(curr_line, sizeof(curr_line), user_ppd)
	  == NULL)		/* end of file */
	break;
      if (curr_line != NULL) {
	if (strstr(curr_line, ".ppd") != NULL) {
	  end = strstr(curr_line, "\n");
	  *end = '\0';
	  ppd_file = g_strdup(curr_line);
	  break;
	} else
	  continue;
      } else
	continue;
    }
    fclose(user_ppd);
  }

  if ((ppd_file == NULL) && (default_ppd_file == NULL)
      && (printcap_ppd_file == NULL)) {
    g_printerr
      (_("Unable to find a default PPD file.\nPlease select a PPD file with the file browser or\nedit the printcap file section for this printer,\nadding an entry under \"ppdfile=\".\n"));
    return;
  }

  /*----------------------------------------------------------
    If the printcap-retrieved ppdfile exists, then
    prepend the master_ppddir directory to the file name, unless
    it already contains a path.
  ----------------------------------------------------------*/
  if (printcap_ppd_file != NULL) {
    if (printcap_ppd_file[0] == '/')	/* "PPDFILE" specified as with full
					   path */
      default_ppd_file_path = printcap_ppd_file;
    else			/* "PPDFILE" specified with relative path */
      default_ppd_file_path =
	g_strjoin("/", MASTER_PPDDIR, printcap_ppd_file, NULL);
  }
  /*----------------------------------------------------------
    If the postscript.cfg-retrieved ppdfile exists, then
    prepend the spool directory to the file name, unless
    it already contains a path.
  ----------------------------------------------------------*/
  else if (default_ppd_file != NULL) {
    if (strstr(default_ppd_file, "/"))	/* "PPDFILE" specified as with full
					   path */
      default_ppd_file_path = default_ppd_file;
    else			/* "PPDFILE" specified with relative path */
      default_ppd_file_path =
	g_strjoin("/", spool_path, default_ppd_file, NULL);
  }

  if ((ppd_file == NULL) && (printcap_ppd_file != NULL))
    ppd_file = g_strdup(default_ppd_file_path);
  else if ((ppd_file == NULL) && (default_ppd_file != NULL))
    ppd_file = g_strdup(default_ppd_file);

  if (ppd_file != NULL) {
    if (strstr(ppd_file, "/"))	/* "PPDFILE" specified as with full path */
      ppd_file_path = ppd_file;
    else			/* "PPDFILE" specified with relative path */
      ppd_file_path = g_strjoin("/", MASTER_PPDDIR, ppd_file, NULL);

#ifdef USE_GNOMEFILEENTRY
    gtk_entry_set_text(GTK_ENTRY
		       (gnome_file_entry_gtk_entry
			(GNOME_FILE_ENTRY(local_ppd->ppd_fileentry))),
		       ppd_file_path);
#else
    gtk_file_chooser_set_filename( GTK_FILE_CHOOSER(local_ppd->ppd_fileentry),ppd_file_path);
#endif
    local_ppd->ppd_file = g_strdup(ppd_file_path);
    local_ppd->default_ppd_file = g_strdup(default_ppd_file_path);
  } else {
#ifdef USE_GNOMEFILEENTRY
    gtk_entry_set_text(GTK_ENTRY
		       (gnome_file_entry_gtk_entry
			(GNOME_FILE_ENTRY(local_ppd->ppd_fileentry))), "");
#endif
  }
#ifdef USE_GNOMEFILEENTRY
  gtk_widget_activate(gnome_file_entry_gtk_entry
		      (GNOME_FILE_ENTRY(local_ppd->ppd_fileentry)));
#endif
}


/*
 * Initializes the ppd file specified in the ppd file entry
 */
void init_ppd(GtkButton * button, gpointer ppd)
{
  ppd_struct *local_ppd;	/* local copy of ppd struct */
  PpdFile *local_ppd_handle = NULL;	/* local pointer to a ppd file
					   structure */
  char *local_file;		/* name of a local ppd file */
  gchar *temp_string;		/* temporary string */
  PpdConstraint *constr_ptr;	/* pointer to a UI constraint structure */
  FILE *settlist;		/* file containing list of saved settings */
  char curr_line[80];		/* a line of input from a file */
  gchar *current_setting;	/* current saved setting in the list of saved
				   settings */
  char *end;			/* end of a temporary string */
  GtkWidget *savesett_menu_item[20];	/* space for 20 saved settings */
  int savesett_count = 0;	/* count of saved settings */

  /* List iterators */
  GSList *list;

  /* redirect stderr to dialog */
  g_set_printerr_handler((GPrintFunc) error_box);

  local_ppd = ppd;

  local_file = local_ppd->ppd_file;

  if ((local_file == NULL) || (g_strcasecmp(local_file, "") == 0)) {
    return;
  }

  /* 
   * Initialize lists and hash tables
   */
  if (local_ppd->choice_list != NULL) {
    g_slist_free(local_ppd->choice_list);
    local_ppd->choice_list = NULL;
  }

  if (local_ppd->inst_opt_list != NULL) {
    g_slist_free(local_ppd->inst_opt_list);
    local_ppd->inst_opt_list = NULL;
  }

  /* create a new menu of saved settings */
  gtk_option_menu_remove_menu(GTK_OPTION_MENU(local_ppd->savesett_optionmenu));
  local_ppd->savesett_optionmenu_menu = gtk_menu_new();
  gtk_option_menu_set_menu(GTK_OPTION_MENU(local_ppd->savesett_optionmenu),
			   local_ppd->savesett_optionmenu_menu);

  /* add something to beggining of menu */
  savesett_menu_item[savesett_count] =
    gtk_menu_item_new_with_label("(current settings)");
  gtk_menu_prepend(GTK_MENU(local_ppd->savesett_optionmenu_menu),
		   savesett_menu_item[savesett_count]);
  gtk_menu_set_active(GTK_MENU(local_ppd->savesett_optionmenu_menu),
		      savesett_count);
  gtk_widget_realize(savesett_menu_item[savesett_count]);
  ++savesett_count;

  /* 
   * fill the saved settings list with all of the previously defined settings, if available
   */
  settlist = fopen((char *)
		   g_strconcat(local_ppd->config_dir, "/.settlist", NULL), "r");
  if (settlist != NULL) {
    for (;;) {
      if (fgets(curr_line, sizeof(curr_line), settlist)
	  == NULL)
	break;
      if (curr_line != NULL) {
	end = strstr(curr_line, "\n");
	*end = '\0';
	current_setting = g_strdup(curr_line);

	savesett_menu_item[savesett_count] =
	  gtk_menu_item_new_with_label(current_setting);
	gtk_menu_append(GTK_MENU(local_ppd->savesett_optionmenu_menu),
			savesett_menu_item[savesett_count]);

	if (local_ppd->setting_name != NULL) {
	  if (g_strcasecmp(current_setting, local_ppd->setting_name) == 0) {
	    /* this doesn't seem to be working */
	    gtk_menu_set_active(GTK_MENU(local_ppd->savesett_optionmenu_menu),
				savesett_count);
	  }
	}

	gtk_widget_realize(savesett_menu_item[savesett_count]);

	gtk_object_set_data(GTK_OBJECT(savesett_menu_item[savesett_count]), SS,
			    g_strdup(current_setting));
	gtk_signal_connect(GTK_OBJECT(savesett_menu_item[savesett_count]),
			   "activate", GTK_SIGNAL_FUNC(retrieve_sett),
			   local_ppd);
	++savesett_count;
	continue;
      } else
	continue;
    }
  }

  gtk_widget_show_all(local_ppd->savesett_optionmenu_menu);

  local_ppd_handle = malloc(sizeof(PpdFile));
  memset(local_ppd_handle, 0, sizeof(PpdFile));

  /* 
   * Check to see if the name is a file or a directory
   */
  if (!is_file((gchar *) local_file)) {
    g_printerr(_("ERROR!\n\n" "Invalid file name. The PPD file \n%s\n"
	       "associated with this printer doesn't\n"
	       "exist. You can either install the\n"
	       "PPD file or select another PPD file.\n"),local_file);

    return;
  }
  local_ppd_handle = ppd_file_new(local_file);


  // check handle here, output error message if null
  if (local_ppd_handle == NULL) {
    g_printerr
      (_("Invalid PPD file.\nPlease select a PPD file with the file browser or\nedit the printcap file section for this printer,\nadding an entry under \"ppdfile=\".\n"));
    return;
  }

  if (local_ppd->constr_hash != NULL) {
    g_hash_table_foreach_remove(local_ppd->constr_hash, (GHRFunc)
				ensure_remove_hash_item, NULL);
    g_hash_table_destroy(local_ppd->constr_hash);
  }

  if (local_ppd->cr_to_hr != NULL) {
    g_hash_table_foreach_remove(local_ppd->cr_to_hr, (GHRFunc)
				ensure_remove_hash_item, NULL);
    g_hash_table_destroy(local_ppd->cr_to_hr);
  }


  local_ppd->ppd_handle = local_ppd_handle;

  /* 
   * Now devise a hash table for constraints
   * The key is really important, not the data
   * We just want a quick lookup to see if it's in the list
   */
  local_ppd->constr_hash = g_hash_table_new(g_str_hash, g_str_equal);

  /* 
   * Hash table to quickly convert computer readable text to 
   * human readable text
   */
  local_ppd->cr_to_hr = g_hash_table_new(g_str_hash, g_str_equal);

  list = (GSList *) local_ppd_handle->consts;

  while (list) {
    constr_ptr = list->data;
    list = g_slist_next(list);
    /* MLP: modified from "option1" to "option1->str" */
    if (constr_ptr->option1 && constr_ptr->choice1 && 
	constr_ptr->option2 && constr_ptr->choice2) {
    temp_string =
      g_strjoin(":", constr_ptr->option1->str, constr_ptr->choice1->str,
		constr_ptr->option2->str, constr_ptr->choice2->str, NULL);

    /* this is somewhat silly, but we need quick lookup. we don't care about
       the 1 */
    g_hash_table_insert(local_ppd->constr_hash, temp_string,
			GINT_TO_POINTER(1));
    }
  }


  free(local_ppd_handle);
  local_ppd_handle = NULL;
}


/*
 * Set the name of the spool dir in the ppd structure
 */
void set_spool_dir(GtkWidget * menu_item, gpointer ppd)
{
  ppd_struct *local_ppd;	/* local copy of ppd_struct */
  gchar *spool_dir_name;	/* the spool directory */

  local_ppd = ppd;

  spool_dir_name = g_strdup(gtk_object_get_data(GTK_OBJECT(menu_item), SD));

  local_ppd->spool_dir = g_strdup(spool_dir_name);
}


/*
 * Set the name of the printer in the ppd structure and set up config directories
 * if they haven't already been created.
 */
void set_printer_name(GtkWidget * menu_item, gpointer ppd)
{
  ppd_struct *local_ppd;	/* local copy of ppd struct */
  gchar *p_name;		/* printer's name */
  gchar *config_dir;		/* printer's config directory */

  gchar *errorstr = NULL;

  local_ppd = ppd;

  /* redirect stderr to dialog */
  g_set_printerr_handler((GPrintFunc) error_box);

  p_name = g_strdup(gtk_object_get_data(GTK_OBJECT(menu_item), PN));

  local_ppd->printer_name = g_strdup(p_name);

  /* 
   * Here, we set up some configuration items. If a config directory
   * for this printer does not exist, set one up.
   */
  /* first check if a gpr config directory exists. if not, make one */
  config_dir = g_strconcat(g_get_home_dir(), "/.gpr", NULL);
  if (chdir((char *)config_dir) != 0) {
    if (mkdir((char *)config_dir, 0700) != 0) {
      /* OK, this needs to be more explanatory. will fix later */
      errorstr = g_strdup_printf (_("Unable to create configuration "
			      "directory (%s).\n"
			      "Make sure you have adequate file "
			      "permissions for this directory.\n\n"
			      "Session settings will not be saved.\n"),
			      config_dir);
      g_printerr(errorstr);
      g_free (errorstr);
    }
  }

  /* 
   * Now we know we have a general config directory. Now check and see
   * if there is a printer-specific config directory. If not, make one.
   */
  config_dir = g_strconcat(config_dir, "/", p_name, NULL);
  if (chdir((char *)config_dir) != 0) {
    if (mkdir((char *)config_dir, 0700) != 0) {
      /* OK, this needs to be more explanatory. will fix later */
      errorstr = g_strdup_printf (_("Unable to create configuration "
			      	"directory (%s).\n"
				"Make sure you have adequate file "
				"permissions for this directory.\n\n"
				"Session settings will not be saved.\n"),
		      		config_dir);
      g_printerr(errorstr);
      g_free (errorstr);
	
    }
  }

  /* set the configuration directory in the main ppd_struct */
  local_ppd->config_dir = g_strdup(config_dir);

  /* 
   * While we're at it, let's see if there's a saved settings subdir
   * for this printer. If not, make one.
   */
  config_dir = g_strconcat(config_dir, "/savesetts", NULL);
  if (chdir((char *)config_dir) != 0) {
    if (mkdir((char *)config_dir, 0700) != 0) {
      /* no need for an error message here, the earlier one
	 tells the same story. */
    }
  }

}


/*
 * Sets up command line options and sends print job
 */
void send_print(GtkWidget * button, gpointer ppd)
{
  ppd_struct *local_ppd;	/* local copy of ppd struct */
  gchar *command_line = NULL;	/* string to send to shell */

  int is_sys_def_ppd = 0;	/* flag telling whether current ppd is system
				   default */
  int use_ppdfilt =0;           /* flag telling whether ppdfilt should be used
				   or not */
  int is_psfile = 0;		/* flag telling whether a file is a ps file */
  FILE *print_file = NULL;	/* file to print */
  char curr_line[80];		/* a line of input from a file */
  char *quoted_filename;	/* filename to pass to /bin/sh */

 
  local_ppd = ppd;

  /* redirect stderr to dialog */
  g_set_printerr_handler((GPrintFunc) error_box);

  if (local_ppd->file_to_print == NULL) {
    fprintf (stderr, _("No file to print was specified. Exitting Gpr.\n"));
    return;
  }
  
  /* A.Mennucci: mangle filename for /bin/sh */
  {
    /* FIXME what happens if this is UTF-8 (answer: a mess)???*/
    gchar *p=local_ppd->file_to_print;
    int a=0,b=1, l=strlen(p);
    quoted_filename=calloc(2,1+l); 
    quoted_filename[0]='\'';
    while(a<l) { /* an exercise in compact C */
      if ( p[a] == '\'' ){
	quoted_filename[b++]='\'';
	quoted_filename[b++]='\\';
	quoted_filename[b++]='\'';
      }
      //if( p[a] == '\\' )	quoted_filename[b++]='\\';
      quoted_filename[b++]=p[a++];
    }
    quoted_filename[b++]='\'';
  }

  if (local_ppd->ppd_file && local_ppd->default_ppd_file &&
      g_strcasecmp(local_ppd->ppd_file, local_ppd->default_ppd_file)
      == 0)
    is_sys_def_ppd = 1;

  use_ppdfilt = !(((is_sys_def_ppd) || (local_ppd->ppdfilt == 0)) && 
		  (local_ppd->ppdfilt != 1));

  if (!use_ppdfilt) {
    is_psfile = 1;	/* doesn't matter if ps or not, but we need the 
				   flag */
    command_line = g_strdup(cmdname);
    command_line =
      g_strconcat(command_line, local_ppd->printer_name, " ", NULL);
    if (local_ppd->lpr_opts != NULL)
      command_line = g_strconcat(command_line, local_ppd->lpr_opts, NULL);
  } else {
    //this is always true !!!
    if (local_ppd->file_to_print != NULL) 
    {
      print_file = fopen(local_ppd->file_to_print, "r");
      if (print_file != NULL) {
	fgets(curr_line, sizeof(curr_line), print_file);
	if (strstr(curr_line, "!PS") != NULL) {
	  is_psfile = 1;
	  command_line = g_strdup("ppdfilt -p ");
	} else {
	  /* not a PS file */
	  if (in_path("a2ps")) {
	    command_line = g_strdup("a2ps -1 -o - ");
	    extern GtkWidget *border_toggle;
	    extern GtkWidget *header_toggle;
	    if (!gtk_toggle_button_get_active (header_toggle))
	      command_line =
		g_strconcat(command_line," -B ",NULL);
	    if (!gtk_toggle_button_get_active (border_toggle))
	      command_line =
		g_strconcat(command_line,"  --borders=no ",NULL);
	    command_line =
	      g_strconcat(command_line, quoted_filename, " ", NULL);
	    command_line = g_strconcat(command_line, "| ppdfilt -p ", NULL);
	  } else {
	    g_printerr(_("Unable to print non-PostScript file.\nPlease install a2ps.\n"));
	    return;
	  }
	}

	fclose(print_file);
      } else {
	/* assume it is a PS file from an application */
	is_psfile = 1;
	command_line = g_strdup("ppdfilt -p ");
      }
    }
    command_line = g_strconcat(command_line, local_ppd->ppd_file, " ", NULL);
  }
  /*A. Mennucc:  open file only if it has reached this point(no errors)
    otherwise if it has an error while printing, it will only partially 
    write this file.
    I have created a subblock to reorganize the code a bit...
  */
  {
    gchar *current_option;	/* the string for the current option */
    FILE *last_used;		/* file containing list of last used settings */
    GSList *olist, *clist;
 
    last_used = fopen((char *)
		      g_strconcat(local_ppd->config_dir, "/.lastused.sett", NULL),
		      "w");
    /* first, associate settings with a ppd */
    if (last_used) fprintf (last_used, "PD:%s\n", local_ppd->ppd_file);

    /* handle non-ppd options */
    if (local_ppd->num_copies != NULL) {
      command_line = g_strconcat(command_line, local_ppd->num_copies, " ", NULL);
      if (last_used) fprintf (last_used, "CO:%s\n", local_ppd->num_copies);
    }

    if (local_ppd->n_up != NULL) {
      command_line = g_strconcat(command_line, local_ppd->n_up, " ", NULL);
      if (last_used) fprintf (last_used, "CO:%s\n", local_ppd->n_up);
    }

    if (local_ppd->page_range != NULL) {
      command_line = g_strconcat(command_line, local_ppd->page_range, " ", NULL);
      if (last_used) fprintf (last_used, "CO:%s\n", local_ppd->page_range);
    }

    if (local_ppd->page_set != NULL) {
      command_line = g_strconcat(command_line, local_ppd->page_set, " ", NULL);
      if (last_used) fprintf (last_used, "CO:%s\n", local_ppd->page_set);
    }

    if (local_ppd->collate != NULL) {
      command_line = g_strconcat(command_line, local_ppd->collate, " ", NULL);
      if (last_used) fprintf (last_used, "CO:%s\n", local_ppd->collate);
    }

    if (local_ppd->output_order != NULL) {
      command_line =
	g_strconcat(command_line, local_ppd->output_order, " ", NULL);
      if (last_used) fprintf (last_used, "CO:%s\n", local_ppd->output_order);
    }

    /* end non-ppd options */

    /* move through the linked list of installable options */
    olist = local_ppd->inst_opt_list;
    while (olist) {
      current_option = olist->data;
      olist = g_slist_next(olist);
      if (last_used) fprintf (last_used, "IO:%s\n", current_option);
    }

    /* move through the linked list of options */
    clist = local_ppd->choice_list;
    while (clist) {
      current_option = clist->data;
      clist = g_slist_next(clist);

      /* Will support more spoolers in future */
      switch (local_ppd->spooler) {
      case SPOOLER_LPRNG:			/* LPRng */
	command_line =
	  g_strconcat(command_line, "-Z ", current_option, " ", NULL);
	break;
      default:
	command_line =
	  g_strconcat(command_line, "-o ", current_option, " ", NULL);
	break;
      }

      if (last_used) fprintf (last_used, "AO:%s\n", current_option);
    }

    if (is_psfile)
      command_line =
	g_strconcat(command_line, " ", quoted_filename, NULL);

    if (use_ppdfilt) {
      command_line = g_strconcat(command_line, " | ", NULL);
      command_line = g_strconcat(command_line, cmdname, NULL);
      command_line =
	g_strconcat(command_line, local_ppd->printer_name, " ", NULL);
      if (local_ppd->lpr_opts != NULL)
	command_line = g_strconcat(command_line, local_ppd->lpr_opts, NULL);
    }

    if (last_used) fclose(last_used);
  }

  if ((local_ppd->debug_flag) || (local_ppd->norun_flag))
    g_print("%s\n", command_line);
  else
    system(command_line);
  
  free(quoted_filename);  

}


gboolean if_no_errors_quit(GtkWidget * main, gpointer data)
{
  /*A.Mennucc: do not exit if there are error messages around */
  extern int messages_refcount;
  if(messages_refcount>0) {
    return FALSE;
  }
  else {
    gtk_main_quit();
    return TRUE;
  }
}

/* A. Mennucc: clean_up is called from main() if and only if
 the program is really quitting */
void clean_up(GtkWidget * button, gpointer ppd)
{
  ppd_struct *local_ppd;	/* local copy of ppd struct */

  local_ppd = ppd;

  /* 
   * Check lists and hash tables and clear them
   */
  if (local_ppd->choice_list != NULL) {
    g_slist_free(local_ppd->choice_list);
    local_ppd->choice_list = NULL;
  }

  if (local_ppd->inst_opt_list != NULL) {
    g_slist_free(local_ppd->inst_opt_list);
    local_ppd->inst_opt_list = NULL;
  }

  if (local_ppd->constr_hash != NULL) {
    g_hash_table_foreach_remove(local_ppd->constr_hash, (GHRFunc)
				ensure_remove_hash_item, NULL);
    // g_hash_table_destroy(local_ppd->constr_hash);
  }

  if (local_ppd->cr_to_hr != NULL) {
    g_hash_table_foreach_remove(local_ppd->cr_to_hr, (GHRFunc)
				ensure_remove_hash_item, NULL);
    // g_hash_table_destroy(local_ppd->cr_to_hr);
  }

  /* free ppd structure */
  if (local_ppd->ppd_handle != NULL)
    free(local_ppd->ppd_handle);

  /* free any temporary files created */
  if (local_ppd->file_to_print != NULL) {
    if (strstr((char *)local_ppd->file_to_print, "/tmp/gpr"))
      unlink((char *)local_ppd->file_to_print);
  }

  /* quit program   gtk_main_quit();
     A.Mennucc No it  is called from main() before quitting
   */
}


/*
 * END INIT AND CONTROL CALLBACKS
 */


/*
 * BEGIN ADVANCED/PPD OPTION CALLBACKS
 */

/*-------------------------------------------------------------------
  Add a notebook tab.
-------------------------------------------------------------------*/
GtkWidget *add_notebook_tab (GtkWidget *widget, char *tab_title) {
  GtkWidget *label;
  GtkWidget *notebook;
  GtkWidget *vbox;
  GtkWidget *scroll;

  /* Find our notebook page. */
  extern GtkWidget *notebook1;

  /* We need a scroll_window for vbox, in case there are many options. */
  scroll = gtk_scrolled_window_new (NULL, NULL);
  gtk_container_add (GTK_CONTAINER(notebook1), scroll);
  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
                                 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
  gtk_widget_show (scroll);

  /* Create the vbox. */
  vbox = gtk_vbox_new(FALSE, 0);
  gtk_widget_show (vbox);

  /* Attach the scroll_window to our vbox. */
  gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scroll),
					 vbox);


  /* Assign a label to the new tab. */
  revalidate_utf8(tab_title);
  label = gtk_label_new (tab_title);
  gtk_notebook_set_tab_label (GTK_NOTEBOOK (notebook1), scroll,
			      label);

  return vbox;
}

/*-------------------------------------------------------------------
  Remove all extra notebook tabs.
-------------------------------------------------------------------*/
void remove_extra_notebook_tabs (GtkWidget *widget) {
  int tidx = 0;
  GtkWidget *notebook;

  /* Find our notebook page. */
    /* Find our notebook page. */
  extern GtkWidget *notebook1;

  while (extra_tabs[tidx].title) {
    free (extra_tabs[tidx].title);
    extra_tabs[tidx].title = NULL;

    /* Remove the actual tab (if we remove them in forward
       order, then the tab pos is always the same). */
    gtk_notebook_remove_page (GTK_NOTEBOOK (notebook1), 3);

    tidx++;
  }
}

/*=================================================================*/


/*
 * Grabs the advanced options from the ppd file and fills 
 * the advanced options tab
 * NOTICE: It may be possible to combine this function with init_ppd
 * The resulting function would be quite large, however
 */
void fill_option_menus(GtkWidget * button, gpointer ppd)
{
  ppd_struct *local_ppd;	/* local copy of ppd struct */
  char *local_file;		/* local ppd file name string */

  int i, l, m;			/* looping vars */

  int option_count;		/* count of non-install options */
  int inst_option_count;	/* count of installable options */

  char *match_title = NULL;	/* title of matched tab */

  GtkWidget *pagesize_combo_list;	/* page size option combo */

  /* pointers to mark our position in the ppd struct */
  PpdGroup *group_ptr;		/* pointer to current group */
  PpdGroup *subgroup_ptr;	/* pointer to current subgroup */
  PpdOption *option_ptr;	/* pointer to current option */
  PpdOption *sub_option_ptr;	/* pointer to current option under a subgroup */

  /* 
   * set up an array of widget pointers for the 
   * printer specific items
   */
  GtkWidget *hbox = NULL;
  GtkWidget *opt_combo;

  /* 
   * set up an array of widget pointers for the 
   * installable items
   */
  GtkWidget *hbox2;
  GtkWidget *iopt_combo;


  /* List iterators */
  GSList *glist, *olist, *sglist;

  local_ppd = ppd;
  local_file = local_ppd->ppd_file;

  /* we displayed an error message above, just exit the function here */
  if ((local_file == NULL) || (g_strcasecmp(local_file, "") == 0))
    return;

  /* 
   * Check to see if the name is a file or a directory
   */
  if (!is_file((gchar *) local_file)) {
    /* No error message here, shoud have caught this above */
    return;
  }
  local_ppd->ppd_handle = ppd_file_new(local_file);

  if (local_ppd->ppd_handle == NULL) {
    // no error message, done in previous function
    return;
  }

  gpr_set_ppd_saved_options(local_ppd);

  /* 
   * need to add code to handle most recently used options
   * and a set of saved options
   */

  /* clear any printer-specific items from a previous ppd */
  if (local_ppd->opt_vbox != NULL) {
    gtk_widget_hide(local_ppd->opt_vbox);
    gtk_widget_destroy(local_ppd->opt_vbox);
  }

  local_ppd->opt_vbox = gtk_vbox_new(FALSE, 0);
  //USELESS?? gtk_widget_ref(local_ppd->opt_vbox);
  gtk_container_add(GTK_CONTAINER(local_ppd->opt_viewport),
		    local_ppd->opt_vbox);

  /* clear any installable items from a previous ppd */
  if (local_ppd->inst_opt_vbox != NULL) {
    gtk_widget_hide(local_ppd->inst_opt_vbox);
    gtk_widget_destroy(local_ppd->inst_opt_vbox);
  }

  local_ppd->inst_opt_vbox = gtk_vbox_new(FALSE, 0);
  //USELESS ?? gtk_widget_ref(local_ppd->inst_opt_vbox);
  gtk_container_add(GTK_CONTAINER(local_ppd->inst_opt_viewport),
		    local_ppd->inst_opt_vbox);

  /* Remove any extra tabs that were previously created. */
  remove_extra_notebook_tabs (local_ppd->opt_vbox);

  option_count = 0;
  inst_option_count = 0;

  /* Loop through groups */
  glist = local_ppd->ppd_handle->groups;
  while (glist) {
    group_ptr = glist->data;
    glist = g_slist_next(glist);
    /* we want everything but the installable options */
    if (!((g_strcasecmp((group_ptr->text->str), "Options Installed") == 0)
	  || (g_strcasecmp((group_ptr->text->str), "InstallableOptions")
	      == 0)
	  || (g_strcasecmp((group_ptr->text->str), "Installable Options") == 0)
	  || (g_strcasecmp((group_ptr->text->str), "Installed Options")
	      == 0))) {

      /* Loop through options */
      olist = group_ptr->options;
      while (olist) {
	option_ptr = olist->data;
	olist = g_slist_next(olist);

	if (g_strcasecmp((option_ptr->keyword->str), "PageRegion") == 0) {
	  continue;
	}

	/* need to handle the PageSize case separately */
	if (g_strcasecmp((option_ptr->keyword->str), "PageSize") == 0) {

	  pagesize_combo_list =
	    gpr_combo_make(local_ppd, option_count, option_ptr,
			   local_ppd->pagesize_optionmenu, 0, select_option_choice);

	  gtk_widget_show_all(local_ppd->pagesize_optionmenu);
	  ++option_count;

	} else {		/* if this is not the PageSize option */
#ifdef HAVE_XML
	  match_title = tab_match (option_ptr->text->str, globalTabList);
#else
	  match_title = NULL;
#endif

	  /* We have a matching tab title. */
	  if (match_title) {
	    /* Have we already created this tab? */
	    int tidx = 0;
	    int tab_found = 0;
	    int tab_pos = -1;
	    while (extra_tabs[tidx].title) {
	      if (! strcmp (extra_tabs[tidx].title, match_title)) {
		tab_found = 1;
		tab_pos = tidx;
	      }
	      tidx++;
	    }

	    /* We need to add this as a new tab... */
	    if (! tab_found) {
	      /* Add a new tab. */
	      extra_tabs[tidx].vbox = add_notebook_tab (hbox, match_title);
	      extra_tabs[tidx].title = (char *) strdup (match_title);
	      tab_pos = tidx;
	    }

	    /* Now we need to add the option to this tab. */
	    hbox = gpr_hbox_make(extra_tabs[tab_pos].vbox);

	    gpr_label_make(option_ptr->text->str, hbox);

	    opt_combo = gpr_optcombo_make(hbox);
	    opt_combo =
	      gpr_combo_make(local_ppd, option_count, 
			    option_ptr, opt_combo, 0,
			    select_option_choice);

	    gtk_widget_show_all (extra_tabs[tab_pos].vbox);
	    ++option_count;
	  }
	  else {
	    hbox = gpr_hbox_make(local_ppd->opt_vbox);
	    gpr_label_make(option_ptr->text->str, hbox);

	    opt_combo = gpr_optcombo_make(hbox);
	    opt_combo =
	      gpr_combo_make(local_ppd, option_count, option_ptr, opt_combo, 0,
			    select_option_choice);

	    ++option_count;
	  }
	}
      }

      /* now go into subgroups  */
      /* Loop through subgroups */
      sglist = group_ptr->subgroups;
      l = -1;
      while (sglist) {
	l++;
	subgroup_ptr = sglist->data;
	sglist = g_slist_next(sglist);

	/* Loop through options */
	olist = subgroup_ptr->options;
	m = -1;
	while (olist) {
	  m++;
	  sub_option_ptr = olist->data;
	  olist = g_slist_next(olist);

	  hbox = gpr_hbox_make(local_ppd->opt_vbox);
	  gpr_label_make(sub_option_ptr->text->str, hbox);
	  opt_combo = gpr_optcombo_make(hbox);

	  opt_combo =
	    gpr_combo_make(local_ppd, option_count, sub_option_ptr,opt_combo,0,
			   select_option_choice);

	  ++option_count;

	}
      }
    }

    /* now, we want only the installable option (assume no subgroups) */
    else {
      /* Loop through options */
      olist = group_ptr->options;
      i = -1;
      while (olist) {
	option_ptr = olist->data;
	i++;
	olist = g_slist_next(olist);

	hbox2 = gpr_hbox_make(local_ppd->inst_opt_vbox);
	gpr_label_make(option_ptr->text->str, hbox2);
	iopt_combo = gpr_optcombo_make(hbox2);

	iopt_combo = 
	  gpr_combo_make(local_ppd, inst_option_count, option_ptr,iopt_combo,1,
			 select_inst_option_choice);

	++inst_option_count;

      }
    }
  }

  gtk_widget_show_all(local_ppd->opt_vbox);
  gtk_widget_show_all(local_ppd->inst_opt_vbox);

}

/*=================================================================*/
gchar * conflict_check 
(
 ppd_struct *local_ppd,
 GSList *oplist, 
 gchar *combined_string,
 gchar *question_string
) {
/*-------------------------------------------------------------------
  Check for conflicts based on "constraints" between options found
  in the PPD file.
-------------------------------------------------------------------*/
  gchar *temp_string;		/* a temporary string */
  gchar *constr_message = NULL;	/* the message to appear in the pop-up
				   constraint box */
  gchar *astr = NULL;
  gchar *bstr = NULL;
  GSList *top = oplist;
  int times = 0;

  astr = question_string;
  bstr = combined_string;

  while ((times <= 1) && (constr_message == NULL)) {
    oplist = top;

    while (oplist) {
      astr = oplist->data;
      oplist = g_slist_next(oplist);

      if (times == 0) 
	temp_string = g_strjoin(":", astr, bstr, NULL);
      else
	temp_string = g_strjoin(":", bstr, astr, NULL);

      if (g_hash_table_lookup(local_ppd->constr_hash, temp_string)) {

	/* stick something in the string so there is no error */
	if (constr_message == NULL)
	  constr_message = g_strdup("\0");

	constr_message =
	  g_strconcat(constr_message,
		      "Option \"", 
		      g_hash_table_lookup(local_ppd->cr_to_hr, bstr),
		      "\"\n conflicts with option\n\"", 
		      g_hash_table_lookup(local_ppd->cr_to_hr, astr),
		      "\"\n", NULL);
      }
    }

    times++;

  }

  return constr_message;
}

/*
 * Adds the choice from the advanced options tab to the
 * list of selected options and checks for any constraints
 */
void select_option_choice(GtkWidget * option_entry, gpointer ppd)
{
  gchar *object_keyword;	/* object keyword string */
  gchar *choice_keyword;	/* choice keyword string */
  gchar *combined_string;	/* object keyword:choice keyword */
  int index;			/* the index number associated with the item */
  ppd_struct *local_ppd;	/* local copy of ppd struct */
  gchar *question_string = NULL;/* value to hash on */
  gchar *constr_message = NULL;	/* the message to appear in the pop-up
				   constraint box */

  GSList *oplist, *ioplist;
  GList *combo_item_list;
  GtkWidget *option_combo, *combo_item = NULL;
  local_ppd = ppd;

  /* redirect stderr to dialog */
  g_set_printerr_handler((GPrintFunc) error_box);

  /* Search the chosen item in the list */
  option_combo = gtk_object_get_data(GTK_OBJECT(option_entry), OC);

#if USE_COMBO_BOX
  choice_keyword = gtk_combo_box_get_active_text(GTK_COMBO_BOX(option_combo)) ;
#else
  combo_item_list = gtk_container_children(GTK_CONTAINER(GTK_COMBO(option_combo)->list));
  while (combo_item_list) {
    combo_item = combo_item_list->data;
    if (g_strcasecmp(gtk_object_get_data(GTK_OBJECT(combo_item), CT),
		     gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(option_combo)->entry))) == 0) {
      break;
    } else {
      combo_item_list = g_list_next(combo_item_list);
    } 
  }
  object_keyword = g_strdup(gtk_object_get_data(GTK_OBJECT(combo_item), OK));
  choice_keyword = g_strdup(gtk_object_get_data(GTK_OBJECT(combo_item), CK));
#endif


  combined_string = g_strjoin(":", object_keyword, choice_keyword, NULL);

  index = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(combo_item), IN));

  /* 
   * need to add this string to the linked list of choices
   * need to associate an index with the choice to insert at the proper
   * spot in the linked list
   */
  local_ppd->choice_list =
    my_g_slist_replace(local_ppd->choice_list, combined_string, index);

  /* 
   * To check for conflicts, we will build a hash table of conflicts
   * in 'init_ppd'. Each choice will consult the hash table for a conflict
   * and display a pop-up dialog with all conflicting choices
   */

  /* conflict check option list */
  oplist = local_ppd->choice_list;
  constr_message = conflict_check (local_ppd, oplist, 
				   combined_string, question_string);

  /* now conflict check installable option list */
  if (constr_message == NULL) {
    ioplist = local_ppd->inst_opt_list;
    constr_message = conflict_check (local_ppd, ioplist, 
				     combined_string, question_string);
  }

  /* show constraint dialog if there are any conflicts */
  if (constr_message != NULL) {
    constr_message =
      g_strdup_printf(_("CONFLICTING CHOICES!\n\n%s\n"
			"Please change options to eliminate conflicts."),
		        constr_message);
    g_printerr(constr_message);
  }
}


/*
 * Adds the choice from the installable options tab to the
 * list of installable options and checks for any constraints
 */
void select_inst_option_choice(GtkWidget * option_entry, gpointer ppd)
{
  gchar *object_keyword;	/* object keyword string */
  gchar *choice_keyword;	/* choice keyword string */
  gchar *combined_string;	/* object keyword:choice keyword */
  int index;			/* the index number associated with the item */
  ppd_struct *local_ppd;	/* local copy of ppd struct */
  gchar *question_string = NULL;/* value to hash on */
  gchar *constr_message = NULL;	/* the message to appear in the pop-up
				   constraint box */

  GSList *oplist, *ioplist;
  GList *combo_item_list;
  GtkWidget *option_combo, *combo_item = NULL;
  local_ppd = ppd;

  /* redirect stderr to dialog */
  g_set_printerr_handler((GPrintFunc) error_box);

#if USE_COMBO_BOX
  choice_keyword = gtk_combo_box_get_active_text(GTK_COMBO_BOX(option_combo)) ;
#else
  /* Search the chosen item in the list */
  option_combo = gtk_object_get_data(GTK_OBJECT(option_entry), OC);
  combo_item_list = gtk_container_children(GTK_CONTAINER(GTK_COMBO(option_combo)->list));
  while (combo_item_list) {
    combo_item = combo_item_list->data;
    if (g_strcasecmp(gtk_object_get_data(GTK_OBJECT(combo_item), CT),
		     gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(option_combo)->entry))) == 0) {
      break;
    } else {
      combo_item_list = g_list_next(combo_item_list);
    } 
  }
  object_keyword = g_strdup(gtk_object_get_data(GTK_OBJECT(combo_item), OK));
  choice_keyword = g_strdup(gtk_object_get_data(GTK_OBJECT(combo_item), CK));
#endif

  combined_string = g_strjoin(":", object_keyword, choice_keyword, NULL);

  index = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(combo_item), IN));

  /* 
   * need to add this string to the linked list of choices
   * need to associate an index with the choice to insert at the proper
   * spot in the linked list
   */
  local_ppd->inst_opt_list =
    my_g_slist_replace(local_ppd->inst_opt_list, combined_string, index);

  /* 
   * To check for conflicts, we will build a hash table(?) of conflicts
   * in 'init_ppd'. Each choice will consult the hash table for a conflict
   * and display a pop-up dialog with all conflicting choices
   */

  /* check printer-specific option list for constraints */
  oplist = local_ppd->choice_list;
  constr_message = conflict_check (local_ppd, oplist, 
				   combined_string, question_string);

  /* now conflict-check installable option list */
  if (constr_message == NULL) {
    ioplist = local_ppd->inst_opt_list;
    constr_message = conflict_check (local_ppd, ioplist, 
				     combined_string, question_string);
  }

  /* if there are conflicts, show the constraint dialog */
  if (constr_message != NULL) {
    constr_message =
	g_strdup_printf(_("CONFLICTING CHOICES!\n\n%s\n"
		      "Please change options to eliminate conflicts."),
		      constr_message);
    g_printerr(constr_message);
  }
}


/*
 * Assigns some data to each PPD choice that is appended to a combo
 */
void choice_process(PpdOption * option, PpdChoice * choice, GtkWidget * combo,
		    int interator, gboolean inst_opt, GtkWidget * combo_item,
		    int count, gpointer ppd)
{
  gchar *key_string;		/* key value to serach in a hash table */
  gchar *value_string;		/* return value from hash */
  gchar *temp_string;		/* temporary string */
  ppd_struct *local_ppd;

  local_ppd = ppd;
  gtk_object_set_data(GTK_OBJECT(combo_item), OT, option->text->str);
  gtk_object_set_data(GTK_OBJECT(combo_item), OK, option->keyword->str);
  gtk_object_set_data(GTK_OBJECT(combo_item), CT, choice->text->str);
  gtk_object_set_data(GTK_OBJECT(combo_item), CK, choice->choice->str);
  gtk_object_set_data(GTK_OBJECT(combo_item), IN, GINT_TO_POINTER(count));
  //printf("IN %d OK %s OT %s CK %s CT %s \n" ,count,
  //	 option->keyword->str,option->text->str,
  //	 choice->choice->str,choice->text->str);

  key_string = g_strjoin(":", option->keyword->str, choice->choice->str, NULL);
  value_string = g_strjoin(":", option->text->str, choice->text->str, NULL);

  g_hash_table_insert(local_ppd->cr_to_hr, key_string, value_string);

  if (choice->marked) {
    revalidate_utf8(choice->text->str);
    gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(combo)->entry), choice->text->str);
    
    temp_string =
      g_strjoin(":", option->keyword->str, choice->choice->str, NULL);
    if (inst_opt)
      local_ppd->inst_opt_list=
	my_g_slist_replace(local_ppd->inst_opt_list,temp_string,count);
    else
      local_ppd->choice_list=
	my_g_slist_replace(local_ppd->choice_list,temp_string,count);
  }

  return;
}

/*
 * END ADVANCED/PPD OPTION CALLBACKS
 */


/*
 * BEGIN SAVED SETTINGS CALLBACKS
 */
/*
 * Saves the current settings to a named file
 */
void save_sett(GtkWidget * button, gpointer ppd)
{
  ppd_struct *local_ppd;	/* local copy of ppd struct */
  FILE *settlist;		/* file containing a list of settings for this
				   printer */
  FILE *setting;		/* the actual file list all of the settings */
  gchar *current_option;	/* the string for the current option */
  gchar *current_choice;	/* the string for the current choice */
  gchar *sett_name;		/* the name of the saved settings file */
  char curr_line[80];		/* a line of file input */
  gchar *curr_sett_name;	/* the name of the current setting in the file */
  char *end;			/* end of a string */
  int update_setting;		/* flag defining whether this is a new setting
				   or one needing updating */
  GtkWidget *menu_item;		/* a menu item */

  GSList *olist, *clist;

  local_ppd = ppd;

  /* redirect stderr to dialog */
  g_set_printerr_handler((GPrintFunc) error_box);

  /* assume a new setting */
  update_setting = 0;


  sett_name =
    g_strdup(gtk_entry_get_text(GTK_ENTRY(local_ppd->savesett_textentry)));
  gtk_entry_set_text(GTK_ENTRY(local_ppd->savesett_textentry), "");

  /* 
   * if no name is specified, do nothing
   */
  if ((g_strcasecmp(sett_name, "") == 0) || (sett_name == NULL)) {
    g_printerr(_("Please enter a name for your settings!"));
    return;
  } else
    local_ppd->setting_name = g_strdup(sett_name);

  /* open the list of settings, need to check if this one is already there */
  settlist = fopen((char *)
		   g_strconcat(local_ppd->config_dir, "/.settlist", NULL), "r");
  if (settlist != NULL) {
    for (;;) {
      if (fgets(curr_line, sizeof(curr_line), settlist)
	  == NULL)		/* end of file */
	break;
      if (curr_line != NULL) {
	end = strstr(curr_line, "\n");
	*end = '\0';
	curr_sett_name = g_strdup(curr_line);
	if (g_strcasecmp(curr_sett_name, sett_name)
	    == 0) {
	  update_setting = 1;
	  break;
	} else
	  continue;
      }
    }
    fclose(settlist);
  }

  setting = fopen((char *)
		  g_strconcat(local_ppd->config_dir, "/savesetts/", sett_name,
			      NULL), "w");

  /* If the file is writeable. */
  if (setting) {
    /* first, associate settings with a ppd */
    fprintf(setting, "PD:%s\n", local_ppd->ppd_file);

    /* handle non-ppd options */
    if (local_ppd->num_copies != NULL)
      fprintf(setting, "CO:%s\n", local_ppd->num_copies);

    if (local_ppd->n_up != NULL)
      fprintf(setting, "CO:%s\n", local_ppd->n_up);

    if (local_ppd->page_range != NULL)
      fprintf(setting, "CO:%s\n", local_ppd->page_range);

    if (local_ppd->page_set != NULL)
      fprintf(setting, "CO:%s\n", local_ppd->page_set);

    if (local_ppd->collate != NULL)
      fprintf(setting, "CO:%s\n", local_ppd->collate);

    if (local_ppd->output_order != NULL)
      fprintf(setting, "CO:%s\n", local_ppd->output_order);
    /* end non-ppd options */

    /* move through the linked list of installable options */
    olist = local_ppd->inst_opt_list;
    while (olist) {
      current_option = olist->data;
      olist = g_slist_next(olist);
      fprintf(setting, "IO:%s\n", current_option);
    }

    /* move through the linked list of choices */
    clist = local_ppd->choice_list;
    while (clist) {
      current_choice = clist->data;
      clist = g_slist_next(clist);
      fprintf(setting, "AO:%s\n", current_choice);
    }

    fclose(setting);
  }

  if (update_setting != 1) {
    settlist = fopen((char *)
		     g_strconcat(local_ppd->config_dir, "/.settlist", NULL),
		     "a");
    if (settlist) {
      fprintf(settlist, "%s\n", sett_name);
      fclose(settlist);
    }

    menu_item = gtk_menu_item_new_with_label(sett_name);
    gtk_menu_append(GTK_MENU(local_ppd->savesett_optionmenu_menu), menu_item);
    gtk_widget_show(menu_item);

    gtk_object_set_data(GTK_OBJECT(menu_item), SS, g_strdup(sett_name));
    gtk_signal_connect(GTK_OBJECT(menu_item), "activate",
		       GTK_SIGNAL_FUNC(retrieve_sett), (gpointer) local_ppd);
  }
}


/*
 * retrieve settings associated with a named saved setting
 */
void retrieve_sett(GtkWidget * menuitem, gpointer ppd)
{
  ppd_struct *local_ppd;

  local_ppd = ppd;

  local_ppd->setting_name =
    g_strdup(gtk_object_get_data(GTK_OBJECT(menuitem), SS));

  init_ppd(NULL, local_ppd);
  fill_option_menus(NULL, local_ppd);
}

/*
 * END SAVED SETTINGS CALLBACKS
 */



/*
 * BEGIN COMMON OPTION CALLBACKS
 */
/*
 * updates the copy count
 */
void update_num_copies(GtkSpinButton * spinbutton, gpointer ppd)
{
  gchar *num_copies;		/* number of copies */
  gchar *copy_command;		/* the copy command for ppdfilt */
  ppd_struct *local_ppd;

  local_ppd = ppd;

  /* Will support more spoolers in future */
  switch (local_ppd->spooler) {
  case SPOOLER_LPRNG:			/* LPRng */
    copy_command = g_strdup("-Z copies:");
    break;
  default:
    copy_command = g_strdup("-o copies:");
    break;
  }

  num_copies = gtk_entry_get_text(GTK_ENTRY(spinbutton));

  if (atoi(num_copies) > 1)
    copy_command = g_strconcat(copy_command, num_copies, NULL);
  else
    copy_command = NULL;

  local_ppd->num_copies = g_strdup(copy_command);
}


/*
 * Sets page range to all pages
 */
void update_pageset_all(GtkWidget * radiobutton, gpointer ppd)
{
  ppd_struct *local_ppd;

  local_ppd = ppd;

  local_ppd->page_set = NULL;
  local_ppd->page_range = NULL;
}


/*
 * Sets page range to odd pages only
 */
void update_pageset_odd(GtkWidget * radiobutton, gpointer ppd)
{
  ppd_struct *local_ppd;

  local_ppd = ppd;

  /* Will support more spoolers in future */
  switch (local_ppd->spooler) {
  case SPOOLER_LPRNG:			/* LPRng */
    local_ppd->page_set = g_strdup("-Z page-set:odd");
    break;
  default:
    local_ppd->page_set = g_strdup("-o page-set:odd");
    break;
  }

  local_ppd->page_range = NULL;
}


/*
 * Set page range to even pages only
 */
void update_pageset_even(GtkWidget * radiobutton, gpointer ppd)
{
  ppd_struct *local_ppd;

  local_ppd = ppd;

  /* Will support more spoolers in future */
  switch (local_ppd->spooler) {
  case SPOOLER_LPRNG:			/* LPRng */
    local_ppd->page_set = g_strdup("-Z page-set:even");
    break;
  default:
    local_ppd->page_set = g_strdup("-o page-set:even");
    break;
  }

  local_ppd->page_range = NULL;
}


/*
 * grabs "from" range and checks "to" range,
 * setting the page-range option if both are specified
 */
void update_range_from(GtkWidget * entrybox, gpointer ppd)
{
  gchar *from;			/* text of from box */
  gchar *range;			/* range text */
  ppd_struct *local_ppd;

  local_ppd = ppd;

  from = gtk_entry_get_text(GTK_ENTRY(entrybox));

  if (from != NULL)
    local_ppd->range_from = g_strdup(from);

  if ((local_ppd->range_to == NULL) ||	/* empty */
      (local_ppd->range_from == NULL) ||	/* empty */
      (local_ppd->range_to[0] < 48) ||	/* not a digit */
      (local_ppd->range_from[0] < 48) ||	/* not a digit */
      (local_ppd->range_to[0] > 57) ||	/* not a digit */
      (local_ppd->range_from[0] > 57))	/* not a digit */
    local_ppd->page_range = NULL;
  else {
    /* Will support more spoolers in future */
    switch (local_ppd->spooler) {
    case SPOOLER_LPRNG:			/* LPRng */
      range =
	g_strconcat("-Z page-ranges:", local_ppd->range_from, "-",
		    local_ppd->range_to, NULL);
      break;
    default:
      range =
	g_strconcat("-o page-ranges:", local_ppd->range_from, "-",
		    local_ppd->range_to, NULL);
      break;
    }

    local_ppd->page_range = g_strdup(range);
  }
}

/*
 * grabs "to" range and checks "from" range,
 * setting the page-range option if both are specified
 */
void update_range_to(GtkWidget * entrybox, gpointer ppd)
{
  gchar *to;			/* text of to entrybox */
  gchar *range;			/* range text */
  ppd_struct *local_ppd;

  local_ppd = ppd;

  to = gtk_entry_get_text(GTK_ENTRY(entrybox));

  if (to != NULL)
    local_ppd->range_to = g_strdup(to);

  if ((local_ppd->range_to == NULL) ||	/* empty */
      (local_ppd->range_from == NULL) ||	/* empty */
      (local_ppd->range_to[0] < 48) ||	/* not a digit */
      (local_ppd->range_from[0] < 48) ||	/* not a digit */
      (local_ppd->range_to[0] > 57) ||	/* not a digit */
      (local_ppd->range_from[0] > 57))	/* not a digit */
    local_ppd->page_range = NULL;
  else {

    /* Will support more spoolers in future */
    switch (local_ppd->spooler) {
    case SPOOLER_LPRNG:			/* LPRng */
      range =
	g_strconcat("-Z page-ranges:", local_ppd->range_from, "-",
		    local_ppd->range_to, NULL);
      break;
    default:
      range =
	g_strconcat("-o page-ranges:", local_ppd->range_from, "-",
		    local_ppd->range_to, NULL);
      break;
    }

    local_ppd->page_range = g_strdup(range);
  }
}


/*
 * Sets collate option to yes
 */
void update_collate_yes(GtkWidget * radiobutton, gpointer ppd)
{
  ppd_struct *local_ppd;

  local_ppd = ppd;

  /* Will support more spoolers in future */
  switch (local_ppd->spooler) {
  case SPOOLER_LPRNG:			/* LPRng */
    local_ppd->collate = g_strdup("-Z Collate:true");
    break;
  default:
    local_ppd->collate = g_strdup("-o Collate:true");
    break;
  }

}

/*
 * Sets collate option to no
 */
void update_collate_no(GtkWidget * radiobutton, gpointer ppd)
{
  ppd_struct *local_ppd;

  local_ppd = ppd;

  local_ppd->collate = NULL;
}


/*
 * Sets reverse order option to no
 */
void update_revordr_yes(GtkWidget * radiobutton, gpointer ppd)
{
  ppd_struct *local_ppd;

  local_ppd = ppd;

  /* Will support more spoolers in future */
  switch (local_ppd->spooler) {
  case SPOOLER_LPRNG:			/* LPRng */
    local_ppd->output_order = g_strdup("-Z OutputOrder:reverse");
    break;
  default:
    local_ppd->output_order = g_strdup("-o OutputOrder:reverse");
    break;
  }

}

/*
 * Sets reverse order option to yes
 */
void update_revordr_no(GtkWidget * radiobutton, gpointer ppd)
{
  ppd_struct *local_ppd;

  local_ppd = ppd;

  local_ppd->output_order = NULL;
}


/*
 * Sets n-up to 1-up
 */
void update_nup_1up(GtkWidget * radiobutton, gpointer ppd)
{
  ppd_struct *local_ppd;

  local_ppd = ppd;

  local_ppd->n_up = NULL;
}

/*
 * Sets n-up to 2 up
 */
void update_nup_2up(GtkWidget * radiobutton, gpointer ppd)
{
  ppd_struct *local_ppd;

  local_ppd = ppd;

  /* Will support more spoolers in future */
  switch (local_ppd->spooler) {
  case SPOOLER_LPRNG:			/* LPRng */
    local_ppd->n_up = g_strdup("-Z number-up:2");
    break;
  default:
    local_ppd->n_up = g_strdup("-o number-up:2");
    break;
  }

}

/*
 * Sets n-up to 4 up
 */
void update_nup_4up(GtkWidget * radiobutton, gpointer ppd)
{
  ppd_struct *local_ppd;

  local_ppd = ppd;

  /* Will support more spoolers in future */
  switch (local_ppd->spooler) {
  case SPOOLER_LPRNG:			/* LPRng */
    local_ppd->n_up = g_strdup("-Z number-up:4");
    break;
  default:
    local_ppd->n_up = g_strdup("-o number-up:4");
    break;
  }

}

/*
 * END COMMON OPTION CALLBACKS
 */


/*
 * BEGIN UI MODIFICATION CALLBACKS
 */
/*
 * "Ungreys" greyed-out entryboxes
 */
void ungrey_entrybox(GtkWidget * button, gpointer entrybox)
{
  gtk_widget_set_sensitive(entrybox, TRUE);
}


/*
 * "regreys" un-greyed-out entryboxes
 */
void regrey_entrybox(GtkWidget * button, gpointer entrybox)
{
  gtk_widget_set_sensitive(entrybox, FALSE);
}

/*
 * END UI MODIFICATION CALLBACKS
 */


/*
 * BEGIN STATIC FUNCTIONS
 */
/*-------------------------------------------------------------------
  Used by fill_option_menus() to construct an hbox widget.
-------------------------------------------------------------------*/
static GtkWidget *gpr_hbox_make(GtkWidget * opt_vbox)
{
  GtkWidget *hbox = NULL;

  hbox = gtk_hbox_new(TRUE, 0);
  //USELESS gtk_widget_ref(hbox);
  gtk_box_pack_start(GTK_BOX(opt_vbox), hbox, FALSE, FALSE, 0);
  //USELESS? gtk_widget_realize(hbox);

  return hbox;

}

/*-------------------------------------------------------------------
  Used by fill_option_menus() to construct a label widget.
-------------------------------------------------------------------*/
static GtkWidget *gpr_label_make(gchar * str, GtkWidget * hbox)
{
  GtkWidget *label;
  gchar *label_text;

  label_text = g_strdup(str);
  revalidate_utf8(label_text);
  label = gtk_label_new(label_text);
  //USELESS? gtk_widget_ref(label);
  gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
  //USELESS? gtk_widget_realize(label);

  return label;
}

/*-------------------------------------------------------------------
  Used by fill_option_menus() to construct an option_combo.
-------------------------------------------------------------------*/
static GtkWidget *gpr_optcombo_make(GtkWidget * hbox)
{
  GtkWidget *opt_combo;

#if USE_COMBO_BOX
  opt_combo = gtk_combo_box_new_text();
#else
  opt_combo = gtk_combo_new();
#endif
  gtk_entry_set_editable(GTK_ENTRY(GTK_COMBO(opt_combo)->entry), FALSE);
  //USELESS?? gtk_widget_ref(opt_combo);
  gtk_box_pack_start(GTK_BOX(hbox), opt_combo, FALSE, FALSE, 0);
  //gtk_widget_set_usize(opt_combo, 220, -2);
  //USELESS?? gtk_widget_realize(opt_combo);

  return opt_combo;
}

/*-------------------------------------------------------------------
  Used by fill_option_menus() to build a combo and its list items.
-------------------------------------------------------------------*/
static GtkWidget *gpr_combo_make(ppd_struct * local_ppd, int option_count,
				PpdOption * option_ptr,	/* pointer to current
							   option */
				GtkWidget * option_combo, gboolean inst_opt,
				GtkSignalFunc signal_func_ptr)
{
  int idx;
  GSList *clist;
  PpdChoice *choice_ptr;	/* pointer to current choice */

  clist = option_ptr->choices;
  idx = -1;
  while (clist) {
    choice_ptr = clist->data;
    clist = g_slist_next(clist);
    idx++;
    /* glist = g_list_append(glist, choice_ptr->text->str); */
    gpr_combo_choices_make(idx, local_ppd, option_count, option_ptr, option_combo,inst_opt);
    //signal_func_ptr);
  }

  gtk_object_set_data(GTK_OBJECT(GTK_COMBO(option_combo)->entry), OC, option_combo);

  /* handle selection of a combo item */
  gtk_signal_connect(GTK_OBJECT(GTK_COMBO(option_combo)->entry), "changed",
		     GTK_SIGNAL_FUNC(signal_func_ptr), local_ppd);

  return option_combo;
}

/*-------------------------------------------------------------------
  Used by gpr_combo_make() to fill in a combo item and its 
  callback function.
-------------------------------------------------------------------*/

static void gpr_combo_choices_make(int idx, ppd_struct * local_ppd,
				  int option_count, PpdOption * option_ptr,
				   /* pointer to current option */
				   GtkWidget * option_combo, gboolean inst_opt)
//GtkSignalFunc signal_func_ptr)
{
#if USE_COMBO_BOX
  gtk_combo_box_append(GTK_COMBO_BOX(option_combo), g_strdup(g_slist_nth_data(option_ptr->choices, idx));
#else

  PpdChoice *choice_ptr;	/* pointer to current choice */
  gchar *choice_label;
  

  choice_ptr = g_slist_nth_data(option_ptr->choices, idx);

  choice_label = g_strdup(choice_ptr->text->str);


  GtkWidget *item, *label;
  item = gtk_list_item_new();
  gtk_widget_show (item);
  revalidate_utf8(choice_label);
  label = gtk_label_new(choice_label);
  gtk_container_add (GTK_CONTAINER (item), label);
  gtk_widget_show (label);
  gtk_combo_set_item_string (GTK_COMBO (option_combo), GTK_ITEM (item),
			     choice_label);
  gtk_container_add (GTK_CONTAINER (GTK_COMBO (option_combo)->list), item);
  /* associate strings with each object for retrieval later */
  /* add to hash table */
  /* deal with defaults or presets */
  choice_process(option_ptr, choice_ptr, option_combo, idx, inst_opt,
		 item, option_count, (gpointer) local_ppd);

  //USELESS?? gtk_widget_realize(item);

  /* handle selection of a combo item */
  /*gtk_signal_connect(item, "activate",
    GTK_SIGNAL_FUNC(signal_func_ptr), local_ppd);*/
#endif
}


/*-------------------------------------------------------------------
-------------------------------------------------------------------*/
void set_common_option_defaults (ppd_struct * local_ppd) {
  GtkWidget *target = NULL;

  /* copies defaults to "1" */
  target = numcopies_spinbutton;
  gtk_entry_set_text (GTK_ENTRY(target), "1");
  update_num_copies (GTK_SPIN_BUTTON(target), (gpointer) local_ppd);

  /* number-up defaults to "1" */
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (nup_1up_radiobutton), TRUE);

  /* Collate defaults to false */
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (collate_no_radiobutton), TRUE);

  /* OutputOrder defaults to "not reverse" */
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (revordr_no_radiobutton), TRUE);

}

/*-------------------------------------------------------------------
-------------------------------------------------------------------*/
void set_common_option (char *line, ppd_struct * local_ppd) {
  char *curr_line = NULL;
  char *option = NULL;
  char *choice = NULL;
  char *end = NULL;
  GtkWidget *target = NULL;

  if (! line) return;
  curr_line = (char *) strdup (line);

  /* parse */
  option = curr_line + 6;	/* skip over "CO:-o " */
  end = strchr(option, '\n');
  if (end)
    *end = '\0';
  choice = strchr(option, ':');
  if(choice)
    *choice = '\0';
  choice++;

  if (! strcmp (option, "copies")) {
    int num = 0;
    num = atoi (choice);
    if (num <= 0) return;

    target = numcopies_spinbutton;
    revalidate_utf8(choice);
    gtk_entry_set_text (GTK_ENTRY(target), choice);
    update_num_copies (GTK_SPIN_BUTTON(target), (gpointer) local_ppd);
  }
  else if (! strcmp (option, "number-up")) {
    int num = 0;
    num = atoi (choice);
    if ((num != 1) && (num != 2) && (num != 4)) return;

    if (num == 1) {
      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (nup_1up_radiobutton), TRUE);
    }
    else if (num == 2) {
      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (nup_2up_radiobutton), TRUE);
    }
    else if (num == 4) {
      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (nup_4up_radiobutton), TRUE);
    }
  }
  else if (! strcmp (option, "Collate")) {
    if (! strcmp (choice, "true")) {
      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (collate_yes_radiobutton), TRUE);
    }
  }
  else if (! strcmp (option, "OutputOrder")) {
    if (! strcmp (choice, "reverse")) {
      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (revordr_yes_radiobutton), TRUE);
    }
  }
}

/*-------------------------------------------------------------------
-------------------------------------------------------------------*/
void print_ppd_data (ppd_struct *local_ppd) {
  /* List iterators */
  GSList *glist, *olist, *clist;
  /* pointers to mark our position in the ppd struct */
  PpdGroup *group_ptr;		/* pointer to current group */
  PpdOption *option_ptr;	/* pointer to current option */
  PpdChoice *choice_ptr;	/* pointer to current choice */
  char marked;

  /* Loop through groups */
  glist = local_ppd->ppd_handle->groups;
  while (glist) {
    group_ptr = glist->data;
    glist = g_slist_next(glist);

    printf ("glist (%s)\n", group_ptr->text->str);
    /* we want everything but the installable options */

    /* Loop through options */
    olist = group_ptr->options;
    while (olist) {
      option_ptr = olist->data;
      olist = g_slist_next(olist);

      printf ("\tolist (%s)\n", option_ptr->keyword->str);

      clist = option_ptr->choices;
      printf ("\tdefchoice (%s)\n", option_ptr->defchoice->str);

      while (clist) {
	choice_ptr = clist->data;
	clist = g_slist_next(clist);
	marked = ' ';
	if (choice_ptr->marked)
	  marked = '*';

	printf ("\t\tclist %c (%s)(%s)\n", marked, choice_ptr->choice->str, choice_ptr->text->str);
      }
    }
  }

}

/*-------------------------------------------------------------------
  Used by fill_option_menus() to set any saved PPD options.
-------------------------------------------------------------------*/
static void gpr_set_ppd_saved_options(ppd_struct * local_ppd	/* local copy
								   of ppd
								   struct */ )
{
  FILE *last_used;		/* The file containing the list of last used
				   settings */
  char curr_line[100];		/* current line of file input */
  char *end;			/* the end of a string */
  char *option;			/* the opiton to mark */
  char *choice;			/* the choice to mark */
  char *tmp_ppd;		/* the ppd file listed in the saved settings
				   file */

  if (local_ppd->setting_name == NULL)
    last_used = fopen((char *)
		      g_strconcat(local_ppd->config_dir, "/.lastused.sett",
				  NULL), "r");
  else
    last_used = fopen((char *)
		      g_strconcat(local_ppd->config_dir, "/savesetts/",
				  local_ppd->setting_name, NULL), "r");

  /* go ahead and mark defaults options in the ppd */
  ppd_mark_defaults(local_ppd->ppd_handle);

  set_common_option_defaults (local_ppd);

  /*A. Mennucc: /etc/papersize is used in Debian as a system wide default */
  {
    FILE *f= fopen("/etc/papersize","r");
    if ( f ) {
      if (fgets(curr_line, sizeof(curr_line), f)) {
	//upcase first letter
	if( curr_line[0] >= 'a'  && curr_line[0] <= 'z')
	  curr_line[0] += 'A' - 'a';
	end = strchr(curr_line, '\n');
	if (end)
	  *end = '\0';
	ppd_mark_option(local_ppd->ppd_handle, "PageSize", curr_line);
      }
      fclose(f);
    }
  }

  /* now check the last used settings */
  if (last_used != NULL) {
    for (;;) {
      if (fgets(curr_line, sizeof(curr_line), last_used)
	  == NULL)		/* end of file */
	break;
      if (curr_line[0] == 80) {	/* the ppd file name ":PD" */
	tmp_ppd = curr_line + 3;
	end = strchr(tmp_ppd, '\n');
	if (end)
	  *end = '\0';
	if (strcmp(tmp_ppd, local_ppd->ppd_file) == 0)
	  continue;
	else
	  break;
      } else if (curr_line[0] == 67) {	/* a common option ":CO" */
	// do nothing for now
	set_common_option (curr_line, local_ppd);
	continue;
      } else if (curr_line[0] == 65 ||	/* an advanced option ":AO" */
		 curr_line[0] == 73) {	/* an installable option ":IO" */
	option = curr_line + 3;
	end = strchr(option, '\n');
	if (end)
	  *end = '\0';
	choice = strchr(option, ':');
	if (choice)
	  *choice = '\0';
	++choice;
	ppd_mark_option(local_ppd->ppd_handle, option, choice);
	continue;
      } else
	continue;
    }
  }

}

/*
 * Returns TRUE to force the removal of a hash entry
 */
static gboolean ensure_remove_hash_item(gpointer key, gpointer value,
					gpointer user_data)
{
  return (TRUE);
}


/*
 * Function to determine whether an executable file is in the user's
 * path
 * Greatly Inspired by Craig Tanis <crt@weblar.org>
 */
static int in_path(gchar * filename)
{
  gchar *path_name;
  gchar *curr_path;
  char buf[256];
  struct stat s;

  path_name = g_strdup(g_getenv("PATH"));

  for (curr_path = strtok(path_name, ":"); curr_path;
       curr_path = strtok(NULL, ":")) {
    g_snprintf(buf, 256, "%s/%s", curr_path, filename);
    if (!stat(buf, &s)) {
      g_free(path_name);
      return 1;
    }
  }

  g_free(path_name);
  return 0;
}

/*
 * Function to tell whether a file is a regular file (not a directory)
 */
static int is_file(gchar * filename)
{
  char buf[256];
  struct stat s;

  g_snprintf(buf, 256, "%s", filename);

  if (stat(buf, &s) != 0)
    return 0;
  else {
    if (S_ISREG(s.st_mode))
      return 1;
    else
      return 0;
  }

}

/*
 * END STATIC FUNCTIONS
 */
