All of lore.kernel.org
 help / color / mirror / Atom feed
From: Pierre Habouzit <madcoder@debian.org>
To: gitster@pobox.com, torvalds@linux-foundation.org
To: Junio C Hamano <gitster@pobox.com>,
	Linus Torvalds <torvalds@linux-foundation.org>
Cc: git@vger.kernel.org, Pierre Habouzit <madcoder@debian.org>
Subject: [PATCH] Add git-parseopt(1) to bring parse-options to shell scripts.
Date: Fri,  2 Nov 2007 16:09:19 +0100	[thread overview]
Message-ID: <1194016162-23599-2-git-send-email-madcoder@debian.org> (raw)
In-Reply-To: <1194016162-23599-1-git-send-email-madcoder@debian.org>

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 8709 bytes --]

---
 .gitignore                     |    1 +
 Documentation/cmd-list.perl    |    1 +
 Documentation/git-parseopt.txt |   91 +++++++++++++++++++++++++++++
 Makefile                       |    1 +
 builtin-parseopt.c             |  123 ++++++++++++++++++++++++++++++++++++++++
 builtin.h                      |    1 +
 git.c                          |    1 +
 7 files changed, 219 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/git-parseopt.txt
 create mode 100644 builtin-parseopt.c

diff --git a/.gitignore b/.gitignore
index c8c13f5..521155c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -86,6 +86,7 @@ git-pack-redundant
 git-pack-objects
 git-pack-refs
 git-parse-remote
+git-parseopt
 git-patch-id
 git-peek-remote
 git-prune
diff --git a/Documentation/cmd-list.perl b/Documentation/cmd-list.perl
index 8d21d42..c491964 100755
--- a/Documentation/cmd-list.perl
+++ b/Documentation/cmd-list.perl
@@ -147,6 +147,7 @@ git-pack-objects                        plumbingmanipulators
 git-pack-redundant                      plumbinginterrogators
 git-pack-refs                           ancillarymanipulators
 git-parse-remote                        synchelpers
+git-parseopt                            plumbingmanipulators
 git-patch-id                            purehelpers
 git-peek-remote                         purehelpers
 git-prune                               ancillarymanipulators
diff --git a/Documentation/git-parseopt.txt b/Documentation/git-parseopt.txt
new file mode 100644
index 0000000..77f5a10
--- /dev/null
+++ b/Documentation/git-parseopt.txt
@@ -0,0 +1,91 @@
+git-parseopt(1)
+============
+
+NAME
+----
+git-parseopt - Git shell script helper similar to getopt(1)
+
+
+SYNOPSIS
+--------
+'git-parseopt' [-u] [--keep-dashdash] -- [<args>...]
+
+DESCRIPTION
+-----------
+
+This is a helper for scripts to behave the same as git C commands. It merely
+breaks and normalize options so that the option parsing becomes shorter and
+easier to read. It takes the specifications of the options to undersand on its
+standard input.
+
+In case of error, it ouputs usage on the standard error stream, and exits with
+code 129. Else its ouput is suitable for evaluation with "eval" to replace
+the script arguments with the normalized form.
+
+OPTIONS
+-------
+
+--keep-dash-dash::
+	Keep the `--` in `<args>...`.
+
+\--::
+	Following arguments are the options to parse. This isn't optional.
+
+<args>...::
+	Options to parse and deal with
+
+INPUT FORMAT
+------------
+
+`git-parseopt` input format is fully text based. It has two parts, separated
+by a line that contains only `--`. The lines before (should be more than one)
+is used for the usage. The lines after describe the options.
+
+Each line of options has this format:
+
+------------
+<opt_spec><arg_spec>? SP+ help LF
+------------
+
+`<opt_spec>`::
+	its format is the short option character, then the long option name
+        separated by a comma. Both parts are not required, though at least one
+        is necessary. `h,help`, `dry-run` and `f` are all three correct
+        `<opt_spec>`.
+
+`<arg_spec>`::
+	an `<arg_spec>` tells the option parser if the option has an argument
+        (`=`), an optionnal one (`?` though its use is discouraged) or none
+        (no `<arg_spec>` in that case).
+
+The rest of the line after as many spaces up to the ending line feed is used
+as the help associated to the option.
+
+EXAMPLE
+-------
+
+------------
+OPTS_SPEC="\
+some-command [options] <args>...
+
+some-command does foo and bar !
+--
+h,help    show the help
+foo       some nifty option --foo
+bar=      some cool option --bar with an argument
+C?        option C with an optionnal argument"
+
+eval `echo "$OPTS_SPEC" | git-parseopt -- "$@" || echo exit $?`
+------------
+
+Author
+------
+Written by Pierre Habouzit <madcoder@debian.org>
+
+Documentation
+--------------
+Documentation by Pierre Habouzit.
+
+GIT
+---
+Part of the gitlink:git[7] suite
diff --git a/Makefile b/Makefile
index 042f79e..7c5df55 100644
--- a/Makefile
+++ b/Makefile
@@ -352,6 +352,7 @@ BUILTIN_OBJS = \
 	builtin-mv.o \
 	builtin-name-rev.o \
 	builtin-pack-objects.o \
+	builtin-parseopt.o \
 	builtin-prune.o \
 	builtin-prune-packed.o \
 	builtin-push.o \
diff --git a/builtin-parseopt.c b/builtin-parseopt.c
new file mode 100644
index 0000000..c032f5a
--- /dev/null
+++ b/builtin-parseopt.c
@@ -0,0 +1,123 @@
+/*
+ * Copyright © 2007 Pierre Habouzit <madcoder@debian.org>
+ */
+#include "git-compat-util.h"
+#include "cache.h"
+#include "quote.h"
+#include "parse-options.h"
+
+static int keep_dashdash = 0;
+static struct strbuf parsed = STRBUF_INIT;
+
+static struct option parseopt_opts[] = {
+	OPT_BOOLEAN(0, "keep-dashdash", &keep_dashdash,
+				"keep the `--` passed as an arg"),
+	OPT_END(),
+};
+
+static char const * const parseopt_usage[] = {
+	"git-parseopt [options] -- [<args>...]",
+	NULL
+};
+
+static int parseopt_dump(const struct option *o, const char *arg, int unset)
+{
+	if (unset)
+		strbuf_addf(&parsed, " --no-%s", o->long_name);
+	else if (o->short_name)
+		strbuf_addf(&parsed, " -%c", o->short_name);
+	else
+		strbuf_addf(&parsed, " --%s", o->long_name);
+	if (arg) {
+		strbuf_addch(&parsed, ' ');
+		sq_quote_buf(&parsed, arg);
+	}
+	return 0;
+}
+
+static const char *skipspaces(const char *s)
+{
+	while (isspace(*s))
+		s++;
+	return s;
+}
+
+int cmd_parseopt(int argc, const char **argv, const char *prefix)
+{
+	struct strbuf sb;
+	const char **usage = NULL;
+	struct option *opts = NULL;
+	int onb = 0, osz = 0, unb = 0, usz = 0;
+
+	strbuf_addstr(&parsed, "set --");
+	argc = parse_options(argc, argv, parseopt_opts, parseopt_usage,
+	                     PARSE_OPT_KEEP_DASHDASH);
+	if (argc < 1 || strcmp(argv[0], "--"))
+		usage_with_options(parseopt_usage, parseopt_opts);
+
+	strbuf_init(&sb, 0);
+	/* get the usage up to the first line with a -- on it */
+	for (;;) {
+		if (strbuf_getline(&sb, stdin, '\n') == EOF)
+			die("premature end of input");
+		ALLOC_GROW(usage, unb + 1, usz);
+		if (!strcmp("--", sb.buf)) {
+			if (unb < 1)
+				die("no usage string given before the `--' separator");
+			usage[unb] = NULL;
+			break;
+		}
+		usage[unb++] = strbuf_detach(&sb, NULL);
+	}
+
+	/* parse: (<short>|<short>,<long>|<long>)[=?]? SP+ <help> */
+	while (strbuf_getline(&sb, stdin, '\n') != EOF) {
+		const char *s;
+		struct option *o;
+
+		ALLOC_GROW(opts, onb + 1, osz);
+		memset(opts + onb, 0, sizeof(opts[onb]));
+
+		o = &opts[onb++];
+		s = strchr(sb.buf, ' ');
+		if (!s || *sb.buf == ' ')
+			die("invalid option spec");
+
+		o->type = OPTION_CALLBACK;
+		o->help = xstrdup(skipspaces(s));
+		o->callback = &parseopt_dump;
+		switch (s[-1]) {
+		case '=':
+			s--;
+			break;
+		case '?':
+			o->flags = PARSE_OPT_OPTARG;
+			s--;
+			break;
+		default:
+			o->flags = PARSE_OPT_NOARG;
+			break;
+		}
+
+		if (s - sb.buf == 1) /* short option only */
+			o->short_name = *sb.buf;
+		else if (sb.buf[1] != ',') /* long option only */
+			o->long_name = xmemdupz(sb.buf, s - sb.buf);
+		else {
+			o->short_name = *sb.buf;
+			o->long_name = xmemdupz(sb.buf + 2, s - sb.buf - 2);
+		}
+	}
+	strbuf_release(&sb);
+
+	/* put an OPT_END() */
+	ALLOC_GROW(opts, onb + 1, osz);
+	memset(opts + onb, 0, sizeof(opts[onb]));
+	argc = parse_options(argc, argv, opts, usage,
+	                     keep_dashdash ? PARSE_OPT_KEEP_DASHDASH : 0);
+
+	strbuf_addf(&parsed, " --");
+	sq_quote_argv(&parsed, argv, argc, 0);
+	puts(parsed.buf);
+	return 0;
+}
diff --git a/builtin.h b/builtin.h
index 9a6213a..b2ef7b6 100644
--- a/builtin.h
+++ b/builtin.h
@@ -55,6 +55,7 @@ extern int cmd_merge_file(int argc, const char **argv, const char *prefix);
 extern int cmd_mv(int argc, const char **argv, const char *prefix);
 extern int cmd_name_rev(int argc, const char **argv, const char *prefix);
 extern int cmd_pack_objects(int argc, const char **argv, const char *prefix);
+extern int cmd_parseopt(int argc, const char **argv, const char *prefix);
 extern int cmd_pickaxe(int argc, const char **argv, const char *prefix);
 extern int cmd_prune(int argc, const char **argv, const char *prefix);
 extern int cmd_prune_packed(int argc, const char **argv, const char *prefix);
diff --git a/git.c b/git.c
index 4e10581..89524dd 100644
--- a/git.c
+++ b/git.c
@@ -333,6 +333,7 @@ static void handle_internal_command(int argc, const char **argv)
 		{ "mv", cmd_mv, RUN_SETUP | NEED_WORK_TREE },
 		{ "name-rev", cmd_name_rev, RUN_SETUP },
 		{ "pack-objects", cmd_pack_objects, RUN_SETUP },
+		{ "parseopt", cmd_parseopt },
 		{ "pickaxe", cmd_blame, RUN_SETUP },
 		{ "prune", cmd_prune, RUN_SETUP },
 		{ "prune-packed", cmd_prune_packed, RUN_SETUP },
-- 
1.5.3.5.1458.g2aa13-dirty


  reply	other threads:[~2007-11-02 15:09 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-11-02 15:09 Bring parse_options to the shell Pierre Habouzit
2007-11-02 15:09 ` Pierre Habouzit [this message]
2007-11-02 15:09   ` [PATCH] Update git-sh-setup(1) to allow transparent use of git-parseopt(1) Pierre Habouzit
2007-11-02 15:09     ` [PATCH] Migrate git-clean.sh to use git-parseoptions(1) Pierre Habouzit
2007-11-02 15:58       ` [PATCH] Migrate git-clone to use git-parseopt(1) Pierre Habouzit
2007-11-02 15:14 ` Bring parse_options to the shell Pierre Habouzit
2007-11-02 15:51   ` Linus Torvalds
2007-11-02 16:09     ` Pierre Habouzit
2007-11-02 18:21       ` Johannes Schindelin
2007-11-02 18:48         ` Pierre Habouzit

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1194016162-23599-2-git-send-email-madcoder@debian.org \
    --to=madcoder@debian.org \
    --cc=git@vger.kernel.org \
    --cc=gitster@pobox.com \
    --cc=torvalds@linux-foundation.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.