[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

gEDA-user: Using GOptionContext to parse command line arguments



Dear friends,

  I have modified gschem.c to use command line arguments from
GOptionContext.  I did not find the version control repository from
gEDA, so I have used the source from the Ubuntu deb for version 1.6.1 as
a base.  Instead of sending a patch, I will hereafter enumerate the
changes.

I. When including:

        #include <glib/gi18n.h>
        
to define _() and N_()

II. Just before main_prog, tell which are the command line arguments and
respective variables.

        static const gchar **remaining_files =
        NULL;                                                                                                                                                                                                                                                            
                                       
        /*! The command line options recognized by the application, to
        be added to those of GTK+.
        */                                                        
        static const GOptionEntry cmdline_options[] =
        {                                                                                                     
            { "quiet", 'q', 0, G_OPTION_ARG_NONE,
        &quiet_mode,                                                                                              
              N_("Quiet mode"),
        NULL },                                                                                                                     
            { "verbose", 'v', 0, G_OPTION_ARG_NONE,
        &verbose_mode,                                                                                          
              N_("Verbose_mode"),
        NULL },                                                                                                                   
            { "auto-place", 'p', 0, G_OPTION_ARG_NONE,
        &auto_place_mode,                                                                                    
              N_("Automatically place window"),
        NULL },                                                                                                     
            { "stroke-info", 't', 0, G_OPTION_ARG_NONE,
        &stroke_info_mode,                                                                                       
              N_("Print stroke information"),
        NULL },                                                                                                       
            { "rc", 'r', 0, G_OPTION_ARG_FILENAME,
        &rc_filename,                                                                                            
              N_("RC file name"),
        N_("filename") },                                                                                                         
            { "script", 's', 0, G_OPTION_ARG_FILENAME,
        &rc_filename,                                                                                        
              N_("Script (guile) file name"),
        N_("filename") },                                                                                             
            { "output", 'o', 0, G_OPTION_ARG_FILENAME,
        &rc_filename,                                                                                        
              N_("Output filename (for printing)"),
        N_("filename") },                                                                                       
            { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY,
        &remaining_files,                                                                      
              N_("The files to open."),
        N_("[files...]") },                                                                                                 
        
        { NULL }                                                                                                                                        
        };
        
We have all the command line options pot in the globals.c  Now, the
remaining files to open are writen in the variable <remaining_files>.
We either pass in no file, and then <remaining_files> is NULL, or it is
a NULL terminated array of strings.

III. Finally, main_prog.  There are quite a few changes.  We have to
declare a GError and a GOptionContext first (if it was only for GCC...).
We replace gtk_main and parse_cmdline with GOptionContext creation,
parsing and error handling.  Finally, we open the files in
<remaining_files> without the need anymore of argv_index.

I have not compiled the code, because I cannot compile in this machine,
but I have used the same code in various other applications, and I
actually copied, pasted and adapted from one of my applications.  Please
tell me if it fits your purposes.

I enclose the whole file, as main_prog is quite large.


Sorry for the trouble, but I think that being the first time I actually
have a look at the code, and not being the latest version control code,
a patch could create more troubles than the ones that solves.

  Thanks for your attention,

    Francisco ColaÃo


-- 
Francisco Miguel ColaÃo <francisco.colaco@xxxxxxxxx>


/* gEDA - GPL Electronic Design Automation
 * gschem - gEDA Schematic Capture
 * Copyright (C) 1998-2010 Ales Hvezda
 * Copyright (C) 1998-2010 gEDA Contributors (see ChangeLog for details)
 *
 * 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 USA
 */
#include <config.h>
#include <version.h>

#include <stdio.h>
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#include <glib.h>
#include <glib/gi18n.h>

#include "gschem.h"

#ifdef HAVE_LIBDMALLOC
#include <dmalloc.h>
#endif

#ifdef HAVE_LOCALE_H
#include <locale.h>
#endif

typedef struct {
  gschem_atexit_func func;
  gpointer arg;
} gschem_atexit_struct;

static GList *exit_functions = NULL;

/*! \brief Register a function to be called on program exit
 *
 *  \par Function Description
 *  This function registers a function to be called on
 *  program exit. Multiple functions will be executed in
 *  the order they are registered.
 *
 *  \param [in] func a pointer to the function to be registered
 *  \param [in] data an arbitrary argument provided to the function
 *                   when it is called
 */
void gschem_atexit(gschem_atexit_func func, gpointer data)
{
  gschem_atexit_struct *p;

  p = g_new(gschem_atexit_struct, 1);
  p->func = func;
  p->arg = data;
  exit_functions = g_list_append(exit_functions, p);
}

/*! \brief Cleanup gSchem on exit.
 *  \par Function Description
 *  This function cleans up all memory objects allocated during the
 *  gSchem runtime.
 */
void gschem_quit(void)
{
  GList *list;
  gschem_atexit_struct *p;

  /* Call all registered functions in order */
  list = exit_functions;
  while(list != NULL) {
    p = (gschem_atexit_struct *) list->data;
    p->func(p->arg);
    g_free(p);
    list = g_list_next(list);
  }
  g_list_free(exit_functions);

  s_clib_free();
  s_slib_free();
  s_menu_free();
  /* o_text_freeallfonts();*/
  s_attrib_free();
  s_papersizes_free();
#ifdef HAVE_LIBSTROKE
  x_stroke_free ();
#endif /* HAVE_LIBSTROKE */
  o_undo_cleanup();
  /* s_stroke_free(); no longer needed */

  i_vars_freenames();
  i_vars_libgeda_freenames();

  /* x_window_free_head(); can't do this since it causes a
   * condition in which window_head->... is still being refered
   * after this */

  /* enable this to get more memory usage from glib */
  /* You also have to enable something in glib I think */
  /* g_mem_profile();*/
	 

  gtk_main_quit();
}



static const gchar **remaining_files = NULL;


/*! The command line options recognized by the application, to be added to those of GTK+. */
static const GOptionEntry cmdline_options[] = {
    { "quiet", 'q', 0, G_OPTION_ARG_NONE, &quiet_mode,
      N_("Quiet mode"), NULL },
    { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose_mode,
      N_("Verbose_mode"), NULL },
    { "auto-place", 'p', 0, G_OPTION_ARG_NONE, &auto_place_mode,
      N_("Automatically place window"), NULL },
    { "stroke-info", 't', 0, G_OPTION_ARG_NONE, &stroke_info_mode,
      N_("Print stroke information"), NULL },
    { "rc", 'r', 0, G_OPTION_ARG_FILENAME, &rc_filename,
      N_("RC file name"), N_("filename") },
    { "script", 's', 0, G_OPTION_ARG_FILENAME, &rc_filename,
      N_("Script (guile) file name"), N_("filename") },
    { "output", 'o', 0, G_OPTION_ARG_FILENAME, &rc_filename,
      N_("Output filename (for printing)"), N_("filename") },
    { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &remaining_files,
      N_("The files to open."), N_("[files...]") },
    { NULL }
};



/*! \brief Main Scheme(GUILE) program function.
 *  \par Function Description
 *  This function is the main program called from scm_boot_guile.
 *  It handles initializing all libraries and gSchem variables
 *  and passes control to the gtk main loop.
 */
void main_prog(void *closure, int argc, char *argv[])
{
  int i;
  char *cwd = NULL;
  GSCHEM_TOPLEVEL *w_current = NULL;
  char *input_str = NULL;
  int argv_index;
  int first_page = 1;
  char *filename;
  SCM scm_tmp;

  GError *error = NULL;
  GOptionContext *context;

#ifdef HAVE_GTHREAD
  /* Gschem isn't threaded, but some of GTK's file chooser
   * backends uses threading so we need to call g_thread_init().
   * GLib requires threading be initialised before any other GLib
   * functions are called. Do it now if its not already setup.  */
  if (!g_thread_supported ()) g_thread_init (NULL);
#endif

#if ENABLE_NLS
  /* this should be equivalent to setlocale (LC_ALL, "") */
  gtk_set_locale();

  /* This must be the same for all locales */
  setlocale(LC_NUMERIC, "C");

  /* Disable gtk's ability to set the locale. */ 
  /* If gtk is allowed to set the locale, then it will override the     */
  /* setlocale for LC_NUMERIC (which is important for proper PS output. */
  /* This may look funny here, given we make a call to gtk_set_locale() */
  /* above.  I don't know yet, if this is really the right thing to do. */
  gtk_disable_setlocale(); 

#endif

  /*  gtk_init(&argc, &argv);

  argv_index = parse_commandline(argc, argv);
  */

  context = g_option_context_new ("");
  g_option_context_set_summary (context, N_("gEDA Schematic editor."));
  //g_option_context_set_description (context, N_("Add text to display *after* the list of command line options in the help message"));
  g_option_context_set_translation_domain (context, "geda-gschem");
  g_option_context_add_main_entries (context, cmdline_options, "geda-gschem");
  g_option_context_add_group (context, gtk_get_option_group (TRUE));

  if (!g_option_context_parse (context, &argc, &argv, &error)) {
      switch (error->code) {
      case G_OPTION_ERROR_UNKNOWN_OPTION:
	  g_printerr (_("There was an unknown command line option: %s\n"),
		      error->message);
	  break;
      case G_OPTION_ERROR_BAD_VALUE:
	  g_printerr (_("There was a bad value passed to a command line option: %s\n"),
		      error->message);
	  break;
      case G_OPTION_ERROR_FAILED:
	  g_printerr (_("The command line parsing failed: %s\n"),
		      error->message);
	  break;
      default:
	  g_printerr (_("Unknown error %d when parsing the command line arguments: %s\n"),
		      error->code, error->message);
	  break;
	}

	g_clear_error (&error);
	g_option_context_free (context);
	return 1;
  }

  // Free the option context, for it is no longer needed.
  g_option_context_free (context);

  cwd = g_get_current_dir();

  libgeda_init();

  /* Install various libgeda callbacks */
  arc_draw_func = o_arc_draw;
  box_draw_func = o_box_draw;
  bus_draw_func = o_bus_draw;
  circle_draw_func = o_circle_draw;
  complex_draw_func = o_complex_draw;
  line_draw_func = o_line_draw;
  net_draw_func = o_net_draw;
  picture_draw_func = o_picture_draw;
  path_draw_func = o_path_draw;
  pin_draw_func = o_pin_draw;
  text_draw_func = o_text_draw;
  select_func = o_select_object;

  /* create log file right away even if logging is enabled */
  s_log_init ("gschem");

  s_log_message(
                _("gEDA/gschem version %s%s.%s\n"), PREPEND_VERSION_STRING,
                PACKAGE_DOTTED_VERSION, PACKAGE_DATE_VERSION);
  s_log_message(
                _("gEDA/gschem comes with ABSOLUTELY NO WARRANTY; see COPYING for more details.\n"));
  s_log_message(
                _("This is free software, and you are welcome to redistribute it under certain\n"));
  s_log_message(
                _("conditions; please see the COPYING file for more details.\n\n")); 

#if defined(__MINGW32__) && defined(DEBUG)
  fprintf(stderr, _("This is the MINGW32 port.\n"));
#endif  

#if DEBUG
  fprintf(stderr, _("Current locale settings: %s\n"), setlocale(LC_ALL, NULL));
#endif

  /* init global buffers */
  o_buffer_init();

  /* register guile (scheme) functions */
  g_register_funcs();

  /* initialise color map (need to do this before reading rc files */
  x_color_init ();

  o_undo_init(); 

  if (s_path_sys_data () == NULL) {
    gchar *message = _("You must set the GEDADATA environment variable!\n\n"
                       "gschem cannot locate its data files. You must set the GEDADATA\n"
                       "environment variable to point to the correct location.\n");
    GtkWidget* error_diag =
      gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_ERROR,
                              GTK_BUTTONS_OK,
                              message);
    gtk_dialog_run (GTK_DIALOG (error_diag));
    g_error (message);
  }

  /* Allocate w_current */
  w_current = gschem_toplevel_new ();
  w_current->toplevel = s_toplevel_new ();
  global_window_current = w_current;

  w_current->toplevel->load_newer_backup_func = x_fileselect_load_backup;
  w_current->toplevel->load_newer_backup_data = w_current;

  o_text_set_rendered_bounds_func (w_current->toplevel,
                                   o_text_get_rendered_bounds, w_current);

  /* Now read in RC files. */
  g_rc_parse_gtkrc();
  g_rc_parse(w_current->toplevel, "gschemrc", rc_filename);

  /* By this point, libgeda should have setup the Guile load path, so
   * we can take advantage of that.  */
  scm_tmp = scm_sys_search_load_path (scm_from_locale_string ("gschem.scm"));
  if (scm_is_false (scm_tmp)) {
    s_log_message (_("Couldn't find init scm file [%s]\n"), "gschem.scm");
  }
  input_str = scm_to_locale_string (scm_tmp);
  if (g_read_file(input_str) != -1) {
    s_log_message(_("Read init scm file [%s]\n"), input_str);
  } else {
    /*! \todo These two messages are the same. Should be
     * integrated. */
    s_log_message(_("Failed to read init scm file [%s]\n"),
                  input_str);
  }
  free (input_str); /* M'allocated by scm_to_locale_string() */
  scm_remember_upto_here_1 (scm_tmp);

  /* Load recent files list. This must be done
   * before calling x_window_setup(). */
  recent_files_load();
  gschem_atexit(recent_files_save, NULL);

  /* Set default icon */
  x_window_set_default_icon();

  /* At end, complete set up of window. */
  x_color_allocate();
  x_window_setup (w_current);

#ifdef HAVE_LIBSTROKE
  x_stroke_init ();
#endif /* HAVE_LIBSTROKE */

  if (remaining_files) {
      gchar *filename;

      for (int i = 0; remaining_files[i]; i++) {
	  if (g_path_is_absolute (remaining_files[i])) {
	      /* Path is already absolute so no need to do any concat of cwd */
	      filename = g_strdup (argv[i]);
	  } else {
	      filename = g_build_filename (cwd, argv[i], NULL);
	  }

	  if (first_page)
	      first_page = 0;
      }

      /*
       * SDB notes:  at this point the filename might be unnormalized, like
       * /path/to/foo/../bar/baz.sch.  Bad filenames will be normalized in
       * f_open (called by x_window_open_page). This works for Linux and MINGW32.
       */
      x_window_open_page(w_current, filename);
      g_free (filename);
  }

  /*
  for (i = argv_index; i < argc; i++) {

    if (g_path_is_absolute(argv[i]))
    {
      /* Path is already absolute so no need to do any concat of cwd */
      filename = g_strdup (argv[i]);
    } else {
      filename = g_build_filename (cwd, argv[i], NULL);
    }

    if ( first_page )
      first_page = 0;

    /*
     * SDB notes:  at this point the filename might be unnormalized, like
     * /path/to/foo/../bar/baz.sch.  Bad filenames will be normalized in
     * f_open (called by x_window_open_page). This works for Linux and MINGW32.
     */
    x_window_open_page(w_current, filename);
    g_free (filename);
  }
  */

  g_free(cwd);

  /* If no page has been loaded (wasn't specified in the command line.) */
  /* Then create an untitled page */
  if ( first_page ) {
    x_window_open_page( w_current, NULL );
  }

  /* Update the window to show the current page */
  x_window_set_current_page( w_current, w_current->toplevel->page_current );


#if DEBUG
  scm_c_eval_string ("(display \"hello guile\n\")");
#endif

  if (w_current->toplevel->scheme_directory == NULL) {
    fprintf(stderr, _("Scheme directory NOT set!\n"));
    exit(-1);
  }


  /* Execute a script if it exists */
  if (script_filename) {
    s_log_message(_("Executing guile script [%s]\n"),
                  script_filename);
    g_read_file(script_filename);
  }

  /* open up log window on startup */
  if (w_current->log_window == MAP_ON_STARTUP) {
    x_log_open ();
  }

  /* if there were any symbols which had major changes, put up an error */
  /* dialog box */
  major_changed_dialog(w_current);
    
  /* enter main loop */
  gtk_main();
}

/*! \brief Main executable entrance point.
 *  \par Function Description
 *  This is the main function for gSchem. It sets up the Scheme(GUILE)
 *  environment and passes control to via scm_boot_guile to
 *  the #main_prog function.
 */
int main (int argc, char *argv[])
{

#if ENABLE_NLS
  setlocale(LC_ALL, "");
  setlocale(LC_NUMERIC, "C");
  bindtextdomain("geda-gschem", LOCALEDIR);
  textdomain("geda-gschem");
  bind_textdomain_codeset("geda-gschem", "UTF-8");
#endif

  /* disable the deprecated warnings in guile 1.6.3 */
  /* Eventually the warnings will need to be fixed */
  if(getenv("GUILE_WARN_DEPRECATED") == NULL)
    putenv("GUILE_WARN_DEPRECATED=no");

  scm_boot_guile (argc, argv, main_prog, 0);
  
  return 0;
}

Attachment: signature.asc
Description: This is a digitally signed message part


_______________________________________________
geda-user mailing list
geda-user@xxxxxxxxxxxxxx
http://www.seul.org/cgi-bin/mailman/listinfo/geda-user