/* load.c - load a menu file into memory
 */

#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pwd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <ctype.h>
#include "menucmd.h"
#include "hook.h"


char *menu_path = 0;
char *menu_dfl_path = 0;
char *menu_file = 0;
int menu_f_skip = 0;
int menu_f_quit = 0;
extern int menu_if__skipdepth;
extern int menu_if__depth;

void menu_cmd_init(void);
void menu_command(char *, menu_t *, menuentry_t *, menuentry_t *);


/* Extend the array of menu entries in "menu" by 1, returning nonzero
 * on failure.
 */
int menu_addentry(menu_t * menu)
{
	menuentry_t *ptr;

	ptr =
	    realloc(menu->menu,
		    sizeof(menuentry_t) * (1 + menu->num_entries));
	if (!ptr)
		return (1);

	menu->menu = ptr;
	memset(&(ptr[menu->num_entries]), 0, sizeof(*ptr));
	menu->num_entries++;

	return (0);
}


/* Put the entry "current", with details given in "buf", into entry "entry".
 */
void menu_entry(menuentry_t * entry, menuentry_t * current, char *buf)
{
	int type, param = 0;
	char temp[1024];
	char *e = "";

	memcpy(entry, current, sizeof(*entry));

	entry->key = toupper(buf[0]);
	buf++;
	while (buf[0] == ' ')
		buf++;

	type = buf[0];
	if (type != 0) {
		buf++;
		if (buf[0] == '&')
			param = '&';
		else if (buf[0] == '!')
			param = '!';
		else if (isdigit(buf[0]))
			param = atoi(buf);
		while (buf[0] > ' ')
			buf++;
		while (buf[0] == ' ')
			buf++;
	}

	switch (type) {
	case 'a':
		type = 'A';
	case 'A':
		entry->type = MENU_ENTRY_ANIMATION;
		break;
	case 'b':
		type = 'B';
	case 'B':
		entry->type = MENU_ENTRY_BINARY;
		break;
	case 'c':
		type = 'C';
	case 'C':
		entry->type = MENU_ENTRY_COMMENTFILE;
		break;
	case 'f':
		type = 'F';
	case 'F':
		entry->type = MENU_ENTRY_FORMATTED;
		break;
	case 'l':
		type = 'L';
	case 'L':
		entry->type = MENU_ENTRY_LISTED;
		break;
	case 'm':
		type = 'M';
	case 'M':
		entry->type = MENU_ENTRY_MENU;
		break;
	case 'r':
		type = 'R';
	case 'R':
		entry->type = MENU_ENTRY_READONLY;
		break;
	case 's':
		type = 'S';
	case 'S':
		entry->type = MENU_ENTRY_SPOOLED;
		entry->param = param;
		break;
	case 't':
		type = 'T';
	case 'T':
		entry->type = MENU_ENTRY_TELNET;
		break;
	case 'u':
		type = 'U';
	case 'U':
		entry->type = MENU_ENTRY_UNFORMATTED;
		break;
	case 'v':
		type = 'V';
	case 'V':
		entry->type = MENU_ENTRY_VTANSI;
		break;
	case 'x':
		type = 'X';
	case 'X':
		entry->type = MENU_ENTRY_EXECUTABLE;
		entry->param = ((param == '&')
				|| (param == '!')) ? param : 0;
		break;
	default:
		if (bbs_hook(HOOK_MENU_TYPE, current, menu_file)) {
			entry->type = current->type;
			entry->status |= MENU_STATUS_NOLAST;
		} else {
			entry->type = MENU_ENTRY_TEXT;
			free(entry->title);
			entry->title = strdup("\035CR\035BERROR:\035b "
					      "unknown entry type"
					      "\035CA");
			entry->key = 0;
		}
		break;
	}

	if (strchr("ABCFLMRUV", type)) {    /* absolutify filename */
		if (buf[0] == '/') {
			strncpy(temp, buf, 1020);
		} else {
			strncpy(temp, menu_path, 1000);
			strcat(temp, "/");
			strncat(temp, buf, 1000 - strlen(temp));
		}
		entry->filename = ldb_filename(temp);
		if (bbs_hook(HOOK_ACL_CHECK, entry->filename, &e)) {
			entry->filename = 0;
			entry->type = MENU_ENTRY_TEXT;
			free(entry->title);
			entry->title = strdup(e);
			entry->key = 0;
		} else {
			entry->filename = strdup(entry->filename);
		}
	} else {
		entry->filename = strdup(buf);
	}

	if (entry->viewfile) {		    /* check viewfile ACL */
		if (bbs_hook(HOOK_ACL_CHECK, entry->viewfile, 0)) {
			free(entry->viewfile);
			entry->viewfile = 0;
		}
	}

	if (entry->commentfile) {	    /* check commentfile ACL */
		if (bbs_hook(HOOK_ACL_CHECK, entry->commentfile, 0)) {
			free(entry->commentfile);
			entry->commentfile = 0;
		}
	}
}


/* Load the menu file "file" into memory by reading the stream "fptr" until
 * EOF. Returns an allocated menu structure, or 0 on error.
 */
menu_t *menu_load(FILE * fptr, char *file)
{
	struct passwd *p;
	struct stat sb;
	menu_t *menu;
	char buf[1024];
	menuentry_t dfl;
	menuentry_t current;
	char *a;

	ldb_init();
	menu_cmd_init();

	menu = calloc(1, sizeof(menu_t));
	if (!menu)
		return (0);

	if ((file) && (!strcmp(file, "-")))
		file = 0;

	menu_file = file;

	if (file) {			    /* work out path of menu directory */
		menu_path = strdup(ldb_filename(file));
		a = strrchr(menu_path, '/');
		if (a)
			a[1] = 0;
	} else {
		getcwd(buf, 1023);
		strcat(buf, "/");
		menu_path = strdup(buf);
	}

	menu_dfl_path = strdup(menu_path);

	p = getpwuid(getuid());
	if (file) {			    /* work out the owner of the file */
		if (!stat(file, &sb))
			p = getpwuid(sb.st_uid);
	}

	menu->owner = strdup((p != 0) ? p->pw_name : "unknown");

	memset(&dfl, 0, sizeof(dfl));
	dfl.status = MENU_STATUS_ADD;

	memcpy(&current, &dfl, sizeof(dfl));

	menu_f_skip = 0;
	menu_if__skipdepth = -1;
	menu_if__depth = -1;
	menu_f_quit = 0;

	while (!feof(fptr) && !ferror(fptr) && !menu_f_quit) {
		buf[0] = 0;
		fgets(buf, 1024, fptr);	    /* read line */
		a = strchr(buf, 10);	    /* strip \n */
		if (a)
			a[0] = 0;
		a = strchr(buf, '#');	    /* strip # */
		if (a)
			a[0] = 0;
		if ((buf[0] == ' ') || (buf[0] == 9) || (buf[0] == 0))
			continue;
		if (buf[0] == '.') {	    /* . command */
			menu_envexpand(buf, 1024);
			menu_command(buf + strspn(buf, ". \011"), menu,
				     &dfl, &current);
		} else if (menu_f_skip == 0) {	/* something else... */
			if (!current.title) {	/* menu entry title */
				menu_envexpand(buf, 1024);
				current.title = strdup(buf);
				current.log_type = dfl.log_type;
				if (dfl.logfile)
					current.logfile =
					    strdup(dfl.logfile);
			} else {	    /* menu entry specifier */

				if (menu_addentry(menu)) {	/* extend array */
					free(current.title);
					free(current.logfile);
					free(dfl.logfile);
					free(menu_path);
					free(menu_dfl_path);
					menu_free(menu);
					return (0);
				}

				menu_envexpand(buf, 1024);
				menu_entry(&
					   (menu->
					    menu[menu->num_entries - 1]),
					   &current, buf);
				memcpy(&current, &dfl, sizeof(dfl));
				if (current.logfile)
					current.logfile =
					    strdup(current.logfile);
			}
		}
	}

	if (menu->viewfile) {		    /* check viewfile ACL */
		if (bbs_hook(HOOK_ACL_CHECK, menu->viewfile, 0)) {
			free(menu->viewfile);
			menu->viewfile = 0;
		}
	}

	if (menu->title) {		    /* check title ACL */
		if (bbs_hook(HOOK_ACL_CHECK, menu->title, 0)) {
			free(menu->title);
			menu->title = 0;
		}
	}

	free(current.title);
	free(current.logfile);
	free(dfl.logfile);
	free(menu_path);
	free(menu_dfl_path);
	return (menu);
}

/* EOF */
