--- /dev/null
+#! /usr/bin/perl
+
+use POSIX;
+use strict;
+use warnings;
+
+my $default = '/etc/default/openflow-switch';
+
+my (%config) = load_config($default);
+if (@ARGV) {
+ foreach my $arg (@ARGV) {
+ my ($key, $value) = $arg =~ /^([^=]+)=(.*)/
+ or die "bad argument '$arg'\n";
+ if ($value ne '') {
+ $config{$key} = $value;
+ } else {
+ delete $config{$key};
+ }
+ }
+ save_config($default, %config);
+}
+print "$_=$config{$_}\n" foreach sort(keys(%config));
+
+sub load_config {
+ my ($file) = @_;
+
+ # Get the list of the variables that the shell sets automatically.
+ my (%auto_vars) = read_vars("set -a && env");
+
+ # Get the variables from $default.
+ my (%config) = read_vars("set -a && . '$default' && env");
+
+ # Subtract.
+ delete @config{keys %auto_vars};
+
+ return %config;
+}
+
+sub read_vars {
+ my ($cmd) = @_;
+ local @ENV;
+ if (!open(VARS, '-|', $cmd)) {
+ print STDERR "$cmd: failed to execute: $!\n";
+ return ();
+ }
+ my (%config);
+ while (<VARS>) {
+ my ($var, $value) = /^([^=]+)=(.*)$/ or next;
+ $config{$var} = $value;
+ }
+ close(VARS);
+ return %config;
+}
+
+sub shell_escape {
+ local $_ = $_[0];
+ if ($_ eq '') {
+ return '""';
+ } elsif (m&^[-a-zA-Z0-9:./%^_+,]*$&) {
+ return $_;
+ } else {
+ s/'/'\\''/;
+ return "'$_'";
+ }
+}
+
+sub shell_assign {
+ my ($var, $value) = @_;
+ return $var . '=' . shell_escape($value);
+}
+
+sub save_config {
+ my ($file, %config) = @_;
+ my (@lines);
+ if (open(FILE, '<', $file)) {
+ @lines = <FILE>;
+ chomp @lines;
+ close(FILE);
+ }
+
+ # Replace all existing variable assignments.
+ for (my ($i) = 0; $i <= $#lines; $i++) {
+ local $_ = $lines[$i];
+ my ($var, $value) = /^\s*([^=#]+)=(.*)$/ or next;
+ if (exists($config{$var})) {
+ $lines[$i] = shell_assign($var, $config{$var});
+ delete $config{$var};
+ } else {
+ $lines[$i] = "#$lines[$i]";
+ }
+ }
+
+ # Find a place to put any remaining variable assignments.
+ VAR:
+ for my $var (keys(%config)) {
+ my $assign = shell_assign($var, $config{$var});
+
+ # Replace the last commented-out variable assignment to $var, if any.
+ for (my ($i) = $#lines; $i >= 0; $i--) {
+ local $_ = $lines[$i];
+ if (/^\s*#\s*$var=/) {
+ $lines[$i] = $assign;
+ next VAR;
+ }
+ }
+
+ # Find a place to add the var: after the final commented line
+ # just after a line that contains "$var:".
+ for (my ($i) = 0; $i <= $#lines; $i++) {
+ if ($lines[$i] =~ /^\s*#\s*$var:/) {
+ for (my ($j) = $i + 1; $j <= $#lines; $j++) {
+ if ($lines[$j] !~ /^\s*#/) {
+ splice(@lines, $j, 0, $assign);
+ next VAR;
+ }
+ }
+ }
+ }
+
+ # Just append it.
+ push(@lines, $assign);
+ }
+
+ open(NEWFILE, '>', "$file.tmp") or die "$file.tmp: create: $!\n";
+ print NEWFILE join('', map("$_\n", @lines));
+ close(NEWFILE);
+ rename("$file.tmp", $file) or die "$file.tmp: rename to $file: $!\n";
+}