]> git.draconx.ca Git - gob-dx.git/blobdiff - src/main.c
Convert command-line options processing to getopt_long.
[gob-dx.git] / src / main.c
index 295199cb6c0263d3d89b929f9c26b97729b37621..c994d5ae3b0119326e3f32b52300a7fb997b71c8 100644 (file)
@@ -2,7 +2,7 @@
  * Copyright (C) 1999,2000 the Free Software Foundation.
  * Copyright (C) 2000 Eazel, Inc.
  * Copyright (C) 2001-2011 George (Jiri) Lebl
- * Copyright © 2019-2020 Nick Bowler
+ * Copyright © 2019-2021 Nick Bowler
  *
  * Author: George (Jiri) Lebl
  *
@@ -31,6 +31,8 @@
 #include <unistd.h>
 #include <stdlib.h>
 #include <sys/stat.h>
+#include <limits.h>
+#include <getopt.h>
 
 #include "treefuncs.h"
 #include "parse.h"
 
 #include "main.h"
 
+enum {
+       SOPT_END = UCHAR_MAX,
+       LOPT_VERSION,
+       LOPT_NO_TOUCH,
+       LOPT_FILE_SEP,
+       LOPT_M4,
+       LOPT_M4_CLEAN,
+       LOPT_M4_DIR
+};
+
+static const char sopts[] = "wnho:";
+static const struct option lopts[] = {
+       { "help", 0, NULL, 'h' },
+       { "version", 0, NULL, LOPT_VERSION },
+       { "exit-on-warn", 0, NULL, 'w' },
+       { "no-exit-on-warn", 0, &exit_on_warn, FALSE },
+       { "for-cpp", 0, &for_cpp, TRUE },
+       { "no-extern-c", 0, &no_extern_c, TRUE },
+       { "no-gnu", 0, &no_gnu, TRUE },
+       { "no-touch", 0, NULL, LOPT_NO_TOUCH },
+       { "no-touch-headers", 0, &no_touch_headers, TRUE },
+       { "always-private-header", 0, &private_header, PRIVATE_HEADER_ALWAYS },
+       { "ondemand-private-header", 0, &private_header, PRIVATE_HEADER_ONDEMAND },
+       { "no-private-header", 0, &private_header, PRIVATE_HEADER_NEVER },
+       { "always-private-struct", 0, &always_private_struct, TRUE },
+       { "m4", 0, NULL, LOPT_M4 },
+       { "m4-clean", 0, NULL, LOPT_M4_CLEAN },
+       { "m4-dir", 0, NULL, LOPT_M4_DIR },
+       { "no-write", 0, NULL, 'n' },
+       { "no-lines", 0, &no_lines, TRUE },
+       { "no-self-alias", 0, &no_self_alias, TRUE },
+       { "no-kill-underscores", 0, NULL, 0 /* no-op */ },
+       { "output-dir", 1, NULL, 'o' },
+       { "file-sep", 2, NULL, LOPT_FILE_SEP },
+       { "gtk3", 0, &gtk3_ok, TRUE },
+       { 0 }
+};
+
 char *filename = NULL;
 
 int yyparse(void);
@@ -128,7 +168,6 @@ gint prealloc = 0;
 
 
 gboolean use_m4 = FALSE; /* preprocess sources with m4 */
-gboolean use_m4_clean = FALSE; /* preprocess sources with m4, no m4 flags */
 char *m4_commandline = NULL;
 #define M4_INCLUDE_DIR  PKGDATADIR "/m4"
 #define M4_BASE_FILENAME "gobm4.m4"
@@ -4603,184 +4642,146 @@ print_help(void)
        puts("End world hunger, donate to the World Food Programme: https://www.wfp.org/");
 }
 
-static void
-parse_options(int argc, char *argv[])
+/*
+ * Called after getopt_long receives an --m4 argument.  Immediately stop
+ * processing options.  Then all non-option arguments seen so far together
+ * with all remaining arguments are appended to M4_COMMANDLINE.  If m4_clean
+ * is false, then M4_FLAGS is inserted before the first non-option argument,
+ * if any.
+ *
+ * The resulting string is returned, which should be freed by the caller.
+ */
+static char *parse_m4_options(int argc, char **argv, gboolean m4_clean)
 {
-       int i;
-       int got_file = FALSE;
-       int no_opts = FALSE;
-       int m4_opts = FALSE; /* if we are just passing on args to m4 */
-
-       filename = NULL;
-
-       for(i = 1 ; i < argc; i++) {
-               if(m4_opts) {
-                       char *new_commandline;
-                       g_assert(m4_commandline!=NULL);
-
-                       /* check whether this one looks like the filename */
-                       if((!strcmp(argv[i],"-") || argv[i][0] != '-') 
-                          && !got_file) {
-                               const gchar *m4_flags=use_m4_clean?"":M4_FLAGS;
-                               filename = argv[i];
-                               got_file = TRUE;
-                               
-                               /* insert flags before the filename */
-                               new_commandline=g_strconcat(m4_commandline,
-                                                           " ",
-                                                           m4_flags, 
-                                                           " ",
-                                                           argv[i],
-                                                           NULL);
-                       }
+       char **nonopt = NULL, *save_argv0, *ret;
+       int opt;
 
-                       /* just an ordinary option */
-                       else                      
-                         new_commandline=g_strconcat(m4_commandline,
-                                                     " ",
-                                                     argv[i],
-                                                     NULL);
-
-                       /* free old commandline */
-                       g_free(m4_commandline);
-                       m4_commandline=new_commandline;
-
-               } else if(no_opts ||
-                  argv[i][0] != '-') {
-                       /*must be a file*/
-                       if(got_file) {
-                               fprintf(stderr, "Specify only one file!\n");
-                               print_usage(stderr);
-                               exit(1);
-                       }
-                       filename = argv[i];
-                       got_file = TRUE;
-               } else if(strcmp(argv[i], "--help")==0) {
-                       print_help();
-                       exit(0);
-               } else if(strcmp(argv[i], "--version")==0) {
-                       print_version();
-                       exit(0);
-               } else if(strcmp(argv[i], "--exit-on-warn")==0) {
-                       exit_on_warn = TRUE;
-               } else if(strcmp(argv[i], "--no-exit-on-warn")==0) {
-                       exit_on_warn = FALSE;
-               } else if(strcmp(argv[i], "--for-cpp")==0) {
-                       for_cpp = TRUE;
-               } else if(strcmp(argv[i], "--no-touch")==0) {
-                       no_touch = TRUE;
-                       no_touch_headers = TRUE;
-               } else if(strcmp(argv[i], "--no-touch-headers")==0) {
-                       no_touch_headers = TRUE;
-               } else if(strcmp(argv[i], "--ondemand-private-header")==0) {
-                       private_header = PRIVATE_HEADER_ONDEMAND;
-               } else if(strcmp(argv[i], "--always-private-header")==0) {
-                       private_header = PRIVATE_HEADER_ALWAYS;
-               } else if(strcmp(argv[i], "--no-private-header")==0) {
-                       private_header = PRIVATE_HEADER_NEVER;
-               } else if(strcmp(argv[i], "--no-gnu")==0) {
-                       no_gnu = TRUE;
-               } else if(strcmp(argv[i], "--no-extern-c")==0) {
-                       no_extern_c = TRUE;
-               } else if(strcmp(argv[i], "--no-write")==0) {
+       /* First, conclude getopt run and reset with remaining args */
+       getopt_long(optind, argv, sopts, lopts, NULL);
+       argv += optind-1;
+       argc -= optind-1;
+       optind = 0;
+
+       save_argv0 = argv[0];
+       argv[0] = M4_COMMANDLINE;
+
+       if (m4_clean) {
+               ret = g_strjoinv(" ", argv);
+               argv[0] = save_argv0;
+               return ret;
+       }
+
+       /* Locate first non-option argument, if any. */
+       while ((opt = getopt_long(argc, argv, "-", NULL, NULL)) != -1) {
+               if (opt == 1) {
+                       nonopt = &argv[optind-2];
+                       break;
+               }
+       }
+
+       /* If there is a non-option but the above didn't see it, must be "--" */
+       if (!nonopt && argv[optind])
+               nonopt = &argv[optind-2];
+
+       if (nonopt) {
+               /* Found non-option, insert M4_FLAGS just before it. */
+               char *save_argv[3] = { nonopt[0], nonopt[1], nonopt[2] };
+
+               nonopt[1] = M4_FLAGS;
+               nonopt[2] = NULL;
+               nonopt[0] = g_strjoinv(" ", argv);
+
+               nonopt[1] = save_argv[1];
+               nonopt[2] = save_argv[2];
+               ret = g_strjoinv(" ", nonopt);
+
+               g_free(nonopt[0]);
+               nonopt[0] = save_argv[0];
+       } else {
+               /* Only options, not inserting M4_FLAGS. */
+               ret = g_strjoinv(" ", argv);
+       }
+
+       argv[0] = save_argv0;
+       return ret;
+}
+
+static int parse_options(int argc, char **argv)
+{
+       gboolean show_m4_dir = FALSE, m4_clean = FALSE;
+       char *raw_file_sep = "-";
+       int opt;
+
+       opterr = 0;
+       while ((opt = getopt_long(argc, argv, sopts, lopts, NULL)) != -1) {
+               switch (opt) {
+               case 'n':
                        no_write = TRUE;
-               } else if(strcmp(argv[i], "--no-lines")==0) {
-                       no_lines = TRUE;
-               } else if(strcmp(argv[i], "--no-self-alias")==0) {
-                       no_self_alias = TRUE;
-               } else if(strcmp(argv[i], "--no-kill-underscores")==0) {
-                       /* no op */;
-               } else if(strcmp(argv[i], "--always-private-struct")==0) {
-                       always_private_struct = TRUE;
-               } else if(strcmp(argv[i], "--m4-dir")==0) {
-                       printf("%s\n",M4_INCLUDE_DIR);
-                       exit(0);
-               } else if(strcmp(argv[i], "--m4")==0) {
-                       use_m4 = TRUE;
-                       use_m4_clean=FALSE;
-                       m4_opts = TRUE;
-                       m4_commandline=g_strdup(M4_COMMANDLINE);
-               } else if(strcmp(argv[i], "--m4-clean")==0) {
+                       break;
+               case 'o':
+                       output_dir = optarg;
+                       break;
+               case 'w':
+                       exit_on_warn = TRUE;
+                       break;
+               case LOPT_FILE_SEP:
+                       raw_file_sep = optarg ? optarg : "";
+                       break;
+               case LOPT_M4_DIR:
+                       show_m4_dir = TRUE;
+                       break;
+               case LOPT_NO_TOUCH:
+                       no_touch = no_touch_headers = TRUE;
+                       break;
+               case LOPT_M4_CLEAN:
+                       m4_clean = TRUE;
+               case LOPT_M4:
                        use_m4 = TRUE;
-                       use_m4_clean=TRUE;
-                       m4_opts = TRUE;
-                       m4_commandline=g_strdup(M4_COMMANDLINE);
-               } else if (strcmp (argv[i], "-o") == 0 || 
-                          strcmp (argv[i], "--output-dir") == 0) {
-                       if (i+1 < argc) {
-                               output_dir = g_strdup (argv[i+1]);
-                               i++;
-                       } else {
-                               output_dir = NULL;
-                       }
-               } else if (strncmp (argv[i], "-o=", strlen ("-o=")) == 0 || 
-                          strncmp (argv[i],
-                                   "--output-dir=",
-                                   strlen ("--output-dir=")) == 0) {
-                       char *p = strchr (argv[i], '=');
-                       g_assert (p != NULL);
-                       output_dir = g_strdup (p+1);
-               } else if (strncmp (argv[i], "--file-sep=",
-                                    strlen ("--file-sep=")) == 0) {
-                       char *p = strchr (argv[i], '=');
-                       g_assert (p != NULL);
-                       file_sep = *(p+1);
-               } else if (strncmp (argv[i], "--file-sep",
-                                    strlen ("--file-sep")) == 0) {
-                       if (i+1 < argc) {
-                               file_sep = (argv[i+1])[0];
-                               i++;
-                       } else {
-                               file_sep = 0;
-                       }
-               } else if(strcmp(argv[i], "--gtk3")==0) {
-                       gtk3_ok = TRUE;
-               } else if(strcmp(argv[i], "--")==0) {
-                       /*further arguments are files*/
-                       no_opts = TRUE;
-               } else if(strncmp(argv[i], "--", 2)==0) {
-                       /*unknown long option*/
-                       fprintf(stderr, "Unknown option '%s'!\n", argv[i]);
-                       print_usage(stderr);
-                       exit(1);
-               } else {
-                       /*by now we know we have a string starting with
-                         - which is a short option string*/
-                       char *p;
-                       for(p = argv[i] + 1; *p; p++) {
-                               switch(*p) {
-                               case 'w':
-                                       exit_on_warn=TRUE;
-                                       break;
-                               case 'n':
-                                       no_write = TRUE;
-                                       break;
-                               case 'h':
-                               case '?':
-                                       print_help();
-                                       exit(0);
-                               default:
-                                       fprintf(stderr,
-                                               "Unknown option '%c'!\n", *p);
-                                       print_usage(stderr);
-                                       exit(1);
-                               }
+                       m4_commandline = parse_m4_options(argc, argv, m4_clean);
+                       goto out;
+               case LOPT_VERSION:
+                       print_version();
+                       return 1;
+               default:
+                       if (optopt == '?') {
+               case 'h':
+                               print_help();
+                               return 1;
                        }
+
+                       /* Rewind getopt to get internal error messages. */
+                       optind = 0, opterr = 1;
+                       while (getopt_long(argc, argv, sopts, lopts, NULL)
+                              != opt);
+                       return -1;
+               case 0: /* no-op or option set by flag */;
                }
        }
 
-#if 0
-       /* if we are using m4, and got no filename, append m4 flags now */
-       if(!got_file && use_m4 && !use_m4_clean) {
-               char *new_commandline;
-               new_commandline=g_strconcat(m4_commandline,
-                                           " ",
-                                           M4_FLAGS,
-                                           NULL);
-               g_free(m4_commandline);
-               m4_commandline=new_commandline;
+       filename = argv[optind];
+       if (argc > optind+1) {
+               char *s = g_strjoinv(" ", argv+optind+1);
+               fprintf(stderr, "%s: Warning: excess arguments ignored: %s\n",
+                               g_get_prgname(), s);
+               g_free(s);
+               if (exit_on_warn)
+                       return -1;
+       }
+out:
+       file_sep = raw_file_sep[0];
+       if (raw_file_sep[0] && raw_file_sep[1]) {
+               fprintf(stderr, "%s: Warning: --file-sep characters beyond the first are ignored\n",
+                               g_get_prgname());
+               if (exit_on_warn)
+                       return -1;
        }
-#endif
+
+       if (show_m4_dir) {
+               printf("%s\n", M4_INCLUDE_DIR);
+               return 1;
+       }
+
+       return 0;
 }
 
 static void
@@ -4861,9 +4862,17 @@ compare_and_move (const char *old_filename)
 int
 main(int argc, char *argv[])
 {
+       int rc;
+
        g_set_prgname(argc > 0 ? argv[0] : "gob2");
 
-       parse_options(argc, argv);
+       rc = parse_options(argc, argv);
+       if (rc < 0) {
+               print_usage(stderr);
+               return EXIT_FAILURE;
+       } else if (rc > 0) {
+               return EXIT_SUCCESS;
+       }
 
        if(use_m4) {
                yyin = popen(m4_commandline, "r");