r4401 - in trunk/src/host: . envedit

werner at docs.openmoko.org werner at docs.openmoko.org
Mon Apr 28 07:25:43 CEST 2008


Author: werner
Date: 2008-04-28 07:25:40 +0200 (Mon, 28 Apr 2008)
New Revision: 4401

Added:
   trunk/src/host/envedit/
   trunk/src/host/envedit/Makefile
   trunk/src/host/envedit/cpp.c
   trunk/src/host/envedit/cpp.h
   trunk/src/host/envedit/env.c
   trunk/src/host/envedit/env.h
   trunk/src/host/envedit/envcpp.pl
   trunk/src/host/envedit/envedit.c
   trunk/src/host/envedit/parse.c
   trunk/src/host/envedit/parse.h
   trunk/src/host/envedit/var.c
   trunk/src/host/envedit/var.h
Log:
Replacement for devirginator/envedit.pl

Improvements:

- no interminable CRC calculations in Perl
- if used with -n, can run in an environment without Perl
- options can be passed in any order



Added: trunk/src/host/envedit/Makefile
===================================================================
--- trunk/src/host/envedit/Makefile	                        (rev 0)
+++ trunk/src/host/envedit/Makefile	2008-04-28 05:25:40 UTC (rev 4401)
@@ -0,0 +1,31 @@
+PREFIX=/usr
+
+CFLAGS_WARN=-Wall -Wshadow -Wstrict-prototypes -Wmissing-prototypes \
+       -Wmissing-declarations
+CFLAGS=-g -O $(CFLAGS_WARN) -DCPP='"$(PREFIX)/bin/envcpp.pl"'
+LDFLAGS=-lz
+
+OBJS=envedit.o env.o parse.o var.o cpp.o
+
+.PHONY:		all install uninstall dep depend clean spotless
+
+all:		envedit
+
+envedit:	$(OBJS)
+
+install:	envedit
+		install -D envedit $(PREFIX)/bin/envedit
+		install -D envcpp.pl $(PREFIX)/bin/envcpp.pl
+
+uninstall:
+		rm -f $(PREFIX)/bin/{envedit,envcpp.pl}
+
+dep depend .depend:
+		$(CPP) $(CFLAGS) -MM -MG *.c >.depend || \
+		  { rm -f .depend; exit 1; }
+
+clean:
+		rm -f $(OBJS)
+
+spotless:	clean
+		rm -f envedit

Added: trunk/src/host/envedit/cpp.c
===================================================================
--- trunk/src/host/envedit/cpp.c	                        (rev 0)
+++ trunk/src/host/envedit/cpp.c	2008-04-28 05:25:40 UTC (rev 4401)
@@ -0,0 +1,210 @@
+/*
+ * cpp.c - CPP subprocess
+ *
+ * Written 2002-2004, 2006 by Werner Almesberger
+ * Copyright 2002,2003 California Institute of Technology
+ * Copyright 2004, 2006 Werner Almesberger
+ *
+ * Distributed under GPLv2, or any later version.
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "cpp.h"
+
+
+static pid_t cpp_pid;
+static int cpp_argc = 0;
+static const char **cpp_argv = NULL;
+static int real_stdin = -1;
+
+
+void add_cpp_arg(const char *arg)
+{
+    if (!cpp_argc)
+	cpp_argc = 1;
+    cpp_argv = realloc(cpp_argv,sizeof(const char *)*(cpp_argc+1));
+    if (!cpp_argv) {
+	perror("realloc");
+	exit(1);
+    }
+    if (cpp_argc == 1)
+	cpp_argv[0] = CPP;
+    if (arg) {
+	arg = strdup(arg);
+	if (!arg) {
+	    perror("strdup");
+	    exit(1);
+	}
+    }
+    cpp_argv[cpp_argc++] = arg;
+}
+
+
+void add_cpp_Wp(const char *arg)
+{
+    char *tmp = strdup(arg);
+    char *curr,*end;
+
+    if (!tmp) {
+	perror("strdup");
+	exit(1);
+    }
+    curr = tmp;
+    do {
+	end = strchr(curr,',');
+	if (end)
+	    *end++ = 0;
+	add_cpp_arg(curr);
+	curr = end;
+    }
+    while (end);
+    free(tmp);
+}
+
+
+static void kill_cpp(void)
+{
+    if (cpp_pid)
+	(void) kill(cpp_pid,SIGTERM);
+}
+
+
+static void run_cpp(const char *name,int fd,int close_fd)
+{
+    char **arg;
+    int fds[2];
+
+    if (pipe(fds) < 0) {
+        perror("pipe");
+        exit(1);
+    }
+    if (name)
+	add_cpp_arg(name);
+    add_cpp_arg(NULL);
+    cpp_pid = fork();
+    if (cpp_pid < 0) {
+        perror("fork");
+        exit(1);
+    }
+    if (!cpp_pid) {
+	if (close(fds[0]) < 0) {
+	    perror("close");
+	    exit(1);
+	}
+	if (close_fd != -1 && close(close_fd) < 0) {
+	    perror("close");
+	    exit(1);
+	}
+	if (fd != -1 && dup2(fd,0) < 0) {
+	    perror("dup2");
+	    exit(1);
+	}
+	if (dup2(fds[1],1) < 0) {
+	    perror("dup2");
+	    exit(1);
+	}
+	if (execvp(CPP,(char **) cpp_argv) < 0) { /* prototype is weird */
+	    perror("execvp " CPP);
+	    exit(1);
+	}
+	/* not reached */
+    }
+    if (close(fds[1]) < 0) {
+	perror("close");
+	exit(1);
+    }
+    real_stdin = dup(0);
+    if (real_stdin < 0) {
+	perror("dup");
+	exit(1);
+    }
+    if (fd != -1 && close(fd) < 0) {
+	perror("close");
+	exit(1);
+    }
+    if (dup2(fds[0],0) < 0) {
+	perror("dup2");
+	exit(1);
+    }
+    for (arg = (char **) cpp_argv+1; *arg; arg++)
+	free(*arg);
+    free(cpp_argv);
+    cpp_argv = NULL;
+    cpp_argc = 0;
+}
+
+
+void run_cpp_on_file(const char *name)
+{
+    run_cpp(name,name ? -1 : 0,-1);
+    atexit(kill_cpp);
+}
+
+
+void run_cpp_on_string(const char *str)
+{
+    int fds[2];
+    pid_t pid;
+    int left,wrote;
+
+    if (pipe(fds) < 0) {
+        perror("pipe");
+        exit(1);
+    }
+    run_cpp(NULL,fds[0],fds[1]);
+    pid = fork();
+    if (pid < 0) {
+	perror("fork");
+	exit(1);
+    }
+    if (!pid) {
+	for (left = strlen(str); left; left -= wrote) {
+	    wrote = write(fds[1],str,left);
+	    if (wrote < 0)
+		break; /* die silently */
+	    str += wrote;
+	}
+	exit(0);
+    }
+    if (close(fds[1]) < 0) {
+	perror("close");
+	exit(1);
+    }
+    atexit(kill_cpp);
+}
+
+
+void reap_cpp(void)
+{
+    int status;
+
+    cpp_pid = 0;
+    if (waitpid(cpp_pid,&status,0) < 0) {
+	perror("waitpid");
+	exit(1);
+    }
+    if (!status) {
+	if (dup2(real_stdin,0) < 0) {
+	    perror("dup2");
+	    exit(1);
+	}
+	return;
+    }
+    if (WIFEXITED(status))
+	exit(WEXITSTATUS(status));
+    if (WIFSIGNALED(status))
+	fprintf(stderr,"cpp terminated with signal %d\n",WTERMSIG(status));
+    else
+	fprintf(stderr,"cpp terminated with incomprehensible status %d\n",
+	  status);
+    exit(1);
+}

Added: trunk/src/host/envedit/cpp.h
===================================================================
--- trunk/src/host/envedit/cpp.h	                        (rev 0)
+++ trunk/src/host/envedit/cpp.h	2008-04-28 05:25:40 UTC (rev 4401)
@@ -0,0 +1,19 @@
+/*
+ * cpp.h - CPP subprocess
+ *
+ * Written 2002,2003 by Werner Almesberger, Caltech Netlab FAST project
+ *
+ * Distributed under GPLv2, or any later version.
+ *
+ */
+
+#ifndef CPP_H
+#define CPP_H
+
+void add_cpp_arg(const char *arg);
+void add_cpp_Wp(const char *arg);
+void run_cpp_on_file(const char *name); /* NULL for stdin */
+void run_cpp_on_string(const char *str);
+void reap_cpp(void);
+
+#endif /* CPP_H */

Added: trunk/src/host/envedit/env.c
===================================================================
--- trunk/src/host/envedit/env.c	                        (rev 0)
+++ trunk/src/host/envedit/env.c	2008-04-28 05:25:40 UTC (rev 4401)
@@ -0,0 +1,256 @@
+/*
+ * env.c - Environment access and file handling
+ *
+ * Copyright (C) 2008 by OpenMoko, Inc.
+ * Written by Werner Almesberger <werner at openmoko.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <zlib.h>
+
+#include "var.h"
+#include "cpp.h"
+#include "parse.h"
+#include "env.h"
+
+
+#define SCRATCH_SIZE	1024	/* buffer for reading in extra environment
+				   data. Any value > 0 is fine. */
+
+
+unsigned long env_size = 16384;
+
+static char *env;
+
+
+static uint32_t crc_env(void)
+{
+	return crc32(crc32(0, NULL, 0), (void *) env+4, env_size-4);
+}
+
+
+static FILE *file_open(const char *name, const char *mode, FILE *default_file)
+{
+	FILE *file;
+
+	if (!strcmp(name, "-"))
+		return default_file;
+	file = fopen(name, mode);
+	if (file)
+		return file;
+	perror(name);
+	exit(1);
+}
+
+
+static void file_close(const char *name, FILE *file, FILE *default_file)
+{
+	if (file == default_file)
+		return;
+	if (fclose(file) != EOF)
+		return;
+	perror(name);
+	exit(1);
+}
+
+
+void read_env(const char *name, int warn_crc)
+{
+	FILE *file;
+	size_t got;
+	uint32_t orig, crc;
+
+	env = malloc(env_size+1);
+	if (!env) {
+		perror("malloc");
+		exit(1);
+	}
+	env[env_size] = 0; /* make sure parse_env stops */
+
+	file = file_open(name, "r", stdin);
+	got = fread(env, 1, env_size, file);
+
+	/* too small for CRC plus \0\0 */
+	if (got < 6) {
+		fprintf(stderr, "environment is too small (%lu bytes < 6)\n",
+		    (unsigned long) got);
+		exit(1);
+	}
+
+	if (got < env_size)
+		memset(env+got, 0, env_size-got);
+	else {
+		char scratch[SCRATCH_SIZE];
+		size_t more;
+		
+		do {
+			more = fread(scratch, 1, SCRATCH_SIZE, file);
+			got += more;
+		}
+		while (more);
+	}
+	if (got != env_size)
+		fprintf(stderr,
+		    "warning: environment is %lu bytes, expected %lu\n",
+		    (unsigned long) got, env_size);
+
+	orig = (uint8_t) env[0] | ((uint8_t) env[1] << 8) |
+	    ((uint8_t) env[2] << 16) | ((uint8_t) env[3] << 24);
+	crc = crc_env();
+	if (crc != orig) {
+		fprintf(stderr,
+		    "CRC error: file says 0x%08lx, calculated 0x%08lx\n",
+		    (unsigned long) orig, (unsigned long) crc);
+		if (!warn_crc)
+			exit(1);
+	}
+
+	file_close(name, file, stdin);
+}
+
+
+void parse_env(void)
+{
+	char *p, *next, *eq;
+
+	for (p = env+4; p != env+env_size; p = next+1) {
+		next = strchr(p, 0);
+		if (next == p)
+			break;
+		if (next == env+env_size) {
+			fprintf(stderr,
+			    "warning: unterminated entry in environment "
+			    "\"%s\"\n", p);
+			break;
+		}
+		eq = strchr(p, '=');
+		if (!eq) {
+			fprintf(stderr,
+			    "warning: ignoring invalid environment entry "
+			    "\"%s\"\n", p);
+			continue;
+		}
+		if (next == eq+1) {
+			fprintf(stderr,
+			    "warning: skipping empty entry for \"%s\"\n", p);
+			continue;
+		}
+		*eq = 0;
+		set_var(p, eq+1);
+	}
+}
+
+
+void print_env(const char *name)
+{
+	FILE *file;
+
+	file = file_open(name, "w", stdout);
+	reset_var();
+	while (1) {
+		const char *var, *value;
+
+		var = next_var(&value);
+		if (!var)
+			break;
+		if (fprintf(file, "%s=%s\n", var, value) < 0) {
+			perror(name);
+			exit(1);
+		}
+	}
+	file_close(name, file, stdout);
+}
+
+
+void write_env(const char *name)
+{
+	FILE *file;
+	char *p;
+	int n;
+	uint32_t crc;
+	size_t wrote;
+
+	memset(env, 0, env_size);
+	reset_var();
+	for (p = env+4; 1; p += n+1) {
+		const char *var, *value;
+
+		var = next_var(&value);
+		if (!var)
+			break;
+		if (p-env+strlen(var)+strlen(value)+3 > env_size) {
+			fprintf(stderr, "environment too big\n");
+			exit(1);
+		}
+		n = sprintf(p, "%s=%s", var, value);
+	}
+	crc = crc_env();
+	env[0] = crc;
+	env[1] = crc >> 8;
+	env[2] = crc >> 16;
+	env[3] = crc >> 24;
+
+	file = file_open(name, "w", stdout);
+	wrote = fwrite(env, 1, env_size, file);
+	if (ferror(file)) {
+		perror(name);
+		exit(1);
+	}
+	if (wrote != env_size) {
+		fprintf(stderr, "%s: short write %lu < %lu\n", name,
+		    (unsigned long) wrote, env_size);
+		exit(1);
+	}
+	file_close(name, file, stdout);
+}
+
+
+void set_env(const char *var)
+{
+	char *tmp, *eq;
+
+	tmp = strdup(var);
+	if (!tmp) {
+		perror("strdup");
+		exit(1);
+	}
+	eq = strchr(tmp, '=');
+	if (!eq) {
+		fprintf(stderr, "invalid assignment syntax: \"%s\"\n", tmp);
+		exit(1);
+	}
+	*eq = 0;
+	if (eq[1])
+		set_var(var, eq+1);
+	else
+		del_var(var);
+}
+
+
+void edit_env(const char *name, int cpp)
+{
+	FILE *file;
+
+	if (!cpp)
+		file = file_open(name, "r", stdin);
+	else {
+		run_cpp_on_file(name);
+		file = stdin;
+	}
+	parse_edit(file);
+	if (cpp)
+		reap_cpp();
+	else
+		file_close(name, file, stdin);
+}

Added: trunk/src/host/envedit/env.h
===================================================================
--- trunk/src/host/envedit/env.h	                        (rev 0)
+++ trunk/src/host/envedit/env.h	2008-04-28 05:25:40 UTC (rev 4401)
@@ -0,0 +1,27 @@
+/*
+ * env.h - Environment access and file handling
+ *
+ * Copyright (C) 2008 by OpenMoko, Inc.
+ * Written by Werner Almesberger <werner at openmoko.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+
+#ifndef ENV_H
+#define ENV_H
+
+extern unsigned long env_size;
+
+void read_env(const char *name, int warn_crc);
+void parse_env(void);
+void print_env(const char *name);
+void write_env(const char *name);
+void set_env(const char *var);
+void edit_env(const char *name, int cpp);
+
+#endif /* ENV_H */

Added: trunk/src/host/envedit/envcpp.pl
===================================================================
--- trunk/src/host/envedit/envcpp.pl	                        (rev 0)
+++ trunk/src/host/envedit/envcpp.pl	2008-04-28 05:25:40 UTC (rev 4401)
@@ -0,0 +1,189 @@
+#!/usr/bin/perl
+#
+# envcpp.pl - U-Boot environment editor
+#
+# Copyright (C) 2006-2008 by OpenMoko, Inc.
+# Written by Werner Almesberger <werner at openmoko.org>
+# All Rights Reserved
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+
+#
+# Preprocessing operations:
+#
+# CPP-like conditionals:
+#
+# #ifdef VAR
+# #ifndef VAR
+# #else
+# #endif
+#
+# Macro expansion:
+#
+# #define MACRO
+# #define MACRO TEXT ...
+# MACRO
+# MACRO##TOKEN
+#
+# Note that #ifdef/#ifndef use both existing environment variables (i.e., those
+# present in the environment at the time of processing) and  preprocessing
+# variables (i.e., those set with -D), while macro expansion only uses
+# preprocessing variables.
+#
+
+
+sub usage
+{
+    print STDERR
+"usage: $0 [-D var[=value]] [env_file]\n".
+"  -D var[=value] define a variable for env_file preprocessing only\n";
+    exit(1);
+}
+
+
+sub expand
+{
+    local ($s, @key) = @_;
+    local ($tmp, $pre, $exp, $post);
+    local ($i, @tmp);
+
+    $tmp = "";
+    while (length $s) {
+	$pre = $s;
+	$exp = "";
+	$post = "";
+	for ($i = 0; $i <= $#key; $i++) {
+	    @tmp = @key;
+	    splice(@tmp, $i, 1);
+	    if ($s =~ /(##)?\b$key[$i]\b(##)?/) {
+		if (length $` < length $pre) {
+		    $pre = $`;
+		    $post = $';
+		    $exp = &expand($def{$key[$i]}, @tmp);
+		}
+	    }
+	}
+	$tmp .= $pre.$exp;
+	$s = $post;
+    }
+    return $tmp;
+}
+
+
+if ($0 =~ m#/[^/]*$#) {
+    push(@INC, $`);
+}
+
+while (@ARGV) {
+    if ($ARGV[0] =~ /^-D/) {
+	shift @ARGV;
+	if ($' ne "") {
+		$def = $';
+	}
+	else {
+		&usage unless defined $ARGV[1];
+		$def = shift @ARGV;
+	}
+	if ($def =~ /=/) {
+	    $def{$`} = $';
+	}
+	else {
+	    $def{$def} = 1;
+	}
+    }
+    elsif ($ARGV[0] =~ /^-.+/) {
+	&usage;
+    }
+    else {
+	&usage if defined $ARGV[1];
+	last;
+    }
+}
+
+$file = shift(@ARGV);
+undef $line;
+if (defined $file) {
+    open(FILE, $file) || die "$file: $!";
+}
+else {
+    *FILE = *STDIN;
+}
+while (<FILE>) {
+    chop;
+    if (/^\s*#if(n?)def\s+(\S+)/) {
+	if (!$false &&
+	  (defined $env{$2} || defined $def{$2}) == ($1 ne "n")) {
+	    $true++;
+	}
+	else {
+	    $false++;
+	}
+    }
+    elsif (/^\s*#else\b/) {
+	if (!$false && !$true) {
+	    print STDERR "$file:$.: #else without #if...\n";
+	    exit(1);
+	}
+	if (!$false) {
+	    $true--;
+	    $false++;
+	}
+	elsif ($false == 1) {
+	    $false--;
+	    $true++;
+	}
+    }
+    elsif (/^\s*#endif\b/) {
+	if (!$false && !$true) {
+	    print STDERR "$file:$.: #endif without #if...\n";
+	    exit(1);
+	}
+	if ($false) {
+	    $false--;
+	}
+	else {
+	    $true--;
+	}
+    }
+    next if $false;
+
+    if (/^\s*#define\s+(\S+)(\s*(.*?))?\s*$/) {
+	if (defined $def{$1} && $def{$1} ne $3) {
+	    print STDERR "$file:$.: redefinition of macro \"$1\"\n";
+	    exit(1);
+	}
+	$def{$1} = $3;
+    }
+
+    $_ = &expand($_, keys %def);
+
+    s/#.*//;
+    s/\s*$//;
+    next if /^\s*$/;
+    if (/^\s+/) {
+	if (!defined $line) {
+	    print STDERR "first line cannot be a continuation\n";
+	    exit(1);
+	}
+	$line .= " ".$';
+    }
+    else {
+	print "$line\n" || die $! if defined $line;
+	$line = $_;
+    }
+}
+close FILE || die $!;
+print "$line\n" || die $! if defined $line;


Property changes on: trunk/src/host/envedit/envcpp.pl
___________________________________________________________________
Name: svn:executable
   + *

Added: trunk/src/host/envedit/envedit.c
===================================================================
--- trunk/src/host/envedit/envedit.c	                        (rev 0)
+++ trunk/src/host/envedit/envedit.c	2008-04-28 05:25:40 UTC (rev 4401)
@@ -0,0 +1,118 @@
+/*
+ * envedit.c - U-Boot environment editor
+ *
+ * Copyright (C) 2008 by OpenMoko, Inc.
+ * Written by Werner Almesberger <werner at openmoko.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "cpp.h"
+#include "env.h"
+
+
+static void usage(const char *name)
+{
+	fprintf(stderr,
+"usage: %s [-f env_file [-n]] [-i file [-c]] [-o file|-p] [-s bytes]\n"
+"       %8s[-D var[=value]] [var=[value] ...]\n\n"
+"  -c             ignore CRC errors in input environment\n"
+"  -f env_file    read changed from file (default: no changes from file)\n"
+"  -i file        read environment from file (default: use empty environment)\n"
+"  -n             don't run env_file through CPP\n"
+"  -o file        write environment to file (default: write to stdout)\n"
+"  -p             print environment in human-readable form\n"
+"  -s bytes       environment size in bytes (default: 16384)\n"
+"  -D var[=value] define a variable for env_file processing only\n"
+"  var=           remove the specified variable\n"
+"  var=value      set the specified variable\n",
+	  name, "");
+	exit(1);
+}
+
+
+int main(int argc, char **argv)
+{
+	int warn_crc = 0;
+	const char *env_file = NULL;
+	const char *in_file = NULL, *out_file = NULL;
+	int cpp = 1;
+	int print = 0;
+	char *tmp, *end;
+	int c, i;
+
+	while ((c = getopt(argc, argv, "cf:i:no:ps:D:")) != EOF)
+		switch (c) {
+		case 'c':
+			warn_crc = 1;
+			break;
+		case 'f':
+			env_file = optarg;
+			break;
+		case 'i':
+			in_file = optarg;
+			break;
+		case 'n':
+			cpp = 0;
+			break;
+		case 'o':
+			out_file = optarg;
+			break;
+		case 'p':
+			print = 1;
+			break;
+		case 's':
+			env_size = strtoul(optarg, &end, 0);
+			if (*end)
+				usage(*argv);
+			break;
+		case 'D':
+			tmp = malloc(strlen(optarg)+3);
+			if (!tmp) {
+				perror("strdup");
+				exit(1);
+			}
+			tmp[0] = '-';
+			tmp[1] = 'D';
+			strcpy(tmp+2, optarg);
+			add_cpp_arg(tmp);
+			free(tmp);
+			break;
+		default:
+			usage(*argv);
+		}
+
+	if (!in_file && warn_crc)
+		usage(*argv);
+	if (out_file && print)
+		usage(*argv);
+	if (!env_file && !cpp)
+		usage(*argv);
+
+	if (in_file) {
+		read_env(in_file, warn_crc);
+		parse_env();
+	}
+	if (env_file)
+		edit_env(env_file, cpp);
+
+	for (i = optind; i != argc; i++)
+		set_env(argv[i]);
+
+	if (print)
+		print_env("-");
+	else
+		write_env(out_file ? out_file : "-");
+
+	return 0;
+}

Added: trunk/src/host/envedit/parse.c
===================================================================
--- trunk/src/host/envedit/parse.c	                        (rev 0)
+++ trunk/src/host/envedit/parse.c	2008-04-28 05:25:40 UTC (rev 4401)
@@ -0,0 +1,249 @@
+/*
+ * parse.c - Environment edits parser
+ *
+ * Copyright (C) 2008 by OpenMoko, Inc.
+ * Written by Werner Almesberger <werner at openmoko.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <assert.h>
+
+#include "var.h"
+#include "parse.h"
+
+
+static enum state {
+	st_0,		/* waiting for variable */
+	st_var,		/* in variable name */
+	st_spc_var,	/* skipping whitespace after variable name */
+	st_spc_eq,	/* skipping whitespace after equal sign */
+	st_val,		/* in value */
+	st_spc_val,	/* skipping whitespace in value */
+} state = st_0;
+
+
+static char *var = NULL, *value = NULL;
+static int line = 1;
+
+
+static void syntax(void)
+{
+	fprintf(stderr, "syntax error in line %d\n", line);
+	exit(1);
+}
+
+
+static void flush(void)
+{
+	assert(var);
+	if (value)
+		set_var(var, value);
+	else
+		del_var(var);
+	free(var);
+	if (value)
+		free(value);
+	var = value = NULL;
+}
+
+
+/*
+ * A bit on the inefficient side, but who really cares ?
+ */
+
+static void add_char(char **base, char c)
+{
+	int len;
+
+	len = *base ? strlen(*base) : 0;
+	*base = realloc(*base, len+2);
+	(*base)[len] = c;
+	(*base)[len+1] = 0;
+}
+
+
+static void add_to_var(char c)
+{
+	add_char(&var, c);
+}
+
+
+static void add_to_value(char c)
+{
+	add_char(&value, c);
+}
+
+
+static int alpha(char c)
+{
+	return c == '_' || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
+}
+
+
+static int alnum(char c)
+{
+	return isdigit(c) ? 1 : alpha(c);
+}
+
+
+static void in_line(int c)
+{
+	switch (state) {
+	case st_var:
+		if (isspace(c)) {
+			state = st_spc_var;
+			break;
+		}
+		if (c == '=') {
+			state = st_spc_eq;
+			break;
+		}
+		if (!alnum(c))
+			syntax();
+		add_to_var(c);
+		break;
+	case st_spc_var:
+		if (isspace(c))
+			break;
+		if (c == '=') {
+			state = st_spc_eq;
+			break;
+		}
+		syntax();
+		break;
+	case st_spc_eq:
+	case st_spc_val:
+		if (isspace(c))
+			break;
+		add_to_value(c);
+		state = st_val;
+		break;
+	case st_val:
+		add_to_value(c);
+		break;
+	default:
+		abort();
+	}
+}
+
+
+static void newline(int c)
+{
+	switch (state) {
+	flush:
+		flush();
+		state = st_0;
+		/* fall through */
+	case st_0:
+		if (isspace(c))
+			syntax();
+		if (!alpha(c))
+			syntax();
+		add_to_var(c);
+		state = st_var;
+		break;
+	case st_var:
+		state = st_spc_var;
+		/* fall through */
+	case st_spc_var:
+		in_line(c);
+		break;
+	case st_spc_eq:
+		if (isspace(c))
+			break;
+		goto flush;
+	case st_val:
+		if (isspace(c)) {
+			state = st_spc_val;
+			break;
+		}
+		goto flush;
+	case st_spc_val:
+		if (isspace(c))
+			break;
+		goto flush;
+	default:
+		abort();
+	}
+}
+
+
+static void double_newline(void)
+{
+	switch (state) {
+	case st_0:
+		break;
+	case st_var:
+	case st_spc_var:
+		syntax();
+		break;
+	case st_spc_eq:
+	case st_val:
+	case st_spc_val:
+		flush();
+		state = st_0;
+		break;
+	default:
+		abort();
+	}
+}
+
+
+void parse_edit(FILE *file)
+{
+	int comment = 0, nl = 1;
+
+	while (1) {
+		int c;
+
+		c = fgetc(file);
+		if (c == EOF)
+			break;
+		if (c == '#')
+			comment = 1;
+		if (c == '\n')
+			line++;
+		if (comment && c != '\n')
+			continue;
+		comment = 0;
+		if (c == '\n') {
+			if (nl)
+				double_newline();
+			nl = 1;
+			if (state == st_val)
+				state = st_spc_val;
+			continue;
+		}
+		if (nl)
+			newline(c);
+		else
+			in_line(c);
+		nl = 0;
+	}
+
+	switch (state) {
+	case st_0:
+		break;
+	case st_var:
+	case st_spc_var:
+		syntax();
+		break;
+	case st_spc_eq:
+	case st_val:
+	case st_spc_val:
+		flush();
+		break;
+	default:
+		abort();
+	}
+}

Added: trunk/src/host/envedit/parse.h
===================================================================
--- trunk/src/host/envedit/parse.h	                        (rev 0)
+++ trunk/src/host/envedit/parse.h	2008-04-28 05:25:40 UTC (rev 4401)
@@ -0,0 +1,23 @@
+/*
+ * parse.h - Environment edits parser
+ *
+ * Copyright (C) 2008 by OpenMoko, Inc.
+ * Written by Werner Almesberger <werner at openmoko.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+
+#ifndef PARSE_H
+#define PARSE_H
+
+#include <stdio.h>
+
+
+void parse_edit(FILE *file);
+
+#endif /* PARSE_H */

Added: trunk/src/host/envedit/var.c
===================================================================
--- trunk/src/host/envedit/var.c	                        (rev 0)
+++ trunk/src/host/envedit/var.c	2008-04-28 05:25:40 UTC (rev 4401)
@@ -0,0 +1,116 @@
+/*
+ * var.c - Environment variable repository
+ *
+ * Copyright (C) 2008 by OpenMoko, Inc.
+ * Written by Werner Almesberger <werner at openmoko.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "var.h"
+
+
+static struct var {
+	const char *name;	/* NULL if deleted */
+	const char *value;
+} *vars = NULL, *curr_var;
+
+static int n_vars = 0;
+
+
+void set_var(const char *name, const char *value)
+{
+	struct var *p, *unused = NULL;
+
+	for (p = vars; p != vars+n_vars; p++) {
+		if (!p->name)
+			unused = p;
+		else {
+			if (!strcmp(p->name, name))
+				break;
+		}
+	}
+	if (p == vars+n_vars && unused)
+		p = unused;
+	if (p == vars+n_vars) {
+		vars = realloc(vars, sizeof(struct var)*2*(n_vars+1));
+		if (!vars) {
+			perror("realloc");
+			exit(1);
+		}
+		memset(vars+n_vars, 0, sizeof(struct var)*(n_vars+2));
+		p = vars+n_vars;
+		n_vars = 2*(n_vars+1);
+	}
+	if (!p->name) {
+		p->name = strdup(name);
+		if (!p->name) {
+			perror("strdup");
+			exit(1);
+		}
+	}
+	if (p->value)
+		free((void *) p->value);
+	p->value = strdup(value);
+	if (!p->value) {
+		perror("strdup");
+		exit(1);
+	}
+}
+
+
+void del_var(const char *name)
+{
+	struct var *p;
+
+	for (p = vars; p != vars+n_vars; p++)
+		if (p->name && !strcmp(p->name, name))
+			break;
+	if (p == vars+n_vars)
+		return;
+	free((void *) p->name);
+	free((void *) p->value);
+	p->name = NULL;
+	p->value = NULL;
+}
+
+
+static int comp(const void *_a, const void *_b)
+{
+	const struct var *a = _a;
+	const struct var *b = _b;
+
+	return a->name ? b->name ? strcmp(a->name, b->name) :
+	    1 : b->name ? -1 : 0;
+}
+
+
+void reset_var(void)
+{
+	if (!vars)
+		return;
+	qsort(vars, n_vars, sizeof(struct var), comp);
+	curr_var = vars;
+}
+
+
+const char *next_var(const char **value)
+{
+	if (!vars)
+		return NULL;
+	while (curr_var != vars+n_vars && !curr_var->name)
+		curr_var++;
+	if (curr_var == vars+n_vars)
+		return NULL;
+	*value = curr_var->value;
+	return (curr_var++)->name;
+}

Added: trunk/src/host/envedit/var.h
===================================================================
--- trunk/src/host/envedit/var.h	                        (rev 0)
+++ trunk/src/host/envedit/var.h	2008-04-28 05:25:40 UTC (rev 4401)
@@ -0,0 +1,23 @@
+/*
+ * var.h - Environment variable repository
+ *
+ * Copyright (C) 2008 by OpenMoko, Inc.
+ * Written by Werner Almesberger <werner at openmoko.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+
+#ifndef VAR_H
+#define VAR_H
+
+void set_var(const char *name, const char *value);
+void del_var(const char *name);
+void reset_var(void);
+const char *next_var(const char const **value);
+
+#endif /* VAR_H */





More information about the commitlog mailing list