[RFC] Git config file reader in Perl (WIP)

15 messages, 5 authors, 2016-06-15 · open the first message on its own page

[RFC] Git config file reader in Perl (WIP)

From: Jakub Narebski <hidden>
Date: 2016-06-15 22:42:49

To make gitweb faster I thought about adding to it, or to Git.pm,
simple nonvalidation config file reader. Nonvalidating means that
it would accept some input which git-repo-config considers invalid.

Some of the trouble is caused because of coner cases like this 
example

[section "sub ; # sect \" ion\\"] ; "]
	key = a " b ; " ; " c ; "

which is valid, but strange.

I'm not proficent in Perl, so help is appreciated.

-- >8 --
#!/usr/bin/perl

use strict;
use warnings;

use Text::Balanced qw(extract_delimited);


sub read_config {
	my $configfile = shift;
	my $section = shift;
	my %config;

	open my $fd, $configfile
		or die "Cannot open $configfile: $!";

	my $sectfull;
	while (my $line = <$fd>) {
		chomp $line;

		if ($line =~ m/^\s*\[\s*([^][:space:]]*)\s*\](.*)$/) {
			# section without subsection

			my $sect = lc($1);

			$sectfull = $sect;

		} elsif ($line =~ m/\s*\[([^][:space:]]*)\s"((?:\\.|[^"])*)"\](.*)$/) {
			# section with subsection

			my $sect = lc($1);
			my $subsect = $2;
			$subsect =~ s/\\(.)/$1/g;

			$sectfull = "$sect.$subsect";

		} elsif ($line =~ m/\s*(\w+)\s*=\s*(.*?)\s*$/) {
			# variable assignment

			my $key = lc($1);
			my $rhs = $2;

			my $value = '';
			my ($next, $remainder, $prefix) = qw();
		DELIM: {
				do {
					($next, $remainder, $prefix) =
						extract_delimited($rhs, '"', qr/(?:\\.|[^"])*/);

					if ($prefix =~ s/\s*[;#].*$//) {
						# comment in unquoted part
						$value .= $prefix;
						last DELIM;
					} else {
						$value .= $prefix if $prefix;
						if ($next && $next =~ s/^"(.*)"$/$1/) {
							$value .= $next;
						}
					}

					$rhs = $remainder;
				} while ($rhs && $next);
			} # DELIM:

			if ($remainder) {
				$remainder =~ s/\s*[;#].*$//;
				$value .= $remainder;
			}

			$value =~ s/\\(.)/$1/g;

			if (exists $config{"$sectfull.$key"}) {
				push @{$config{"$sectfull.$key"}}, $value;
			} else {
				$config{"$sectfull.$key"} = [ $value ];
			}

		} elsif ($line =~ m/^\s*(\w+)\s*(:?[;#].*)?$/) {
			# boolean variable without value

			my $key = lc($1);

			if (exists $config{"$sectfull.$key"}) {
				push @{$config{"$sectfull.$key"}}, undef;
			} else {
				$config{"$sectfull.$key"} = [ undef ];
			}
		} # end if
	}

	close $fd
		or die "Cannot close $configfile: $!";

	return wantarray ? %config : \%config;
}

# --------------------------------------------------------------------------

my %config;

%config = read_config("~/git/.git/config");
%config = read_config("/tmp/jnareb/gitconfig");

foreach my $ckey (sort keys %config) {
	foreach my $cvalue (@{$config{$ckey}}) {
		if (defined $cvalue) {
			print "$ckey=$cvalue\n";
		} else {
			print "$ckey\n";
		}
	}
}

__END__

Re: [RFC] Git config file reader in Perl (WIP)

From: Nikolai Weibull <hidden>
Date: 2016-06-15 22:42:49

On 1/15/07, Johannes Schindelin [off-list ref] wrote:
On Mon, 15 Jan 2007, Nikolai Weibull wrote:
quoted
On 1/15/07, Johannes Schindelin [off-list ref] wrote:
quoted
quoted
On Mon, 15 Jan 2007, Eric Wong wrote:
quoted
quoted
quoted
quoted
Would you write "git repo-config --perl", then? ;-)
quoted
quoted
quoted
The below patch should be a start (only tested on my fairly standard
.git/config).  A --python option should be easy, too :)
quoted
quoted
A bit shorter (and gets the booleans right, plus being even easier
towards --python extension):
quoted
If we're going down this slippery slope, why not just give up and add
a --xml switch instead?
AFAIR this switch was meant to _enhance_ performance.
As far as I can tell, comparing fork() vs. reading a dump with eval
vs. XML isn't meaningful - parsing a 20-line XML file can hardly be
much more (if it even is more) expensive than evaling a file of the
same length.
quoted
That said, parsing the config file as-is can't be so difficult that we
need to export it to separate files with a different syntax, now can it?
The point is having one parser to rule them all, and avoid having
different parsers, all with their own set of shortcomings.
So then you must agree that having one export format makes a lot of
sense, for the same reasons.  Not that I think that an export format
makes sense in the first place.

  nikolai

Re: [RFC] Git config file reader in Perl (WIP)

From: Johannes Schindelin <hidden>
Date: 2016-06-15 22:42:49

Hi,

On Mon, 15 Jan 2007, Nikolai Weibull wrote:
On 1/15/07, Johannes Schindelin [off-list ref] wrote:
quoted
On Mon, 15 Jan 2007, Eric Wong wrote:
quoted
quoted
quoted
Would you write "git repo-config --perl", then? ;-)
quoted
quoted
The below patch should be a start (only tested on my fairly standard
.git/config).  A --python option should be easy, too :)
quoted
A bit shorter (and gets the booleans right, plus being even easier
towards --python extension):
If we're going down this slippery slope, why not just give up and add
a --xml switch instead?
AFAIR this switch was meant to _enhance_ performance.
That said, parsing the config file as-is can't be so difficult that we 
need to export it to separate files with a different syntax, now can it?
The point is having one parser to rule them all, and avoid having 
different parsers, all with their own set of shortcomings.

Ciao,
Dscho

Re: [RFC] Git config file reader in Perl (WIP)

From: Eric Wong <hidden>
Date: 2016-06-15 22:42:49

Jakub Narebski [off-list ref] wrote:
Eric Wong wrote:
quoted
Jakub Narebski [off-list ref] wrote:
quoted
To make gitweb faster I thought about adding to it, or to Git.pm,
simple nonvalidation config file reader. Nonvalidating means that
it would accept some input which git-repo-config considers invalid.
How about something like git-for-each-ref that dumps the entire output
of a config file into an eval()-able string?  That way we don't have to
deal with corner-cases and subtle differences between C and Perl
implementations.
The idea is (at least for gitweb) to avoid cost of fork. And I think
if the format gets documented properly, there should be no differences
in config file parsing.
If the Perl output is redirected to a file (say .git/config.perl) and
only regenerated when .git/config changes, `do(".git/config.perl")' will
likely be faster since all the parsing will be done by Perl itself.

-- 
Eric Wong

Re: [RFC] Git config file reader in Perl (WIP)

From: Shawn O. Pearce <hidden>
Date: 2016-06-15 22:42:49

Eric Wong [off-list ref] wrote:
If the Perl output is redirected to a file (say .git/config.perl) and
only regenerated when .git/config changes, `do(".git/config.perl")' will
likely be faster since all the parsing will be done by Perl itself.
So long as its automatic in gitweb.cgi and based on the stat
attributes of .git/config, OK.  But my database background tells
me two copies of the same thing is fishy...

-- 
Shawn.

Re: [RFC] Git config file reader in Perl (WIP)

From: Jakub Narebski <hidden>
Date: 2016-06-15 22:42:49

Eric Wong wrote:
Jakub Narebski [off-list ref] wrote:
quoted
Eric Wong wrote:
quoted
Jakub Narebski [off-list ref] wrote:
quoted
To make gitweb faster I thought about adding to it, or to Git.pm,
simple nonvalidation config file reader. Nonvalidating means that
it would accept some input which git-repo-config considers invalid.
How about something like git-for-each-ref that dumps the entire output
of a config file into an eval()-able string?  That way we don't have to
deal with corner-cases and subtle differences between C and Perl
implementations.
The idea is (at least for gitweb) to avoid cost of fork. And I think
if the format gets documented properly, there should be no differences
in config file parsing.
If the Perl output is redirected to a file (say .git/config.perl) and
only regenerated when .git/config changes, `do(".git/config.perl")' will
likely be faster since all the parsing will be done by Perl itself.
Would you write "git repo-config --perl", then? ;-)

Besides, I'd rather avoid the need for /tmp/gitweb, and I think usually
gitweb do not have (and should not have) write access to repository.

-- 
Jakub Narebski
Poland

Re: [RFC] Git config file reader in Perl (WIP)

From: Johannes Schindelin <hidden>
Date: 2016-06-15 22:42:49

Hi,

On Mon, 15 Jan 2007, Eric Wong wrote:
quoted
Would you write "git repo-config --perl", then? ;-)
The below patch should be a start (only tested on my fairly standard 
.git/config).  A --python option should be easy, too :)
A bit shorter (and gets the booleans right, plus being even easier 
towards --python extension):

---

 builtin-repo-config.c |   19 +++++++++++++++++--
 1 files changed, 17 insertions(+), 2 deletions(-)
diff --git a/builtin-repo-config.c b/builtin-repo-config.c
index 9063311..8ebf436 100644
--- a/builtin-repo-config.c
+++ b/builtin-repo-config.c
@@ -1,5 +1,6 @@
 #include "builtin.h"
 #include "cache.h"
+#include "quote.h"
 
 static const char git_config_set_usage[] =
 "git-repo-config [ --global ] [ --bool | --int ] [--get | --get-all | --get-regexp | --replace-all | --add | --unset | --unset-all] name [value [value_regex]] | --rename-section old_name new_name | --list";
@@ -12,11 +13,18 @@ static int use_key_regexp;
 static int do_all;
 static int do_not_match;
 static int seen;
+static const char *perl_prefix = NULL;
 static enum { T_RAW, T_INT, T_BOOL } type = T_RAW;
 
 static int show_all_config(const char *key_, const char *value_)
 {
-	if (value_)
+	if (perl_prefix) {
+		printf("%s", perl_prefix);
+		perl_quote_print(stdout, key_);
+		printf(" => ");
+		perl_quote_print(stdout, value_ ? value_ : "true");
+		perl_prefix = ",\n\t";
+	} else if (value_)
 		printf("%s=%s\n", key_, value_);
 	else
 		printf("%s\n", key_);
@@ -138,7 +146,14 @@ int cmd_repo_config(int argc, const char **argv, const char *prefix)
 			type = T_BOOL;
 		else if (!strcmp(argv[1], "--list") || !strcmp(argv[1], "-l"))
 			return git_config(show_all_config);
-		else if (!strcmp(argv[1], "--global")) {
+		else if (!strcmp(argv[1], "--perl")) {
+			int ret;
+			perl_prefix = "\n\t";
+			printf("%%git_config = (");
+			ret = git_config(show_all_config);
+			printf("\n);\n");
+			return ret;
+		} else if (!strcmp(argv[1], "--global")) {
 			char *home = getenv("HOME");
 			if (home) {
 				char *user_config = xstrdup(mkpath("%s/.gitconfig", home));

Re: [RFC] Git config file reader in Perl (WIP)

From: Eric Wong <hidden>
Date: 2016-06-15 22:42:49

Jakub Narebski [off-list ref] wrote:
Eric Wong wrote:
quoted
Jakub Narebski [off-list ref] wrote:
quoted
Eric Wong wrote:
quoted
Jakub Narebski [off-list ref] wrote:
quoted
To make gitweb faster I thought about adding to it, or to Git.pm,
simple nonvalidation config file reader. Nonvalidating means that
it would accept some input which git-repo-config considers invalid.
How about something like git-for-each-ref that dumps the entire output
of a config file into an eval()-able string?  That way we don't have to
deal with corner-cases and subtle differences between C and Perl
implementations.
The idea is (at least for gitweb) to avoid cost of fork. And I think
if the format gets documented properly, there should be no differences
in config file parsing.
If the Perl output is redirected to a file (say .git/config.perl) and
only regenerated when .git/config changes, `do(".git/config.perl")' will
likely be faster since all the parsing will be done by Perl itself.
Would you write "git repo-config --perl", then? ;-)
The below patch should be a start (only tested on my fairly standard
.git/config).  A --python option should be easy, too :)
Besides, I'd rather avoid the need for /tmp/gitweb, and I think usually
gitweb do not have (and should not have) write access to repository.
Good point.  Having to maintain a .git/config.perl in the repository
would be a pain from an administrative standpoint; but on the other hand
.git/config is not often regenerated.

I don't think giving gitweb write access to a repo is a good idea;
either.  Perhaps it would be updated via hook like the HTTP stuff.
IMHO, there is nothing wrong with gitweb writing to /tmp; however.
diff --git a/builtin-repo-config.c b/builtin-repo-config.c
index 9063311..a9ef358 100644
--- a/builtin-repo-config.c
+++ b/builtin-repo-config.c
@@ -1,5 +1,6 @@
 #include "builtin.h"
 #include "cache.h"
+#include "quote.h"
 
 static const char git_config_set_usage[] =
 "git-repo-config [ --global ] [ --bool | --int ] [--get | --get-all | --get-regexp | --replace-all | --add | --unset | --unset-all] name [value [value_regex]] | --rename-section old_name new_name | --list";
@@ -13,6 +14,7 @@ static int do_all;
 static int do_not_match;
 static int seen;
 static enum { T_RAW, T_INT, T_BOOL } type = T_RAW;
+static char *last_key;
 
 static int show_all_config(const char *key_, const char *value_)
 {
@@ -23,6 +25,30 @@ static int show_all_config(const char *key_, const char *value_)
 	return 0;
 }
 
+static int show_perl_config(const char *key_, const char *value_)
+{
+	if (last_key) {
+		if (strcmp(last_key, key_)) {
+			free(last_key);
+			last_key = xstrdup(key_);
+			fputs("\t],\n\t", stdout);
+			perl_quote_print(stdout, key_);
+			fputs(" => [\n", stdout);
+		}
+	} else {
+		last_key = xstrdup(key_);
+		fputc('\t', stdout);
+		perl_quote_print(stdout, key_);
+		fputs(" => [\n", stdout);
+	}
+	if (value_) {
+		fputs("\t\t", stdout);
+		perl_quote_print(stdout, value_);
+		fputs(",\n", stdout);
+	}
+	return 0;
+}
+
 static int show_config(const char* key_, const char* value_)
 {
 	char value[256];
@@ -138,6 +164,17 @@ int cmd_repo_config(int argc, const char **argv, const char *prefix)
 			type = T_BOOL;
 		else if (!strcmp(argv[1], "--list") || !strcmp(argv[1], "-l"))
 			return git_config(show_all_config);
+		else if (!strcmp(argv[1], "--perl")) {
+			int rv;
+			puts("\%git_config = (");
+			rv = git_config(show_perl_config);
+			if (last_key) {
+				puts("\t]\n);\n");
+				free(last_key);
+				last_key = NULL;
+			}
+			return rv;
+		}
 		else if (!strcmp(argv[1], "--global")) {
 			char *home = getenv("HOME");
 			if (home) {
-- 
Eric Wong

Re: [RFC] Git config file reader in Perl (WIP)

From: Nikolai Weibull <hidden>
Date: 2016-06-15 22:42:49

On 1/15/07, Johannes Schindelin [off-list ref] wrote:
On Mon, 15 Jan 2007, Eric Wong wrote:
quoted
quoted
Would you write "git repo-config --perl", then? ;-)
quoted
The below patch should be a start (only tested on my fairly standard
.git/config).  A --python option should be easy, too :)
A bit shorter (and gets the booleans right, plus being even easier
towards --python extension):
If we're going down this slippery slope, why not just give up and add
a --xml switch instead?  Readable by all and a lot more flexible than
--perl, --python, --ruby, --tcl, --sh, --c++, --fortran, --lisp,
--html, --that-next-silver-bullet-language-that-hasnt-been-invented-yet-but-will-need-its-own-switch-once-it-has-been.

That said, parsing the config file as-is can't be so difficult that we
need to export it to separate files with a different syntax, now can
it?

  nikolai

Re: [RFC] Git config file reader in Perl (WIP)

From: Jakub Narebski <hidden>
Date: 2016-06-15 22:42:49

Nikolai Weibull wrote:
On 1/15/07, Johannes Schindelin [off-list ref] wrote:
quoted
On Mon, 15 Jan 2007, Eric Wong wrote:
quoted
quoted
quoted
Would you write "git repo-config --perl", then? ;-)
quoted
quoted
The below patch should be a start (only tested on my fairly standard
.git/config).  A --python option should be easy, too :)
quoted
A bit shorter (and gets the booleans right, plus being even easier
towards --python extension):
If we're going down this slippery slope, why not just give up and add
a --xml switch instead?  Readable by all and a lot more flexible than
--perl, --python, --ruby, --tcl, --sh, --c++, --fortran, --lisp,
--html, --that-next-silver-bullet-language [...].

That said, parsing the config file as-is can't be so difficult that we
need to export it to separate files with a different syntax, now can
it?
Parsing the config file is not _that_ difficult (the first post in this 
thread had config reader in Perl), but it is not that easy: case 
(in)setiviness, quoting, escaping, comments, removing leading and 
trailing whitespace when not quoted...

P.S. I'd rather have an additional implementation (in Perl) conforming 
to yet to be written git ini-like config file specs, to find places 
where canonic parser, git-repo-config, doesn't conform to the specs.
-- 
Jakub Narebski
Poland

Re: [RFC] Git config file reader in Perl (WIP)

From: Jakub Narebski <hidden>
Date: 2016-06-15 22:42:49

Eric Wong wrote:
Jakub Narebski [off-list ref] wrote:
quoted
To make gitweb faster I thought about adding to it, or to Git.pm,
simple nonvalidation config file reader. Nonvalidating means that
it would accept some input which git-repo-config considers invalid.
How about something like git-for-each-ref that dumps the entire output
of a config file into an eval()-able string?  That way we don't have to
deal with corner-cases and subtle differences between C and Perl
implementations.
The idea is (at least for gitweb) to avoid cost of fork. And I think
if the format gets documented properly, there should be no differences
in config file parsing.

Please remember also that is first draft of git config file reader
in perl; an alpha version.
-- 
Jakub Narebski
Poland

Re: [RFC] Git config file reader in Perl (WIP)

From: Eric Wong <hidden>
Date: 2016-06-15 22:42:49

Jakub Narebski [off-list ref] wrote:
To make gitweb faster I thought about adding to it, or to Git.pm,
simple nonvalidation config file reader. Nonvalidating means that
it would accept some input which git-repo-config considers invalid.
How about something like git-for-each-ref that dumps the entire output
of a config file into an eval()-able string?  That way we don't have to
deal with corner-cases and subtle differences between C and Perl
implementations.

-- 
Eric Wong

Re: [RFC] Git config file reader in Perl (WIP)

From: Eric Wong <hidden>
Date: 2016-06-15 22:42:49

Johannes Schindelin [off-list ref] wrote:
Hi,

On Mon, 15 Jan 2007, Eric Wong wrote:
quoted
quoted
Would you write "git repo-config --perl", then? ;-)
The below patch should be a start (only tested on my fairly standard 
.git/config).  A --python option should be easy, too :)
A bit shorter (and gets the booleans right, plus being even easier 
towards --python extension):
Your version doesn't get arrays right, however.

Here's a Perl/Python/Ruby version below.  It should be extendable for
other languages; feedback and additions appreciated:

Note that usage has been changed to --dump=(perl|python|ruby)

I may add key_suffix to lang_dump just to be consistent with pairings,
but array_start seems to handle all cases of it and it would be
redundant...
--- a/builtin-repo-config.c
+++ b/builtin-repo-config.c
@@ -1,5 +1,6 @@
 #include "builtin.h"
 #include "cache.h"
+#include "quote.h"
 
 static const char git_config_set_usage[] =
 "git-repo-config [ --global ] [ --bool | --int ] [--get | --get-all | --get-regexp | --replace-all | --add | --unset | --unset-all] name [value [value_regex]] | --rename-section old_name new_name | --list";
@@ -14,6 +15,90 @@ static int do_not_match;
 static int seen;
 static enum { T_RAW, T_INT, T_BOOL } type = T_RAW;
 
+struct lang_dump {
+	const char *name;
+	const char *decl_start;
+	const char *decl_end;
+	const char *key_prefix;
+	const char *array_start;
+	const char *array_end;
+	const char *val_prefix;
+	const char *val_suffix;
+	const char *true_val; /* should already be quoted, if needed */
+	void (*quote_key_fn)(FILE *, const char*);
+	void (*quote_val_fn)(FILE *, const char*);
+};
+static char *last_key;
+static struct lang_dump *lang;
+static struct lang_dump lang_dump_defs[] = {
+	{ "perl",
+		"\%git_config = (\n", ");\n",
+		"\t",
+		" => [\n", "\t],\n",
+		"\t\t", ",\n",
+		"'true'",
+		perl_quote_print, perl_quote_print },
+	{ "python",
+		"git_config = {\n", "}\n",
+		"    ",
+		" : [\n", "    ],\n",
+		"        ", ",\n",
+		"True",
+		python_quote_print, python_quote_print },
+	{ "ruby", /* Ruby is very Perl-like */
+		"git_config = {\n", "}\n",
+		"  ",
+		" => [\n", "  ],\n",
+		"    ", ",\n",
+		"true",
+		perl_quote_print, perl_quote_print },
+};
+
+static int show_lang_config(const char *key_, const char *value_)
+{
+	if (last_key) {
+		if (strcmp(last_key, key_)) {
+			free(last_key);
+			fputs(lang->array_end, stdout);
+			goto new_key;
+		}
+	} else {
+new_key:
+		last_key = xstrdup(key_);
+		fputs(lang->key_prefix, stdout);
+		lang->quote_key_fn(stdout, key_);
+		fputs(lang->array_start, stdout);
+	}
+	fputs(lang->val_prefix, stdout);
+	if (value_)
+		lang->quote_val_fn(stdout, value_);
+	else
+		fputs(lang->true_val, stdout);
+	fputs(lang->val_suffix, stdout);
+	return 0;
+}
+
+static int show_lang_config_all(const char *lang_name)
+{
+	int i, rv;
+	for (i = ARRAY_SIZE(lang_dump_defs); --i >= 0; ) {
+		if (strcmp(lang_name, lang_dump_defs[i].name))
+			continue;
+		lang = lang_dump_defs + i;
+		fputs(lang->decl_start, stdout);
+		rv = git_config(show_lang_config);
+		if (last_key) {
+			free(last_key);
+			last_key = NULL;
+			fputs(lang->array_end, stdout);
+			fputs(lang->decl_end, stdout);
+		}
+		return rv;
+	}
+	fputs("Dumping config to '%s' is not yet supported", stderr);
+	return -1;
+}
+
 static int show_all_config(const char *key_, const char *value_)
 {
 	if (value_)
@@ -138,6 +223,8 @@ int cmd_repo_config(int argc, const char **argv, const char *prefix)
 			type = T_BOOL;
 		else if (!strcmp(argv[1], "--list") || !strcmp(argv[1], "-l"))
 			return git_config(show_all_config);
+		else if (!strncmp(argv[1], "--dump=", 7))
+			return show_lang_config_all(argv[1] + 7);
 		else if (!strcmp(argv[1], "--global")) {
 			char *home = getenv("HOME");
 			if (home) {
-- 
Eric Wong

Re: [RFC] Git config file reader in Perl (WIP)

From: Johannes Schindelin <hidden>
Date: 2016-06-15 22:42:49

Hi,

On Tue, 16 Jan 2007, Eric Wong wrote:
Johannes Schindelin [off-list ref] wrote:
quoted
On Mon, 15 Jan 2007, Eric Wong wrote:
quoted
quoted
Would you write "git repo-config --perl", then? ;-)
The below patch should be a start (only tested on my fairly standard 
.git/config).  A --python option should be easy, too :)
A bit shorter (and gets the booleans right, plus being even easier 
towards --python extension):
Your version doesn't get arrays right, however.
That's right.

I'd like that code to be simpler, though. Way simpler.
quoted hunk
--- a/builtin-repo-config.c
+++ b/builtin-repo-config.c
@@ -1,5 +1,6 @@
 #include "builtin.h"
 #include "cache.h"
+#include "quote.h"
 
 static const char git_config_set_usage[] =
 "git-repo-config [ --global ] [ --bool | --int ] [--get | --get-all | --get-regexp | --replace-all | --add | --unset | --unset-all] name [value [value_regex]] | --rename-section old_name new_name | --list";
@@ -14,6 +15,90 @@ static int do_not_match;
 static int seen;
 static enum { T_RAW, T_INT, T_BOOL } type = T_RAW;
 
+struct lang_dump {
+	const char *name;
+	const char *decl_start;
+	const char *decl_end;
+	const char *key_prefix;
+	const char *array_start;
+	const char *array_end;
+	const char *val_prefix;
+	const char *val_suffix;
+	const char *true_val; /* should already be quoted, if needed */
+	void (*quote_key_fn)(FILE *, const char*);
+	void (*quote_val_fn)(FILE *, const char*);
+};
+static char *last_key;
+static struct lang_dump *lang;
+static struct lang_dump lang_dump_defs[] = {
+	{ "perl",
+		"\%git_config = (\n", ");\n",
0> +		"\t",
+		" => [\n", "\t],\n",
+		"\t\t", ",\n",
+		"'true'",
+		perl_quote_print, perl_quote_print },
The two quote members seem to be the same for _all_ three languages.
+	{ "python",
+		"git_config = {\n", "}\n",
+		"    ",
I don't understand why you do not consolidate that into using tabs for 
_all_ backends?

+static int show_lang_config(const char *key_, const char *value_)
+{
+	if (last_key) {
+		if (strcmp(last_key, key_)) {
+			free(last_key);
+			fputs(lang->array_end, stdout);
+			goto new_key;
+		}
+	} else {
+new_key:
+		last_key = xstrdup(key_);
+		fputs(lang->key_prefix, stdout);
+		lang->quote_key_fn(stdout, key_);
+		fputs(lang->array_start, stdout);
+	}
So this makes _all_ config vars arrays? It is consistent, yes... but it is 
also ugly, no?
+static int show_lang_config_all(const char *lang_name)
+{
+	int i, rv;
+	for (i = ARRAY_SIZE(lang_dump_defs); --i >= 0; ) {
+		if (strcmp(lang_name, lang_dump_defs[i].name))
+			continue;
+		lang = lang_dump_defs + i;
IMHO this would be much easier to read using a path_list:

	struct path_list_item *item = path_list_lookup(lang_name, &langs);

	if (item == NULL)
		return -1;

	lang = item->util;
+		fputs(lang->decl_start, stdout);
+		rv = git_config(show_lang_config);
+		if (last_key) {
+			free(last_key);
+			last_key = NULL;
+			fputs(lang->array_end, stdout);
+			fputs(lang->decl_end, stdout);
If the config is empty, no decl_end is printed, right?
+		}
+		return rv;
+	}
+	fputs("Dumping config to '%s' is not yet supported", stderr);
+	return -1;
+}
Ciao,
Dscho

Re: [RFC] Git config file reader in Perl (WIP)

From: Eric Wong <hidden>
Date: 2016-06-15 22:42:49

Johannes Schindelin [off-list ref] wrote:
I'd like that code to be simpler, though. Way simpler.
I've tried, but I'm not sure how much farther I can go.
quoted
+	{ "perl",
+		"\%git_config = (\n", ");\n",
0> +		"\t",
quoted
+		" => [\n", "\t],\n",
+		"\t\t", ",\n",
+		"'true'",
+		perl_quote_print, perl_quote_print },
The two quote members seem to be the same for _all_ three languages.
Yes, I was trying to imagine a corner case where quoting
for keys could be different than values.

I know Ruby can use :symbols but those don't support '.' and '-' as
far as I know, Perl doesn't require quoting for keys if they match
^-?\w+.  I guess just using quoted strings as keys is fine enough.
The patch below uses the same quote operator for both keys and values.
quoted
+	{ "python",
+		"git_config = {\n", "}\n",
+		"    ",
I don't understand why you do not consolidate that into using tabs for 
_all_ backends?
I wanted to make things look familiar to people using those languages.

Most Ruby programmers I've seen use 2-space indents.  I myself have given
into using them when I write Ruby.

Python doesn't seem to care about indentation for data structures, but I
think Python programmers prefer spaces for indentation.  I'm not very
experienced with Python, however.

Tabs are my own personal preference for Perl; but indentation is very
inconsistent in Perl code I've looked at :/.  perlstyle(1) actually
recommends 4 space indents...

On the other hand, we are writing for interpreters and not humans.  So
maybe just using tabs is good enough (some formatting makes debugging
easier, so I'm not putting everything on one line :).
quoted
+static int show_lang_config(const char *key_, const char *value_)
+{
+	if (last_key) {
+		if (strcmp(last_key, key_)) {
+			free(last_key);
+			fputs(lang->array_end, stdout);
+			goto new_key;
+		}
+	} else {
+new_key:
+		last_key = xstrdup(key_);
+		fputs(lang->key_prefix, stdout);
+		lang->quote_key_fn(stdout, key_);
+		fputs(lang->array_start, stdout);
+	}
So this makes _all_ config vars arrays? It is consistent, yes... but it is 
also ugly, no?
Somewhat ugly, yes, but I think returning everything as an array would
make things easier for code using the data structures.  They could
always just reference the first element if they didn't want the array
instead of having to find the type with ref() or .kind_of?
quoted
+static int show_lang_config_all(const char *lang_name)
+{
+	int i, rv;
+	for (i = ARRAY_SIZE(lang_dump_defs); --i >= 0; ) {
+		if (strcmp(lang_name, lang_dump_defs[i].name))
+			continue;
+		lang = lang_dump_defs + i;
IMHO this would be much easier to read using a path_list:

	struct path_list_item *item = path_list_lookup(lang_name, &langs);

	if (item == NULL)
		return -1;

	lang = item->util;
Ah, I didn't know about path_list_lookup().  Now that I know about it, I
don't think it's worth it to create the extra data structures around
it.  We can just as easily switch to bsearch(3) when we add more
languages.
quoted
+		fputs(lang->decl_start, stdout);
+		rv = git_config(show_lang_config);
+		if (last_key) {
+			free(last_key);
+			last_key = NULL;
+			fputs(lang->array_end, stdout);
+			fputs(lang->decl_end, stdout);
If the config is empty, no decl_end is printed, right?
Good catch.  Thanks.
quoted
+		}
+		return rv;
+	}
+	fputs("Dumping config to '%s' is not yet supported", stderr);
+	return -1;
+}
--- a/builtin-repo-config.c
+++ b/builtin-repo-config.c
@@ -1,5 +1,6 @@
 #include "builtin.h"
 #include "cache.h"
+#include "quote.h"
 
 static const char git_config_set_usage[] =
 "git-repo-config [ --global ] [ --bool | --int ] [--get | --get-all | --get-regexp | --replace-all | --add | --unset | --unset-all] name [value [value_regex]] | --rename-section old_name new_name | --list";
@@ -14,6 +15,89 @@ static int do_not_match;
 static int seen;
 static enum { T_RAW, T_INT, T_BOOL } type = T_RAW;
 
+struct lang_dump {
+	const char *name;
+	const char *decl_start;
+	const char *decl_end;
+	const char *key_prefix;
+	const char *array_start;
+	const char *array_end;
+	const char *val_prefix;
+	const char *val_suffix;
+	const char *true_val; /* should already be quoted, if needed */
+	void (*quote_fn)(FILE *, const char*);
+};
+static char *last_key;
+static struct lang_dump *lang;
+static struct lang_dump lang_dump_defs[] = {
+	{ "perl",
+		"\%git_config = (\n", ");\n",
+		"\t",
+		" => [\n", "\t],\n",
+		"\t\t", ",\n",
+		"'true'",
+		perl_quote_print },
+	{ "python",
+		"git_config = {\n", "}\n",
+		"    ",
+		" : [\n", "    ],\n",
+		"        ", ",\n",
+		"True",
+		python_quote_print },
+	{ "ruby", /* Ruby is very Perl-like */
+		"git_config = {\n", "}\n",
+		"  ",
+		" => [\n", "  ],\n",
+		"    ", ",\n",
+		"true",
+		perl_quote_print },
+};
+
+static int show_lang_config(const char *key_, const char *value_)
+{
+	if (last_key) {
+		if (strcmp(last_key, key_)) {
+			free(last_key);
+			fputs(lang->array_end, stdout);
+			goto new_key;
+		}
+	} else {
+new_key:
+		last_key = xstrdup(key_);
+		fputs(lang->key_prefix, stdout);
+		lang->quote_fn(stdout, key_);
+		fputs(lang->array_start, stdout);
+	}
+	fputs(lang->val_prefix, stdout);
+	if (value_)
+		lang->quote_fn(stdout, value_);
+	else
+		fputs(lang->true_val, stdout);
+	fputs(lang->val_suffix, stdout);
+	return 0;
+}
+
+static int show_lang_config_all(const char *lang_name)
+{
+	int i, rv;
+	for (i = ARRAY_SIZE(lang_dump_defs); --i >= 0; ) {
+		if (strcmp(lang_name, lang_dump_defs[i].name))
+			continue;
+		lang = lang_dump_defs + i;
+		fputs(lang->decl_start, stdout);
+		rv = git_config(show_lang_config);
+		if (last_key) {
+			free(last_key);
+			last_key = NULL;
+			fputs(lang->array_end, stdout);
+		}
+		fputs(lang->decl_end, stdout);
+		return rv;
+	}
+	fputs("Dumping config to '%s' is not yet supported", stderr);
+	return -1;
+}
+
 static int show_all_config(const char *key_, const char *value_)
 {
 	if (value_)
@@ -138,6 +222,8 @@ int cmd_repo_config(int argc, const char **argv, const char *prefix)
 			type = T_BOOL;
 		else if (!strcmp(argv[1], "--list") || !strcmp(argv[1], "-l"))
 			return git_config(show_all_config);
+		else if (!strncmp(argv[1], "--dump=", 7))
+			return show_lang_config_all(argv[1] + 7);
 		else if (!strcmp(argv[1], "--global")) {
 			char *home = getenv("HOME");
 			if (home) {
-- 
Eric Wong
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help