#include "tree.h"
#include "blob.h"
#include "quote.h"
+#include "parse-options.h"
/* Quoting styles */
#define QUOTE_NONE 0
#define QUOTE_SHELL 1
#define QUOTE_PERL 2
-#define QUOTE_PYTHON 3
-#define QUOTE_TCL 4
+#define QUOTE_PYTHON 4
+#define QUOTE_TCL 8
typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;
static int parse_atom(const char *atom, const char *ep)
{
const char *sp;
- char *n;
int i, at;
sp = atom;
/* Is the atom a valid one? */
for (i = 0; i < ARRAY_SIZE(valid_atom); i++) {
int len = strlen(valid_atom[i].name);
- if (len == ep - sp && !memcmp(valid_atom[i].name, sp, len))
+ /*
+ * If the atom name has a colon, strip it and everything after
+ * it off - it specifies the format for this entry, and
+ * shouldn't be used for checking against the valid_atom
+ * table.
+ */
+ const char *formatp = strchr(sp, ':');
+ if (!formatp || ep < formatp)
+ formatp = ep;
+ if (len == formatp - sp && !memcmp(valid_atom[i].name, sp, len))
break;
}
(sizeof *used_atom) * used_atom_cnt);
used_atom_type = xrealloc(used_atom_type,
(sizeof(*used_atom_type) * used_atom_cnt));
- n = xmalloc(ep - atom + 1);
- memcpy(n, atom, ep - atom);
- n[ep-atom] = 0;
- used_atom[at] = n;
+ used_atom[at] = xmemdupz(atom, ep - atom);
used_atom_type[at] = valid_atom[i].cmp_type;
return at;
}
* Make sure the format string is well formed, and parse out
* the used atoms.
*/
-static void verify_format(const char *format)
+static int verify_format(const char *format)
{
const char *cp, *sp;
for (cp = format; *cp && (sp = find_next(cp)); ) {
const char *ep = strchr(sp, ')');
if (!ep)
- die("malformatted format string %s", sp);
+ return error("malformed format string %s", sp);
/* sp points at "%(" and ep points at the closing ")" */
parse_atom(sp + 2, ep);
cp = ep + 1;
}
+ return 0;
}
/*
name++;
if (!strcmp(name, "tag"))
v->s = tag->tag;
+ else if (!strcmp(name, "type") && tag->tagged)
+ v->s = typename(tag->tagged->type);
+ else if (!strcmp(name, "object") && tag->tagged) {
+ char *s = xmalloc(41);
+ strcpy(s, sha1_to_hex(tag->tagged->sha1));
+ v->s = s;
+ }
}
}
static const char *copy_line(const char *buf)
{
- const char *eol = strchr(buf, '\n');
- char *line;
- int len;
- if (!eol)
- return "";
- len = eol - buf;
- line = xmalloc(len + 1);
- memcpy(line, buf, len);
- line[len] = 0;
- return line;
+ const char *eol = strchrnul(buf, '\n');
+ return xmemdupz(buf, eol - buf);
}
static const char *copy_name(const char *buf)
{
- const char *eol = strchr(buf, '\n');
- const char *eoname = strstr(buf, " <");
- char *line;
- int len;
- if (!(eoname && eol && eoname < eol))
- return "";
- len = eoname - buf;
- line = xmalloc(len + 1);
- memcpy(line, buf, len);
- line[len] = 0;
- return line;
+ const char *cp;
+ for (cp = buf; *cp && *cp != '\n'; cp++) {
+ if (!strncmp(cp, " <", 2))
+ return xmemdupz(buf, cp - buf);
+ }
+ return "";
}
static const char *copy_email(const char *buf)
{
const char *email = strchr(buf, '<');
const char *eoemail = strchr(email, '>');
- char *line;
- int len;
if (!email || !eoemail)
return "";
- eoemail++;
- len = eoemail - email;
- line = xmalloc(len + 1);
- memcpy(line, email, len);
- line[len] = 0;
- return line;
+ return xmemdupz(email, eoemail + 1 - email);
}
-static void grab_date(const char *buf, struct atom_value *v)
+static void grab_date(const char *buf, struct atom_value *v, const char *atomname)
{
const char *eoemail = strstr(buf, "> ");
char *zone;
unsigned long timestamp;
long tz;
+ enum date_mode date_mode = DATE_NORMAL;
+ const char *formatp;
+
+ /*
+ * We got here because atomname ends in "date" or "date<something>";
+ * it's not possible that <something> is not ":<format>" because
+ * parse_atom() wouldn't have allowed it, so we can assume that no
+ * ":" means no format is specified, and use the default.
+ */
+ formatp = strchr(atomname, ':');
+ if (formatp != NULL) {
+ formatp++;
+ date_mode = parse_date_format(formatp);
+ }
if (!eoemail)
goto bad;
tz = strtol(zone, NULL, 10);
if ((tz == LONG_MIN || tz == LONG_MAX) && errno == ERANGE)
goto bad;
- v->s = xstrdup(show_date(timestamp, tz, 0));
+ v->s = xstrdup(show_date(timestamp, tz, date_mode));
v->ul = timestamp;
return;
bad:
if (name[wholen] != 0 &&
strcmp(name + wholen, "name") &&
strcmp(name + wholen, "email") &&
- strcmp(name + wholen, "date"))
+ prefixcmp(name + wholen, "date"))
continue;
if (!wholine)
wholine = find_wholine(who, wholen, buf, sz);
v->s = copy_name(wholine);
else if (!strcmp(name + wholen, "email"))
v->s = copy_email(wholine);
- else if (!strcmp(name + wholen, "date"))
- grab_date(wholine, v);
+ else if (!prefixcmp(name + wholen, "date"))
+ grab_date(wholine, v, name);
}
/* For a tag or a commit object, if "creator" or "creatordate" is
if (deref)
name++;
- if (!strcmp(name, "creatordate"))
- grab_date(wholine, v);
+ if (!prefixcmp(name, "creatordate"))
+ grab_date(wholine, v, name);
else if (!strcmp(name, "creator"))
v->s = copy_line(wholine);
}
return;
*sub = buf; /* first non-empty line */
buf = strchr(buf, '\n');
- if (!buf)
+ if (!buf) {
+ *body = "";
return; /* no body */
+ }
while (*buf == '\n')
buf++; /* skip blank between subject and body */
*body = buf;
}
}
+/*
+ * generate a format suitable for scanf from a ref_rev_parse_rules
+ * rule, that is replace the "%.*s" spec with a "%s" spec
+ */
+static void gen_scanf_fmt(char *scanf_fmt, const char *rule)
+{
+ char *spec;
+
+ spec = strstr(rule, "%.*s");
+ if (!spec || strstr(spec + 4, "%.*s"))
+ die("invalid rule in ref_rev_parse_rules: %s", rule);
+
+ /* copy all until spec */
+ strncpy(scanf_fmt, rule, spec - rule);
+ scanf_fmt[spec - rule] = '\0';
+ /* copy new spec */
+ strcat(scanf_fmt, "%s");
+ /* copy remaining rule */
+ strcat(scanf_fmt, spec + 4);
+
+ return;
+}
+
+/*
+ * Shorten the refname to an non-ambiguous form
+ */
+static char *get_short_ref(struct refinfo *ref)
+{
+ int i;
+ static char **scanf_fmts;
+ static int nr_rules;
+ char *short_name;
+
+ /* pre generate scanf formats from ref_rev_parse_rules[] */
+ if (!nr_rules) {
+ size_t total_len = 0;
+
+ /* the rule list is NULL terminated, count them first */
+ for (; ref_rev_parse_rules[nr_rules]; nr_rules++)
+ /* no +1 because strlen("%s") < strlen("%.*s") */
+ total_len += strlen(ref_rev_parse_rules[nr_rules]);
+
+ scanf_fmts = xmalloc(nr_rules * sizeof(char *) + total_len);
+
+ total_len = 0;
+ for (i = 0; i < nr_rules; i++) {
+ scanf_fmts[i] = (char *)&scanf_fmts[nr_rules]
+ + total_len;
+ gen_scanf_fmt(scanf_fmts[i], ref_rev_parse_rules[i]);
+ total_len += strlen(ref_rev_parse_rules[i]);
+ }
+ }
+
+ /* bail out if there are no rules */
+ if (!nr_rules)
+ return ref->refname;
+
+ /* buffer for scanf result, at most ref->refname must fit */
+ short_name = xstrdup(ref->refname);
+
+ /* skip first rule, it will always match */
+ for (i = nr_rules - 1; i > 0 ; --i) {
+ int j;
+ int short_name_len;
+
+ if (1 != sscanf(ref->refname, scanf_fmts[i], short_name))
+ continue;
+
+ short_name_len = strlen(short_name);
+
+ /*
+ * check if the short name resolves to a valid ref,
+ * but use only rules prior to the matched one
+ */
+ for (j = 0; j < i; j++) {
+ const char *rule = ref_rev_parse_rules[j];
+ unsigned char short_objectname[20];
+
+ /*
+ * the short name is ambiguous, if it resolves
+ * (with this previous rule) to a valid ref
+ * read_ref() returns 0 on success
+ */
+ if (!read_ref(mkpath(rule, short_name_len, short_name),
+ short_objectname))
+ break;
+ }
+
+ /*
+ * short name is non-ambiguous if all previous rules
+ * haven't resolved to a valid ref
+ */
+ if (j == i)
+ return short_name;
+ }
+
+ free(short_name);
+ return ref->refname;
+}
+
+
/*
* Parse the object referred by ref, and grab needed value.
*/
for (i = 0; i < used_atom_cnt; i++) {
const char *name = used_atom[i];
struct atom_value *v = &ref->value[i];
- if (!strcmp(name, "refname"))
- v->s = ref->refname;
- else if (!strcmp(name, "*refname")) {
- int len = strlen(ref->refname);
- char *s = xmalloc(len + 4);
- sprintf(s, "%s^{}", ref->refname);
- v->s = s;
+ int deref = 0;
+ if (*name == '*') {
+ deref = 1;
+ name++;
+ }
+ if (!prefixcmp(name, "refname")) {
+ const char *formatp = strchr(name, ':');
+ const char *refname = ref->refname;
+
+ /* look for "short" refname format */
+ if (formatp) {
+ formatp++;
+ if (!strcmp(formatp, "short"))
+ refname = get_short_ref(ref);
+ else
+ die("unknown refname format %s",
+ formatp);
+ }
+
+ if (!deref)
+ v->s = refname;
+ else {
+ int len = strlen(refname);
+ char *s = xmalloc(len + 4);
+ sprintf(s, "%s^{}", refname);
+ v->s = s;
+ }
}
}
if ((plen <= namelen) &&
!strncmp(refname, p, plen) &&
(refname[plen] == '\0' ||
- refname[plen] == '/'))
+ refname[plen] == '/' ||
+ p[plen-1] == '/'))
break;
if (!fnmatch(p, refname, FNM_PATHNAME))
break;
return sort;
}
-int cmd_for_each_ref(int ac, const char **av, const char *prefix)
+static int opt_parse_sort(const struct option *opt, const char *arg, int unset)
+{
+ struct ref_sort **sort_tail = opt->value;
+ struct ref_sort *s;
+ int len;
+
+ if (!arg) /* should --no-sort void the list ? */
+ return -1;
+
+ *sort_tail = s = xcalloc(1, sizeof(*s));
+ sort_tail = &s->next;
+
+ if (*arg == '-') {
+ s->reverse = 1;
+ arg++;
+ }
+ len = strlen(arg);
+ s->atom = parse_atom(arg, arg+len);
+ return 0;
+}
+
+static char const * const for_each_ref_usage[] = {
+ "git for-each-ref [options] [<pattern>]",
+ NULL
+};
+
+int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
{
int i, num_refs;
- const char *format = NULL;
+ const char *format = "%(objectname) %(objecttype)\t%(refname)";
struct ref_sort *sort = NULL, **sort_tail = &sort;
- int maxcount = 0;
- int quote_style = -1; /* unspecified yet */
+ int maxcount = 0, quote_style = 0;
struct refinfo **refs;
struct grab_ref_cbdata cbdata;
- for (i = 1; i < ac; i++) {
- const char *arg = av[i];
- if (arg[0] != '-')
- break;
- if (!strcmp(arg, "--")) {
- i++;
- break;
- }
- if (!prefixcmp(arg, "--format=")) {
- if (format)
- die("more than one --format?");
- format = arg + 9;
- continue;
- }
- if (!strcmp(arg, "-s") || !strcmp(arg, "--shell") ) {
- if (0 <= quote_style)
- die("more than one quoting style?");
- quote_style = QUOTE_SHELL;
- continue;
- }
- if (!strcmp(arg, "-p") || !strcmp(arg, "--perl") ) {
- if (0 <= quote_style)
- die("more than one quoting style?");
- quote_style = QUOTE_PERL;
- continue;
- }
- if (!strcmp(arg, "--python") ) {
- if (0 <= quote_style)
- die("more than one quoting style?");
- quote_style = QUOTE_PYTHON;
- continue;
- }
- if (!strcmp(arg, "--tcl") ) {
- if (0 <= quote_style)
- die("more than one quoting style?");
- quote_style = QUOTE_TCL;
- continue;
- }
- if (!prefixcmp(arg, "--count=")) {
- if (maxcount)
- die("more than one --count?");
- maxcount = atoi(arg + 8);
- if (maxcount <= 0)
- die("The number %s did not parse", arg);
- continue;
- }
- if (!prefixcmp(arg, "--sort=")) {
- struct ref_sort *s = xcalloc(1, sizeof(*s));
- int len;
-
- s->next = NULL;
- *sort_tail = s;
- sort_tail = &s->next;
-
- arg += 7;
- if (*arg == '-') {
- s->reverse = 1;
- arg++;
- }
- len = strlen(arg);
- sort->atom = parse_atom(arg, arg+len);
- continue;
- }
- break;
+ struct option opts[] = {
+ OPT_BIT('s', "shell", "e_style,
+ "quote placeholders suitably for shells", QUOTE_SHELL),
+ OPT_BIT('p', "perl", "e_style,
+ "quote placeholders suitably for perl", QUOTE_PERL),
+ OPT_BIT(0 , "python", "e_style,
+ "quote placeholders suitably for python", QUOTE_PYTHON),
+ OPT_BIT(0 , "tcl", "e_style,
+ "quote placeholders suitably for tcl", QUOTE_TCL),
+
+ OPT_GROUP(""),
+ OPT_INTEGER( 0 , "count", &maxcount, "show only <n> matched refs"),
+ OPT_STRING( 0 , "format", &format, "format", "format to use for the output"),
+ OPT_CALLBACK(0 , "sort", sort_tail, "key",
+ "field name to sort on", &opt_parse_sort),
+ OPT_END(),
+ };
+
+ parse_options(argc, argv, opts, for_each_ref_usage, 0);
+ if (maxcount < 0) {
+ error("invalid --count argument: `%d'", maxcount);
+ usage_with_options(for_each_ref_usage, opts);
}
- if (quote_style < 0)
- quote_style = QUOTE_NONE;
+ if (HAS_MULTI_BITS(quote_style)) {
+ error("more than one quoting style?");
+ usage_with_options(for_each_ref_usage, opts);
+ }
+ if (verify_format(format))
+ usage_with_options(for_each_ref_usage, opts);
if (!sort)
sort = default_sort();
sort_atom_limit = used_atom_cnt;
- if (!format)
- format = "%(objectname) %(objecttype)\t%(refname)";
-
- verify_format(format);
memset(&cbdata, 0, sizeof(cbdata));
- cbdata.grab_pattern = av + i;
+ cbdata.grab_pattern = argv;
for_each_ref(grab_single_ref, &cbdata);
refs = cbdata.grab_array;
num_refs = cbdata.grab_cnt;