+#include "builtin.h"
#include "cache.h"
#include "refs.h"
#include "object.h"
#include "tree.h"
#include "blob.h"
#include "quote.h"
-#include <fnmatch.h>
/* Quoting styles */
#define QUOTE_NONE 0
#define QUOTE_SHELL 1
#define QUOTE_PERL 2
#define QUOTE_PYTHON 3
+#define QUOTE_TCL 4
typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;
{ "objectsize", FIELD_ULONG },
{ "objectname" },
{ "tree" },
- { "parent" }, /* NEEDSWORK: how to address 2nd and later parents? */
+ { "parent" },
{ "numparent", FIELD_ULONG },
{ "object" },
{ "type" },
{ "taggername" },
{ "taggeremail" },
{ "taggerdate", FIELD_TIME },
+ { "creator" },
+ { "creatordate", FIELD_TIME },
{ "subject" },
{ "body" },
{ "contents" },
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;
}
while (*cp) {
if (*cp == '%') {
/* %( is the start of an atom;
- * %% is a quoteed per-cent.
+ * %% is a quoted per-cent.
*/
if (cp[1] == '(')
return cp;
*/
static void *get_obj(const unsigned char *sha1, struct object **obj, unsigned long *sz, int *eaten)
{
- char type[20];
- void *buf = read_sha1_file(sha1, type, sz);
+ enum object_type type;
+ void *buf = read_sha1_file(sha1, &type, sz);
if (buf)
*obj = parse_object_buffer(sha1, type, *sz, buf, eaten);
if (deref)
name++;
if (!strcmp(name, "objecttype"))
- v->s = type_names[obj->type];
+ v->s = typename(obj->type);
else if (!strcmp(name, "objectsize")) {
char *s = xmalloc(40);
sprintf(s, "%lu", sz);
}
if (!strcmp(name, "numparent")) {
char *s = xmalloc(40);
+ v->ul = num_parents(commit);
sprintf(s, "%lu", v->ul);
v->s = s;
- v->ul = num_parents(commit);
}
else if (!strcmp(name, "parent")) {
int num = num_parents(commit);
int i;
struct commit_list *parents;
- char *s = xmalloc(42 * num);
+ char *s = xmalloc(41 * num + 1);
v->s = s;
for (i = 0, parents = commit->parents;
parents;
- parents = parents->next, i = i + 42) {
+ parents = parents->next, i = i + 41) {
struct commit *parent = parents->item;
strcpy(s+i, sha1_to_hex(parent->object.sha1));
if (parents->next)
s[i+40] = ' ';
}
+ if (!i)
+ *s = '\0';
}
}
}
return "";
}
-static char *copy_line(const char *buf)
+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;
+ return xmemdupz(buf, eol - buf);
}
-static char *copy_name(const char *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 char *copy_email(const char *buf)
+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
+ * requested, do something special.
+ */
+ if (strcmp(who, "tagger") && strcmp(who, "committer"))
+ return; /* "author" for commit object is not wanted */
+ if (!wholine)
+ wholine = find_wholine(who, wholen, buf, sz);
+ if (!wholine)
+ return;
+ for (i = 0; i < used_atom_cnt; i++) {
+ const char *name = used_atom[i];
+ struct atom_value *v = &val[i];
+ if (!!deref != (*name == '*'))
+ continue;
+ if (deref)
+ name++;
+
+ if (!prefixcmp(name, "creatordate"))
+ grab_date(wholine, v, name);
+ else if (!strcmp(name, "creator"))
+ v->s = copy_line(wholine);
}
}
if (!strcmp(name, "subject"))
v->s = copy_line(subpos);
else if (!strcmp(name, "body"))
- v->s = bodypos;
+ v->s = xstrdup(bodypos);
else if (!strcmp(name, "contents"))
- v->s = subpos;
+ v->s = xstrdup(subpos);
}
}
*v = &ref->value[atom];
}
-static struct refinfo **grab_array;
-static const char **grab_pattern;
-static int *grab_cnt;
+struct grab_ref_cbdata {
+ struct refinfo **grab_array;
+ const char **grab_pattern;
+ int grab_cnt;
+};
/*
* A call-back given to for_each_ref(). It is unfortunate that we
* need to use global variables to pass extra information to this
* function.
*/
-static int grab_single_ref(const char *refname, const unsigned char *sha1)
+static int grab_single_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
{
+ struct grab_ref_cbdata *cb = cb_data;
struct refinfo *ref;
int cnt;
- if (*grab_pattern) {
+ if (*cb->grab_pattern) {
const char **pattern;
int namelen = strlen(refname);
- for (pattern = grab_pattern; *pattern; pattern++) {
+ for (pattern = cb->grab_pattern; *pattern; pattern++) {
const char *p = *pattern;
int plen = strlen(p);
ref->refname = xstrdup(refname);
hashcpy(ref->objectname, sha1);
- cnt = *grab_cnt;
- grab_array = xrealloc(grab_array, sizeof(*grab_array) * (cnt + 1));
- grab_array[cnt++] = ref;
- *grab_cnt = cnt;
+ cnt = cb->grab_cnt;
+ cb->grab_array = xrealloc(cb->grab_array,
+ sizeof(*cb->grab_array) * (cnt + 1));
+ cb->grab_array[cnt++] = ref;
+ cb->grab_cnt = cnt;
return 0;
}
-static struct refinfo **grab_refs(const char **pattern, int *cnt)
-{
- /* Sheesh, we really should make for-each-ref to take
- * callback data.
- */
- *cnt = 0;
- grab_pattern = pattern;
- grab_cnt = cnt;
- for_each_ref(grab_single_ref);
- return grab_array;
-}
-
static int cmp_ref_sort(struct ref_sort *s, struct refinfo *a, struct refinfo *b)
{
struct atom_value *va, *vb;
case QUOTE_PYTHON:
python_quote_print(stdout, v->s);
break;
+ case QUOTE_TCL:
+ tcl_quote_print(stdout, v->s);
+ break;
}
}
return sort;
}
-int cmd_for_each_ref(int ac, const char **av, char *prefix)
+int cmd_for_each_ref(int ac, const char **av, const char *prefix)
{
int i, num_refs;
const char *format = NULL;
int maxcount = 0;
int quote_style = -1; /* unspecified yet */
struct refinfo **refs;
+ struct grab_ref_cbdata cbdata;
for (i = 1; i < ac; i++) {
const char *arg = av[i];
i++;
break;
}
- if (!strncmp(arg, "--format=", 9)) {
+ if (!prefixcmp(arg, "--format=")) {
if (format)
die("more than one --format?");
format = arg + 9;
quote_style = QUOTE_PYTHON;
continue;
}
- if (!strncmp(arg, "--count=", 8)) {
+ 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);
die("The number %s did not parse", arg);
continue;
}
- if (!strncmp(arg, "--sort=", 7)) {
+ if (!prefixcmp(arg, "--sort=")) {
struct ref_sort *s = xcalloc(1, sizeof(*s));
int len;
verify_format(format);
- refs = grab_refs(av + i, &num_refs);
+ memset(&cbdata, 0, sizeof(cbdata));
+ cbdata.grab_pattern = av + i;
+ for_each_ref(grab_single_ref, &cbdata);
+ refs = cbdata.grab_array;
+ num_refs = cbdata.grab_cnt;
for (i = 0; i < used_atom_cnt; i++) {
if (used_atom[i][0] == '*') {