/*
 * env2.c
 *
 * Copyright (C) 2005 Andreas Oberritter
 *
 * Homepage: http://www.saftware.de/
 *
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <unistd.h>

#define NVRAM_MAGIC	0x48534C46	// FLSH
#define NVRAM_SIZE      0x1ff0

static void help(FILE *f)
{
	fprintf(f, "Usage: env [OPTION]...\n\n");
	fprintf(f, "printenv\n");
	fprintf(f, "\tenv\n\n");
	fprintf(f, "getenv\n");
	fprintf(f, "\tenv -n <name>\n\n");
	fprintf(f, "setenv\n");
	fprintf(f, "\tenv -n <name> -v <value>\n\n");
	fprintf(f, "unsetenv\n");
	fprintf(f, "\tenv -n <name> -e\n\n");
}

int main(int argc, char *argv[])
{
	int fd, ret = EXIT_FAILURE;
	extern char *optarg;
	int flags;
	int c;
	char *name = NULL;
	char *value = NULL;
	bool erase = false;
	char *nvram;

	while ((c = getopt(argc, argv, "ehn:v:")) != -1) {
		switch (c) {
		case 'h':
			help(stdout);
			return EXIT_SUCCESS;
		case 'e':
			erase = true;
			break;
		case 'n':
			name = strdup(optarg);
			break;
		case 'v':
			value = strdup(optarg);
			break;
		default:
			help(stderr);
			return EXIT_FAILURE;
		}
	}

	if (((name == NULL) && (value != NULL)) || (optind < argc)) {
		help(stderr);
		return EXIT_FAILURE;
	}

	flags = value ? O_RDWR : O_RDONLY;
	fd = open("/dev/nvram", flags);
	if (fd == -1) {
		perror("open");
		return EXIT_FAILURE;
	}

	flags = value ? PROT_READ | PROT_WRITE : PROT_READ;
	nvram = mmap(NULL, NVRAM_SIZE, flags, MAP_SHARED, fd, 0);
	if (nvram == MAP_FAILED) {
		perror("mmap");
		goto out_close;
	}

	printf("%c%c%c%c\n", nvram[0], nvram[1], nvram[2], nvram[3]);

	if (name && erase) {
		char buf[strlen(name) + 1];
		strcpy(buf, name);
		/* nvram_set */
		ret = write(fd, buf, sizeof(buf));
		if (ret == -1) {
			perror("write");
			goto out_munmap;
		}
		/* nvram_commit */
		ret = ioctl(fd, NVRAM_MAGIC);
		if (ret == -1) {
			perror("ioctl");
		}
	} else if (name && value) {
		char buf[strlen(name) + strlen(value) + 2];
		sprintf(buf, "%s=%s", name, value);
		/* nvram_set */
		ret = write(fd, buf, sizeof(buf));
		if (ret == -1) {
			perror("write");
			goto out_munmap;
		}
		/* nvram_commit */
		ret = ioctl(fd, NVRAM_MAGIC);
		if (ret == -1) {
			perror("ioctl");
		}
	} else if (name) {
		char buf[strlen(name) + 1];
		strcpy(buf, name);
		ret = read(fd, buf, sizeof(buf));
		if (ret == -1) {
			perror("read");
			goto out_munmap;
		}
		if (ret == sizeof(unsigned long)) {
			unsigned long *offset = (unsigned long *)buf;
			printf("offset=%lu\n", *offset);
			printf("value=%s\n", &nvram[*offset]);
		}
		else {
			printf("unexpected return value: %d\n", ret);
		}
	} else {
		char buf[NVRAM_SIZE];
		char *ptr;
		buf[0] = '\0';
		ret = read(fd, buf, sizeof(buf));
		if (ret == -1) {
			perror("read");
			goto out_munmap;
		}
		for (ptr = buf; *ptr != '\0'; ptr = memchr(ptr, 0, sizeof(buf) - (ptr - buf)) + 1)
			puts(ptr);
	}

out_munmap:
	munmap(nvram, NVRAM_SIZE);
out_close:
	close(fd);

	return ret;
}


