From 12e78322c5fdd633c6582b8049ed8b412b275fd6 Mon Sep 17 00:00:00 2001
From: Daniel Friesel <derf@derf.homelinux.org>
Date: Wed, 11 Nov 2009 15:22:01 +0100
Subject: Simplified envstore (no need for linked lists)

from branch 'simple'
---
 man/1/envstore |   4 +-
 src/envstore.c | 247 ++++++++++++++++++++++-----------------------------------
 2 files changed, 98 insertions(+), 153 deletions(-)

diff --git a/man/1/envstore b/man/1/envstore
index 2a65781..d3ae39a 100644
--- a/man/1/envstore
+++ b/man/1/envstore
@@ -39,8 +39,8 @@ By default /tmp/envstore\-\fIEUID\fR
 .SH "LIMITATIONS"
 Variable names or values must not contain null bytes or newlines.
 .PP
-The current maximum length (in bytes) is 127 bytes for the variable name
-and 255 bytes for its content.
+The current maximum length (in bytes) is 255 bytes for the variable name
+and 1023 bytes for its content.
 .SH "AUTHOR"
 \fBenvstore\fR was written by Daniel Friesel <derf@derf.homelinux.org>
 .PP
diff --git a/src/envstore.c b/src/envstore.c
index 171b786..7cb1f68 100644
--- a/src/envstore.c
+++ b/src/envstore.c
@@ -13,94 +13,18 @@
 #include <sys/types.h>
 #include <unistd.h>
 
-/*
- * This struct is part of a linked list and contains one shell parameter
- */
-
-struct parameter {
-	char *name;
-	char *content;
-	struct parameter *next;
-};
-
-
-/*
- * Add element to linked list
- *
- * Parameters: list head (may be NULL), new list element
- * Returns new list head
- */
-
-static struct parameter * list_add(struct parameter *first, struct parameter *new) {
-	new->next = first;
-	first = new;
-	return first;
-}
-
-
-/*
- * Remove element from linked list
- *
- * Parameters: list head, ->name part of element to remove
- * Returns new list head (may be NULL)
- */
-
-static struct parameter * list_remove(struct parameter *first, char *string) {
-	struct parameter *cur = first;
-	struct parameter *prev = NULL;
-
-	while (cur != NULL) {
-		if (strcmp(cur->name, string) == 0) {
-
-			if (prev == NULL)
-				return cur->next;
-
-			prev->next = cur->next;
-			return first;
-		}
-		prev = cur;
-		cur = cur->next;
-	}
-	return first;
-}
-
-
-static void store_save(struct parameter *first, char *file) {
-	struct parameter *cur = first;
-	FILE *fp;
-	char *tmpfile = malloc(strlen(file) + 5);
-
-	if (tmpfile == NULL)
-		err(EXIT_FAILURE, "malloc");
-
-	if (snprintf(tmpfile, strlen(file) + 5, "%s.tmp", file) < 3)
-		err(EXIT_FAILURE, "snprintf");
-
-	umask(0077);
-	fp = fopen(tmpfile, "w+");
-	if (fp == NULL)
-		err(EXIT_FAILURE, "Unable to save store to '%s'", file);
-
-	while (cur != NULL) {
-		fprintf(fp, "%s %s\n", cur->name, cur->content);
-		cur = cur->next;
-	}
-
-	if (fclose(fp) != 0)
-		err(EXIT_FAILURE, "fclose %s", file);
-
-	if (rename(tmpfile, file) != 0)
-		err(EXIT_FAILURE, "Unable to rename '%s' to '%s'", tmpfile, file);
-}
+#define CMD_EVAL 1
+#define CMD_LIST 2
+#define CMD_RM 3
+#define CMD_SAVE 4
 
+#define PARAM_LENGTH 256
+#define VALUE_LENGTH 1024
+#define SCAN_FORMAT "%255s %1023[^\n]\n"
 
-static struct parameter * store_load(char *file) {
-	struct parameter *first = NULL;
-	struct parameter *new;
+static FILE * store_open(char *file) {
 	struct stat finfo;
 	uid_t self_uid = geteuid();
-	char vname[128];
-	char vcontent[256];
 	FILE *fp = fopen(file, "r");
 
 	/* Assume the store file does not exist and the store is empty
@@ -112,7 +36,7 @@ static struct parameter * store_load(char *file) {
 		return NULL;
 
 	if (fstat(fileno(fp), &finfo) != 0)
-		errx(EXIT_FAILURE, "Unable to verify store file permissions (%s)", file);
+		err(EXIT_FAILURE, "Unable to verify store file permissions (%s)", file);
 
 	if (finfo.st_uid != self_uid)
 		errx(EXIT_FAILURE, "Store file '%s' is insecure (must be owned by you, not uid %d)", file, finfo.st_uid);
@@ -120,96 +44,116 @@ static struct parameter * store_load(char *file) {
 	if ((finfo.st_mode & 077) > 0)
 		errx(EXIT_FAILURE, "Store file '%s' has insecure permissions %04o (recommended: 0600)", file, finfo.st_mode & 07777);
 
-	while (fscanf(fp, "%127s %255[^\n]\n", vname, vcontent) != EOF) {
-		new = malloc(sizeof (struct parameter));
-		if (new == NULL)
-			err(EXIT_FAILURE, "malloc");
-
-		new->name = strdup(vname);
-		new->content = strdup(vcontent);
+	return fp;
+}
 
-		if ((new->name == NULL) || (new->content) == NULL)
-			err(EXIT_FAILURE, "strdup");
+static char * new_filename(char *file) {
+	char *new_file = malloc(strlen(file) + 5);
 
-		first = list_add(first, new);
-	}
+	if (new_file == NULL)
+		err(EXIT_FAILURE, "malloc");
 
-	if (fclose(fp) != 0)
-		err(EXIT_FAILURE, "fclose %s", file);
+	if (snprintf(new_file, strlen(file) + 5, "%s.tmp", file) < 3)
+		err(EXIT_FAILURE, "snprintf");
 
-	return first;
+	return new_file;
 }
 
+static FILE * store_open_new(char *file) {
+	FILE *fp;
 
-static inline void command_clear(char *store_file) {
-	/*
-	 * No error checking - assume that the file didn't exist in the first place
-	 * if unlink fails.
-	 */
-	unlink(store_file);
+	umask(0077);
+	fp = fopen(file, "w");
+
+	if (fp == NULL)
+		err(EXIT_FAILURE, "fopen %s", file);
+
+	return fp;
 }
 
 
-static inline void command_eval(char *store_file) {
-	struct parameter *first = store_load(store_file);
-	struct parameter *cur = first;
-	unsigned long int i;
+static inline void print_escaped(char *name, char *content) {
+	unsigned int i;
 
-	while (cur != NULL) {
+	printf("export %s='", name);
 
-		printf("export %s='", cur->name);
-		for (i = 0; i < strlen(cur->content); i++) {
-			if (cur->content[i] == '\'')
-				fputs("'\"'\"'", stdout);
-			else
-				putchar(cur->content[i]);
-		}
-		fputs("'\n", stdout);
-		cur = cur->next;
+	for (i = 0; i < strlen(content); i++) {
+		if (content[i] == '\'')
+			fputs("'\"'\"'", stdout);
+		else
+			putchar(content[i]);
 	}
+
+	fputs("'\n", stdout);
 }
 
 
-static inline void command_list(char *store_file) {
-	struct parameter *first = store_load(store_file);
-	struct parameter *cur = first;
+static void command_disp(char *file, int command) {
+	char vname[PARAM_LENGTH];
+	char vcontent[VALUE_LENGTH];
+	FILE *fp = store_open(file);
+
+	if (fp == NULL)
+		exit(EXIT_SUCCESS);
 
-	while (cur != NULL) {
-		printf("%-15s = %s\n", cur->name, cur->content);
-		cur = cur->next;
+	while (fscanf(fp, SCAN_FORMAT, vname, vcontent) != EOF) {
+		if (command == CMD_LIST)
+			printf("%-15s = %s", vname, vcontent);
+		else
+			print_escaped(vname, vcontent);
 	}
+	if (fclose(fp) != 0)
+		err(EXIT_FAILURE, "fclose %s", file);
 }
 
+static void command_rm_save(char *old_file, char *param, char *value, int argc, int mode) {
+	char curparam[PARAM_LENGTH];
+	char curvalue[VALUE_LENGTH];
+	char *newvalue;
+	char *new_file = new_filename(old_file);
+	FILE *old_fp = store_open(old_file);
+	FILE *new_fp = store_open_new(new_file);
+
+	if (old_fp != NULL) {
+		while (fscanf(old_fp, SCAN_FORMAT, curparam, curvalue) != EOF) {
+			if (strcmp(curparam, param) != 0)
+				if (fprintf(new_fp, "%s %s\n", curparam, curvalue) <= 0)
+					err(EXIT_FAILURE, "fprintf %s", new_file);
+		}
+		if (fclose(old_fp) != 0)
+			err(EXIT_FAILURE, "fclose %s", old_file);
+	}
 
-static inline void command_rm(char *store_file, char *param) {
-	struct parameter *first = store_load(store_file);
-	first = list_remove(first, param);
-	store_save(first, store_file);
-}
+	if (mode == CMD_SAVE) {
+		if (argc > 3)
+			newvalue = value;
+		else
+			newvalue = getenv(param);
 
+		if (newvalue == NULL)
+			errx(EXIT_FAILURE, "parameter '%s' has no value", param);
 
-static inline void command_save(char *store_file, char *param, char *value, int argc) {
-	struct parameter *first = store_load(store_file);
-	struct parameter new;
-	char *newvalue;
-	first = list_remove(first, param);
+		if ((strlen(param) > PARAM_LENGTH - 1) || (strlen(newvalue) > VALUE_LENGTH - 1))
+			errx(EXIT_FAILURE, "parameter or value too long (see man envstore -> LIMITATIONS)");
 
-	if (argc > 3)
-		newvalue = value;
-	else
-		newvalue = getenv(param);
+		if (fprintf(new_fp, "%s %s\n", param, newvalue) <= 0)
+			err(EXIT_FAILURE, "fprintf %s", new_file);
+	}
 
-	if (newvalue == NULL)
-		errx(EXIT_FAILURE, "parameter '%s' has no value", param);
+	if (fclose(new_fp) != 0)
+		err(EXIT_FAILURE, "fclose %s", new_file);
 
-	if ((strlen(param) > 127) || (strlen(newvalue) > 255))
-		errx(EXIT_FAILURE, "parameter or value too long (see man envstore -> LIMITATIONS)");
+	if (rename(new_file, old_file) != 0)
+		err(EXIT_FAILURE, "Unable to rename '%s' to '%s'", new_file, old_file);
+}
 
-	new.name = param;
-	new.content = newvalue;
 
-	first = list_add(first, &new);
-	store_save(first, store_file);
+static inline void command_clear(char *store_file) {
+	/*
+	 * No error checking - assume that the file didn't exist in the first place
+	 * if unlink fails.
+	 */
+	unlink(store_file);
 }
 
 
@@ -231,25 +175,26 @@ int main(int argc, char **argv) {
 		}
 	}
 
+
 	switch (argv[1][0]) {
 		case 'c':
 			command_clear(store_file);
 			break;
 		case 'e':
-			command_eval(store_file);
+			command_disp(store_file, CMD_EVAL);
 			break;
 		case 'l':
-			command_list(store_file);
+			command_disp(store_file, CMD_LIST);
 			break;
 		case 'r':
 			if (argc < 3)
 				errx(EXIT_FAILURE, "Usage: rm <parameter>");
-			command_rm(store_file, argv[2]);
+			command_rm_save(store_file, argv[2], argv[3], argc, CMD_RM);
 			break;
 		case 's':
 			if (argc < 3)
 				errx(EXIT_FAILURE, "Usage: save <parameter> [value]");
-			command_save(store_file, argv[2], argv[3], argc);
+			command_rm_save(store_file, argv[2], argv[3], argc, CMD_SAVE);
 			break;
 		default:
 			errx(EXIT_FAILURE, "Unknown action: %s", argv[1]);
-- 
cgit v1.2.3