Added getopt(1) from "Alfred M. Szmidt" <ams@trillian.itslinux.org>
authorEric Andersen <andersen@codepoet.org>
Mon, 21 Aug 2000 22:02:34 +0000 (22:02 -0000)
committerEric Andersen <andersen@codepoet.org>
Mon, 21 Aug 2000 22:02:34 +0000 (22:02 -0000)
 -Erik

applets/busybox.c
busybox.c
busybox.def.h
docs/busybox.pod
docs/busybox.sgml
getopt.c [new file with mode: 0644]
internal.h
util-linux/getopt.c [new file with mode: 0644]

index 5d13a7b..d93fc51 100644 (file)
@@ -100,6 +100,9 @@ const struct BB_applet applets[] = {
 #ifdef BB_FSCK_MINIX
        {"fsck.minix", fsck_minix_main, _BB_DIR_SBIN, fsck_minix_usage},
 #endif
+#ifdef BB_GETOPT
+       {"getopt", getopt_main, _BB_DIR_BIN},
+#endif
 #ifdef BB_GREP
        {"grep", grep_main, _BB_DIR_BIN, grep_usage},
 #endif
index 5d13a7b..d93fc51 100644 (file)
--- a/busybox.c
+++ b/busybox.c
@@ -100,6 +100,9 @@ const struct BB_applet applets[] = {
 #ifdef BB_FSCK_MINIX
        {"fsck.minix", fsck_minix_main, _BB_DIR_SBIN, fsck_minix_usage},
 #endif
+#ifdef BB_GETOPT
+       {"getopt", getopt_main, _BB_DIR_BIN},
+#endif
 #ifdef BB_GREP
        {"grep", grep_main, _BB_DIR_BIN, grep_usage},
 #endif
index fd4302a..692c721 100644 (file)
@@ -33,6 +33,7 @@
 #define BB_FREE
 #define BB_FREERAMDISK
 #define BB_FSCK_MINIX
+#define BB_GETOPT
 #define BB_GREP
 #define BB_GUNZIP
 #define BB_GZIP
index 916ccb6..4fba7ea 100644 (file)
@@ -57,7 +57,7 @@ Currently defined functions include:
 
 ar, basename, cat, chgrp, chmod, chown, chroot, chvt, clear, cp, cut, date,
 dc, dd, deallocvt, df, dirname, dmesg, du, dumpkmap, dutmp, echo, false, fbset,
-fdflush, find, free, freeramdisk, fsck.minix, grep, gunzip, gzip, halt,
+fdflush, find, free, freeramdisk, fsck.minix, getopt, grep, gunzip, gzip, halt,
 head, hostid, hostname, id, init, insmod, kill, killall, length, ln,
 loadacm, loadfont, loadkmap, logger, logname, ls, lsmod, makedevs, mkdir,
 mkfifo, mkfs.minix, mknod, mkswap, mktemp, more, mount, mt, mv, nc,
@@ -614,6 +614,48 @@ Options:
 
 -------------------------------
 
+=item getopt
+
+Usage: getopt [OPTIONS]...
+
+Parse command options
+
+Options:
+
+       -a, --alternative            Allow long options starting with single -\n"
+       -l, --longoptions=longopts   Long options to be recognized\n"
+       -n, --name=progname          The name under which errors are reported\n"
+       -o, --options=optstring      Short options to be recognized\n"
+       -q, --quiet                  Disable error reporting by getopt(3)\n"
+       -Q, --quiet-output           No normal output\n"
+       -s, --shell=shell            Set shell quoting conventions\n"
+       -T, --test                   Test for getopt(1) version\n"
+       -u, --unqote                 Do not quote the output\n"
+
+Example:
+        $ cat getopt.test
+        #!/bin/sh
+        GETOPT=`getopt -o ab:c:: --long a-long,b-long:,c-long:: \
+                -n 'example.busybox' -- "$@"`
+        if [ $? != 0 ] ; then  exit 1 ; fi
+        eval set -- "$GETOPT"
+        while true ; do
+          case $1 in
+            -a|--a-long) echo "Option a" ; shift ;;
+            -b|--b-long) echo "Option b, argument \`$2'" ; shift 2 ;;
+            -c|--c-long)
+              case "$2" in
+                "") echo "Option c, no argument"; shift 2 ;;
+                *)  echo "Option c, argument \`$2'" ; shift 2 ;;
+              esac ;;
+            --) shift ; break ;;
+            *) echo "Internal error!" ; exit 1 ;;
+          esac
+        done
+
+
+-------------------------------
+
 =item grep
 
 Usage: grep [OPTIONS]... PATTERN [FILE]...
@@ -2052,4 +2094,4 @@ Enrique Zanardi <ezanardi@ull.es>
 
 =cut
 
-# $Id: busybox.pod,v 1.61 2000/08/21 21:18:52 andersen Exp $
+# $Id: busybox.pod,v 1.62 2000/08/21 22:02:34 andersen Exp $
index 864dbe0..f53be8d 100644 (file)
                </screen>
                </para>
        </sect1>
+       
+       <sect1 id="getopt">
+           <title>getopt</title>
+
+               <para>
+               Usage: getopt [OPTIONS]...
+               </para>
+
+               <para>
+               Parse command options
+               </para>
+
+               <para>
+               <screen>
+                  -a, --alternative            Allow long options starting with single -\n"
+                  -l, --longoptions=longopts   Long options to be recognized\n"
+                  -n, --name=progname          The name under which errors are reported\n"
+                  -o, --options=optstring      Short options to be recognized\n"
+                  -q, --quiet                  Disable error reporting by getopt(3)\n"
+                  -Q, --quiet-output           No normal output\n"
+                  -s, --shell=shell            Set shell quoting conventions\n"
+                  -T, --test                   Test for getopt(1) version\n"
+                  -u, --unqote                 Do not quote the output\n"
+               </screen>
+               </para>
+
+
+               <para>
+               Example:
+               </para>
+
+               <para>
+               <screen>
+                       $ cat getopt.test
+                       #!/bin/sh
+                       GETOPT=`getopt -o ab:c:: --long a-long,b-long:,c-long:: \
+                               -n 'example.busybox' -- "$@"`
+                       if [ $? != 0 ] ; then  exit 1 ; fi
+                       eval set -- "$GETOPT"
+                       while true ; do
+                         case $1 in
+                           -a|--a-long) echo "Option a" ; shift ;;
+                           -b|--b-long) echo "Option b, argument \`$2'" ; shift 2 ;;
+                           -c|--c-long)
+                             case "$2" in
+                               "") echo "Option c, no argument"; shift 2 ;;
+                               *)  echo "Option c, argument \`$2'" ; shift 2 ;;
+                             esac ;;
+                           --) shift ; break ;;
+                           *) echo "Internal error!" ; exit 1 ;;
+                         esac
+                       done
+               </screen>
+               </para>
+       </sect1>
 
        <sect1 id="grep">
            <title>grep</title>
diff --git a/getopt.c b/getopt.c
new file mode 100644 (file)
index 0000000..fb75790
--- /dev/null
+++ b/getopt.c
@@ -0,0 +1,416 @@
+/*
+ * getopt.c - Enhanced implementation of BSD getopt(1)
+ *   Copyright (c) 1997, 1998, 1999, 2000  Frodo Looijaard <frodol@dds.nl>
+ *
+ *   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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * Version 1.0-b4: Tue Sep 23 1997. First public release.
+ * Version 1.0: Wed Nov 19 1997.
+ *   Bumped up the version number to 1.0
+ *   Fixed minor typo (CSH instead of TCSH)
+ * Version 1.0.1: Tue Jun 3 1998
+ *   Fixed sizeof instead of strlen bug
+ *   Bumped up the version number to 1.0.1
+ * Version 1.0.2: Thu Jun 11 1998 (not present)
+ *   Fixed gcc-2.8.1 warnings
+ *   Fixed --version/-V option (not present)
+ * Version 1.0.5: Tue Jun 22 1999
+ *   Make -u option work (not present)
+ * Version 1.0.6: Tue Jun 27 2000
+ *   No important changes
+ * Version 1.1.0: Tue Jun 30 2000
+ *   Added NLS support (partly written by Arkadiusz Mi<B6>kiewicz
+ *     <misiek@misiek.eu.org>)
+ * Ported to Busybox - Alfred M. Szmidt <ams@trillian.itslinux.org>
+ *  Removed --version/-V and --help/-h in
+ *  Removed prase_error(), using errorMsg() from Busybox instead
+ *  Replaced our_malloc with xmalloc and our_realloc with xrealloc
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <getopt.h>
+
+#include "internal.h"
+
+/* NON_OPT is the code that is returned when a non-option is found in '+'
+   mode */
+#define NON_OPT 1
+/* LONG_OPT is the code that is returned when a long option is found. */
+#define LONG_OPT 2
+
+/* The shells recognized. */
+typedef enum {BASH,TCSH} shell_t;
+
+
+/* Some global variables that tells us how to parse. */
+shell_t shell=BASH; /* The shell we generate output for. */
+int quiet_errors=0; /* 0 is not quiet. */
+int quiet_output=0; /* 0 is not quiet. */
+int quote=1; /* 1 is do quote. */
+int alternative=0; /* 0 is getopt_long, 1 is getopt_long_only */
+
+/* Function prototypes */
+const char *normalize(const char *arg);
+int generate_output(char * argv[],int argc,const char *optstr,
+                    const struct option *longopts);
+void add_long_options(char *options);
+void add_longopt(const char *name,int has_arg);
+void set_shell(const char *new_shell);
+void set_initial_shell(void);
+
+
+/*
+ * This function 'normalizes' a single argument: it puts single quotes around
+ * it and escapes other special characters. If quote is false, it just
+ * returns its argument.
+ * Bash only needs special treatment for single quotes; tcsh also recognizes
+ * exclamation marks within single quotes, and nukes whitespace.
+ * This function returns a pointer to a buffer that is overwritten by
+ * each call.
+ */
+const char *normalize(const char *arg)
+{
+        static char *BUFFER=NULL;
+        const char *argptr=arg;
+        char *bufptr;
+
+        if (BUFFER != NULL)
+                free(BUFFER);
+
+        if (!quote) { /* Just copy arg */
+                BUFFER=xmalloc(strlen(arg)+1);
+
+                strcpy(BUFFER,arg);
+                return BUFFER;
+        }
+
+        /* Each character in arg may take upto four characters in the result:
+           For a quote we need a closing quote, a backslash, a quote and an
+           opening quote! We need also the global opening and closing quote,
+           and one extra character for '\0'. */
+        BUFFER=xmalloc(strlen(arg)*4+3);
+
+        bufptr=BUFFER;
+        *bufptr++='\'';
+
+        while (*argptr) {
+                if (*argptr == '\'') {
+                        /* Quote: replace it with: '\'' */
+                        *bufptr++='\'';
+                        *bufptr++='\\';
+                        *bufptr++='\'';
+                        *bufptr++='\'';
+                } else if (shell==TCSH && *argptr=='!') {
+                        /* Exclamation mark: replace it with: \! */
+                        *bufptr++='\'';
+                        *bufptr++='\\';
+                        *bufptr++='!';
+                        *bufptr++='\'';
+                } else if (shell==TCSH && *argptr=='\n') {
+                        /* Newline: replace it with: \n */
+                        *bufptr++='\\';
+                        *bufptr++='n';
+                } else if (shell==TCSH && isspace(*argptr)) {
+                        /* Non-newline whitespace: replace it with \<ws> */
+                        *bufptr++='\'';
+                        *bufptr++='\\';
+                        *bufptr++=*argptr;
+                        *bufptr++='\'';
+                } else
+                        /* Just copy */
+                        *bufptr++=*argptr;
+                argptr++;
+        }
+        *bufptr++='\'';
+        *bufptr++='\0';
+        return BUFFER;
+}
+
+/*
+ * Generate the output. argv[0] is the program name (used for reporting errors).
+ * argv[1..] contains the options to be parsed. argc must be the number of
+ * elements in argv (ie. 1 if there are no options, only the program name),
+ * optstr must contain the short options, and longopts the long options.
+ * Other settings are found in global variables.
+ */
+int generate_output(char * argv[],int argc,const char *optstr,
+                    const struct option *longopts)
+{
+        int exit_code = 0; /* We assume everything will be OK */
+        int opt;
+        int longindex;
+        const char *charptr;
+
+        if (quiet_errors) /* No error reporting from getopt(3) */
+                opterr=0;
+        optind=0; /* Reset getopt(3) */
+
+        while ((opt = (alternative?
+                       getopt_long_only(argc,argv,optstr,longopts,&longindex):
+                       getopt_long(argc,argv,optstr,longopts,&longindex)))
+               != EOF)
+                if (opt == '?' || opt == ':' )
+                        exit_code = 1;
+                else if (!quiet_output) {
+                        if (opt == LONG_OPT) {
+                                printf(" --%s",longopts[longindex].name);
+                                if (longopts[longindex].has_arg)
+                                        printf(" %s",
+                                               normalize(optarg?optarg:""));
+                        } else if (opt == NON_OPT)
+                                printf(" %s",normalize(optarg));
+                        else {
+                                printf(" -%c",opt);
+                                charptr = strchr(optstr,opt);
+                                if (charptr != NULL && *++charptr == ':')
+                                        printf(" %s",
+                                               normalize(optarg?optarg:""));
+                        }
+                }
+
+        if (! quiet_output) {
+                printf(" --");
+                while (optind < argc)
+                        printf(" %s",normalize(argv[optind++]));
+                printf("\n");
+        }
+        return exit_code;
+}
+
+static struct option *long_options=NULL;
+static int long_options_length=0; /* Length of array */
+static int long_options_nr=0; /* Nr of used elements in array */
+#define LONG_OPTIONS_INCR 10
+#define init_longopt() add_longopt(NULL,0)
+
+/* Register a long option. The contents of name is copied. */
+void add_longopt(const char *name,int has_arg)
+{
+        char *tmp;
+        if (!name) { /* init */
+                free(long_options);
+                long_options=NULL;
+                long_options_length=0;
+                long_options_nr=0;
+        }
+
+        if (long_options_nr == long_options_length) {
+                long_options_length += LONG_OPTIONS_INCR;
+                long_options=xrealloc(long_options,
+                                         sizeof(struct option) *
+                                         long_options_length);
+        }
+
+        long_options[long_options_nr].name=NULL;
+        long_options[long_options_nr].has_arg=0;
+        long_options[long_options_nr].flag=NULL;
+        long_options[long_options_nr].val=0;
+
+        if (long_options_nr) { /* Not for init! */
+                long_options[long_options_nr-1].has_arg=has_arg;
+                long_options[long_options_nr-1].flag=NULL;
+                long_options[long_options_nr-1].val=LONG_OPT;
+                tmp = xmalloc(strlen(name)+1);
+                strcpy(tmp,name);
+                long_options[long_options_nr-1].name=tmp;
+        }
+        long_options_nr++;
+}
+
+
+/*
+ * Register several long options. options is a string of long options,
+ * separated by commas or whitespace.
+ * This nukes options!
+ */
+void add_long_options(char *options)
+{
+        int arg_opt;
+        char *tokptr=strtok(options,", \t\n");
+        while (tokptr) {
+                arg_opt=no_argument;
+                if (strlen(tokptr) > 0) {
+                        if (tokptr[strlen(tokptr)-1] == ':') {
+                                if (tokptr[strlen(tokptr)-2] == ':') {
+                                        tokptr[strlen(tokptr)-2]='\0';
+                                        arg_opt=optional_argument;
+                                } else {
+                                        tokptr[strlen(tokptr)-1]='\0';
+                                        arg_opt=required_argument;
+                                }
+                                if (strlen(tokptr) == 0)
+                                        errorMsg("empty long option after -l or --long argument\n");
+                        }
+                        add_longopt(tokptr,arg_opt);
+                }
+                tokptr=strtok(NULL,", \t\n");
+        }
+}
+
+void set_shell(const char *new_shell)
+{
+        if (!strcmp(new_shell,"bash"))
+                shell=BASH;
+        else if (!strcmp(new_shell,"tcsh"))
+                shell=TCSH;
+        else if (!strcmp(new_shell,"sh"))
+                shell=BASH;
+        else if (!strcmp(new_shell,"csh"))
+                shell=TCSH;
+        else
+                errorMsg("unknown shell after -s or --shell argument\n");
+}
+
+
+/* Exit codes:
+ *   0) No errors, succesful operation.
+ *   1) getopt(3) returned an error.
+ *   2) A problem with parameter parsing for getopt(1).
+ *   3) Internal error, out of memory
+ *   4) Returned for -T
+ */
+
+static struct option longopts[]=
+{
+        {"options",required_argument,NULL,'o'},
+        {"longoptions",required_argument,NULL,'l'},
+        {"quiet",no_argument,NULL,'q'},
+        {"quiet-output",no_argument,NULL,'Q'},
+        {"shell",required_argument,NULL,'s'},
+        {"test",no_argument,NULL,'T'},
+        {"unquoted",no_argument,NULL,'u'},
+        {"alternative",no_argument,NULL,'a'},
+        {"name",required_argument,NULL,'n'},
+        {NULL,0,NULL,0}
+};
+
+/* Stop scanning as soon as a non-option argument is found! */
+static const char *shortopts="+ao:l:n:qQs:Tu";
+
+static const char getopt_usage[] =
+"getopt [OPTIONS]...\n"
+#ifndef BB_FEATURE_TRIVIAL_HELP
+"Parse command options\n"
+"  -a, --alternative            Allow long options starting with single -\n"
+"  -l, --longoptions=longopts   Long options to be recognized\n"
+"  -n, --name=progname          The name under which errors are reported\n"
+"  -o, --options=optstring      Short options to be recognized\n"
+"  -q, --quiet                  Disable error reporting by getopt(3)\n"
+"  -Q, --quiet-output           No normal output\n"
+"  -s, --shell=shell            Set shell quoting conventions\n"
+"  -T, --test                   Test for getopt(1) version\n"
+"  -u, --unqote                 Do not quote the output\n"
+#endif
+;
+
+
+int getopt_main(int argc, char *argv[])
+{
+        char *optstr=NULL;
+        char *name=NULL;
+        int opt;
+        int compatible=0;
+
+        init_longopt();
+
+        if (getenv("GETOPT_COMPATIBLE"))
+                compatible=1;
+
+        if (argc == 1) {
+                if (compatible) {
+                        /* For some reason, the original getopt gave no error
+                           when there were no arguments. */
+                        printf(" --\n");
+                        exit(0);
+                } else
+                        fatalError("missing optstring argument\n");
+        }
+
+        if (argv[1][0] != '-' || compatible) {
+                quote=0;
+                optstr=xmalloc(strlen(argv[1])+1);
+                strcpy(optstr,argv[1]+strspn(argv[1],"-+"));
+                argv[1]=argv[0];
+                exit(generate_output(argv+1,argc-1,optstr,long_options));
+        }
+
+        while ((opt=getopt_long(argc,argv,shortopts,longopts,NULL)) != EOF)
+                switch (opt) {
+                case 'a':
+                        alternative=1;
+                        break;
+                case 'o':
+                        if (optstr)
+                                free(optstr);
+                        optstr=xmalloc(strlen(optarg)+1);
+                        strcpy(optstr,optarg);
+                        break;
+                case 'l':
+                        add_long_options(optarg);
+                        break;
+                case 'n':
+                        if (name)
+                                free(name);
+                        name=xmalloc(strlen(optarg)+1);
+                        strcpy(name,optarg);
+                        break;
+                case 'q':
+                        quiet_errors=1;
+                        break;
+                case 'Q':
+                        quiet_output=1;
+                        break;
+                case 's':
+                        set_shell(optarg);
+                        break;
+                case 'T':
+                        exit(4);
+                case 'u':
+                        quote=0;
+                        break;
+                default:
+                        usage(getopt_usage);
+                }
+
+        if (!optstr) {
+                if (optind >= argc)
+                        fatalError("missing optstring argument\n");
+                else {
+                        optstr=xmalloc(strlen(argv[optind])+1);
+                        strcpy(optstr,argv[optind]);
+                        optind++;
+                }
+        }
+        if (name)
+                argv[optind-1]=name;
+        else
+                argv[optind-1]=argv[0];
+        exit(generate_output(argv+optind-1,argc-optind+1,optstr,long_options));
+}
+
+/*
+  Local Variables:
+  c-file-style: "linux"
+  c-basic-offset: 4
+  tab-width: 4
+  End:
+*/
index a472743..939384c 100644 (file)
@@ -137,6 +137,7 @@ extern int fsck_minix_main(int argc, char **argv);
 extern int find_main(int argc, char** argv);
 extern int free_main(int argc, char** argv);
 extern int freeramdisk_main(int argc, char** argv);
+extern int getopt_main(int argc, char** argv);
 extern int grep_main(int argc, char** argv);
 extern int gunzip_main (int argc, char** argv);
 extern int gzip_main(int argc, char** argv);
diff --git a/util-linux/getopt.c b/util-linux/getopt.c
new file mode 100644 (file)
index 0000000..fb75790
--- /dev/null
@@ -0,0 +1,416 @@
+/*
+ * getopt.c - Enhanced implementation of BSD getopt(1)
+ *   Copyright (c) 1997, 1998, 1999, 2000  Frodo Looijaard <frodol@dds.nl>
+ *
+ *   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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * Version 1.0-b4: Tue Sep 23 1997. First public release.
+ * Version 1.0: Wed Nov 19 1997.
+ *   Bumped up the version number to 1.0
+ *   Fixed minor typo (CSH instead of TCSH)
+ * Version 1.0.1: Tue Jun 3 1998
+ *   Fixed sizeof instead of strlen bug
+ *   Bumped up the version number to 1.0.1
+ * Version 1.0.2: Thu Jun 11 1998 (not present)
+ *   Fixed gcc-2.8.1 warnings
+ *   Fixed --version/-V option (not present)
+ * Version 1.0.5: Tue Jun 22 1999
+ *   Make -u option work (not present)
+ * Version 1.0.6: Tue Jun 27 2000
+ *   No important changes
+ * Version 1.1.0: Tue Jun 30 2000
+ *   Added NLS support (partly written by Arkadiusz Mi<B6>kiewicz
+ *     <misiek@misiek.eu.org>)
+ * Ported to Busybox - Alfred M. Szmidt <ams@trillian.itslinux.org>
+ *  Removed --version/-V and --help/-h in
+ *  Removed prase_error(), using errorMsg() from Busybox instead
+ *  Replaced our_malloc with xmalloc and our_realloc with xrealloc
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <getopt.h>
+
+#include "internal.h"
+
+/* NON_OPT is the code that is returned when a non-option is found in '+'
+   mode */
+#define NON_OPT 1
+/* LONG_OPT is the code that is returned when a long option is found. */
+#define LONG_OPT 2
+
+/* The shells recognized. */
+typedef enum {BASH,TCSH} shell_t;
+
+
+/* Some global variables that tells us how to parse. */
+shell_t shell=BASH; /* The shell we generate output for. */
+int quiet_errors=0; /* 0 is not quiet. */
+int quiet_output=0; /* 0 is not quiet. */
+int quote=1; /* 1 is do quote. */
+int alternative=0; /* 0 is getopt_long, 1 is getopt_long_only */
+
+/* Function prototypes */
+const char *normalize(const char *arg);
+int generate_output(char * argv[],int argc,const char *optstr,
+                    const struct option *longopts);
+void add_long_options(char *options);
+void add_longopt(const char *name,int has_arg);
+void set_shell(const char *new_shell);
+void set_initial_shell(void);
+
+
+/*
+ * This function 'normalizes' a single argument: it puts single quotes around
+ * it and escapes other special characters. If quote is false, it just
+ * returns its argument.
+ * Bash only needs special treatment for single quotes; tcsh also recognizes
+ * exclamation marks within single quotes, and nukes whitespace.
+ * This function returns a pointer to a buffer that is overwritten by
+ * each call.
+ */
+const char *normalize(const char *arg)
+{
+        static char *BUFFER=NULL;
+        const char *argptr=arg;
+        char *bufptr;
+
+        if (BUFFER != NULL)
+                free(BUFFER);
+
+        if (!quote) { /* Just copy arg */
+                BUFFER=xmalloc(strlen(arg)+1);
+
+                strcpy(BUFFER,arg);
+                return BUFFER;
+        }
+
+        /* Each character in arg may take upto four characters in the result:
+           For a quote we need a closing quote, a backslash, a quote and an
+           opening quote! We need also the global opening and closing quote,
+           and one extra character for '\0'. */
+        BUFFER=xmalloc(strlen(arg)*4+3);
+
+        bufptr=BUFFER;
+        *bufptr++='\'';
+
+        while (*argptr) {
+                if (*argptr == '\'') {
+                        /* Quote: replace it with: '\'' */
+                        *bufptr++='\'';
+                        *bufptr++='\\';
+                        *bufptr++='\'';
+                        *bufptr++='\'';
+                } else if (shell==TCSH && *argptr=='!') {
+                        /* Exclamation mark: replace it with: \! */
+                        *bufptr++='\'';
+                        *bufptr++='\\';
+                        *bufptr++='!';
+                        *bufptr++='\'';
+                } else if (shell==TCSH && *argptr=='\n') {
+                        /* Newline: replace it with: \n */
+                        *bufptr++='\\';
+                        *bufptr++='n';
+                } else if (shell==TCSH && isspace(*argptr)) {
+                        /* Non-newline whitespace: replace it with \<ws> */
+                        *bufptr++='\'';
+                        *bufptr++='\\';
+                        *bufptr++=*argptr;
+                        *bufptr++='\'';
+                } else
+                        /* Just copy */
+                        *bufptr++=*argptr;
+                argptr++;
+        }
+        *bufptr++='\'';
+        *bufptr++='\0';
+        return BUFFER;
+}
+
+/*
+ * Generate the output. argv[0] is the program name (used for reporting errors).
+ * argv[1..] contains the options to be parsed. argc must be the number of
+ * elements in argv (ie. 1 if there are no options, only the program name),
+ * optstr must contain the short options, and longopts the long options.
+ * Other settings are found in global variables.
+ */
+int generate_output(char * argv[],int argc,const char *optstr,
+                    const struct option *longopts)
+{
+        int exit_code = 0; /* We assume everything will be OK */
+        int opt;
+        int longindex;
+        const char *charptr;
+
+        if (quiet_errors) /* No error reporting from getopt(3) */
+                opterr=0;
+        optind=0; /* Reset getopt(3) */
+
+        while ((opt = (alternative?
+                       getopt_long_only(argc,argv,optstr,longopts,&longindex):
+                       getopt_long(argc,argv,optstr,longopts,&longindex)))
+               != EOF)
+                if (opt == '?' || opt == ':' )
+                        exit_code = 1;
+                else if (!quiet_output) {
+                        if (opt == LONG_OPT) {
+                                printf(" --%s",longopts[longindex].name);
+                                if (longopts[longindex].has_arg)
+                                        printf(" %s",
+                                               normalize(optarg?optarg:""));
+                        } else if (opt == NON_OPT)
+                                printf(" %s",normalize(optarg));
+                        else {
+                                printf(" -%c",opt);
+                                charptr = strchr(optstr,opt);
+                                if (charptr != NULL && *++charptr == ':')
+                                        printf(" %s",
+                                               normalize(optarg?optarg:""));
+                        }
+                }
+
+        if (! quiet_output) {
+                printf(" --");
+                while (optind < argc)
+                        printf(" %s",normalize(argv[optind++]));
+                printf("\n");
+        }
+        return exit_code;
+}
+
+static struct option *long_options=NULL;
+static int long_options_length=0; /* Length of array */
+static int long_options_nr=0; /* Nr of used elements in array */
+#define LONG_OPTIONS_INCR 10
+#define init_longopt() add_longopt(NULL,0)
+
+/* Register a long option. The contents of name is copied. */
+void add_longopt(const char *name,int has_arg)
+{
+        char *tmp;
+        if (!name) { /* init */
+                free(long_options);
+                long_options=NULL;
+                long_options_length=0;
+                long_options_nr=0;
+        }
+
+        if (long_options_nr == long_options_length) {
+                long_options_length += LONG_OPTIONS_INCR;
+                long_options=xrealloc(long_options,
+                                         sizeof(struct option) *
+                                         long_options_length);
+        }
+
+        long_options[long_options_nr].name=NULL;
+        long_options[long_options_nr].has_arg=0;
+        long_options[long_options_nr].flag=NULL;
+        long_options[long_options_nr].val=0;
+
+        if (long_options_nr) { /* Not for init! */
+                long_options[long_options_nr-1].has_arg=has_arg;
+                long_options[long_options_nr-1].flag=NULL;
+                long_options[long_options_nr-1].val=LONG_OPT;
+                tmp = xmalloc(strlen(name)+1);
+                strcpy(tmp,name);
+                long_options[long_options_nr-1].name=tmp;
+        }
+        long_options_nr++;
+}
+
+
+/*
+ * Register several long options. options is a string of long options,
+ * separated by commas or whitespace.
+ * This nukes options!
+ */
+void add_long_options(char *options)
+{
+        int arg_opt;
+        char *tokptr=strtok(options,", \t\n");
+        while (tokptr) {
+                arg_opt=no_argument;
+                if (strlen(tokptr) > 0) {
+                        if (tokptr[strlen(tokptr)-1] == ':') {
+                                if (tokptr[strlen(tokptr)-2] == ':') {
+                                        tokptr[strlen(tokptr)-2]='\0';
+                                        arg_opt=optional_argument;
+                                } else {
+                                        tokptr[strlen(tokptr)-1]='\0';
+                                        arg_opt=required_argument;
+                                }
+                                if (strlen(tokptr) == 0)
+                                        errorMsg("empty long option after -l or --long argument\n");
+                        }
+                        add_longopt(tokptr,arg_opt);
+                }
+                tokptr=strtok(NULL,", \t\n");
+        }
+}
+
+void set_shell(const char *new_shell)
+{
+        if (!strcmp(new_shell,"bash"))
+                shell=BASH;
+        else if (!strcmp(new_shell,"tcsh"))
+                shell=TCSH;
+        else if (!strcmp(new_shell,"sh"))
+                shell=BASH;
+        else if (!strcmp(new_shell,"csh"))
+                shell=TCSH;
+        else
+                errorMsg("unknown shell after -s or --shell argument\n");
+}
+
+
+/* Exit codes:
+ *   0) No errors, succesful operation.
+ *   1) getopt(3) returned an error.
+ *   2) A problem with parameter parsing for getopt(1).
+ *   3) Internal error, out of memory
+ *   4) Returned for -T
+ */
+
+static struct option longopts[]=
+{
+        {"options",required_argument,NULL,'o'},
+        {"longoptions",required_argument,NULL,'l'},
+        {"quiet",no_argument,NULL,'q'},
+        {"quiet-output",no_argument,NULL,'Q'},
+        {"shell",required_argument,NULL,'s'},
+        {"test",no_argument,NULL,'T'},
+        {"unquoted",no_argument,NULL,'u'},
+        {"alternative",no_argument,NULL,'a'},
+        {"name",required_argument,NULL,'n'},
+        {NULL,0,NULL,0}
+};
+
+/* Stop scanning as soon as a non-option argument is found! */
+static const char *shortopts="+ao:l:n:qQs:Tu";
+
+static const char getopt_usage[] =
+"getopt [OPTIONS]...\n"
+#ifndef BB_FEATURE_TRIVIAL_HELP
+"Parse command options\n"
+"  -a, --alternative            Allow long options starting with single -\n"
+"  -l, --longoptions=longopts   Long options to be recognized\n"
+"  -n, --name=progname          The name under which errors are reported\n"
+"  -o, --options=optstring      Short options to be recognized\n"
+"  -q, --quiet                  Disable error reporting by getopt(3)\n"
+"  -Q, --quiet-output           No normal output\n"
+"  -s, --shell=shell            Set shell quoting conventions\n"
+"  -T, --test                   Test for getopt(1) version\n"
+"  -u, --unqote                 Do not quote the output\n"
+#endif
+;
+
+
+int getopt_main(int argc, char *argv[])
+{
+        char *optstr=NULL;
+        char *name=NULL;
+        int opt;
+        int compatible=0;
+
+        init_longopt();
+
+        if (getenv("GETOPT_COMPATIBLE"))
+                compatible=1;
+
+        if (argc == 1) {
+                if (compatible) {
+                        /* For some reason, the original getopt gave no error
+                           when there were no arguments. */
+                        printf(" --\n");
+                        exit(0);
+                } else
+                        fatalError("missing optstring argument\n");
+        }
+
+        if (argv[1][0] != '-' || compatible) {
+                quote=0;
+                optstr=xmalloc(strlen(argv[1])+1);
+                strcpy(optstr,argv[1]+strspn(argv[1],"-+"));
+                argv[1]=argv[0];
+                exit(generate_output(argv+1,argc-1,optstr,long_options));
+        }
+
+        while ((opt=getopt_long(argc,argv,shortopts,longopts,NULL)) != EOF)
+                switch (opt) {
+                case 'a':
+                        alternative=1;
+                        break;
+                case 'o':
+                        if (optstr)
+                                free(optstr);
+                        optstr=xmalloc(strlen(optarg)+1);
+                        strcpy(optstr,optarg);
+                        break;
+                case 'l':
+                        add_long_options(optarg);
+                        break;
+                case 'n':
+                        if (name)
+                                free(name);
+                        name=xmalloc(strlen(optarg)+1);
+                        strcpy(name,optarg);
+                        break;
+                case 'q':
+                        quiet_errors=1;
+                        break;
+                case 'Q':
+                        quiet_output=1;
+                        break;
+                case 's':
+                        set_shell(optarg);
+                        break;
+                case 'T':
+                        exit(4);
+                case 'u':
+                        quote=0;
+                        break;
+                default:
+                        usage(getopt_usage);
+                }
+
+        if (!optstr) {
+                if (optind >= argc)
+                        fatalError("missing optstring argument\n");
+                else {
+                        optstr=xmalloc(strlen(argv[optind])+1);
+                        strcpy(optstr,argv[optind]);
+                        optind++;
+                }
+        }
+        if (name)
+                argv[optind-1]=name;
+        else
+                argv[optind-1]=argv[0];
+        exit(generate_output(argv+optind-1,argc-optind+1,optstr,long_options));
+}
+
+/*
+  Local Variables:
+  c-file-style: "linux"
+  c-basic-offset: 4
+  tab-width: 4
+  End:
+*/