#include "ftptool.h"

#ifdef USE_PROTOTYPES
int start_viewer(char *filename, int isremote)
#else
int start_viewer(filename, isremote)
char	*filename;
int		isremote;
#endif
{
	struct extension_info *type_ext;
	struct extension_info *type_magic;
	char	*viewer = default_viewer;
	char	*ext = "default";

	if (iscompressed(filename)) {
		/* uncompress file */
		if (isremote)
			footer_message("Uncompressing %s\n", filename, (char *)NULL);
		else
			local_footer_message("Uncompressing %s\n", filename, (char *)NULL);
		UNCOMPRESS(filename);
		return COMPRESSED;
	}

	type_ext = type_by_extension(filename);
	type_magic = type_by_magic(filename);
	if (type_ext != NULL && type_magic == NULL) {
		/* extension matched, go for it */
		viewer = type_ext->program;
		ext = type_ext->extension;
	} else if (type_ext == NULL && type_magic != NULL) {
		/* magic number matched */
		viewer = type_magic->program;
		ext = type_magic->extension;
	} else if (type_ext == NULL && type_ext == type_magic) {
		/* both NULL, try default */
		viewer = default_viewer;
		ext = "default";
	} else if (type_ext == type_magic) {
		/* they match, great! */
		viewer = type_ext->program;
		ext = type_ext->extension;
	} else {
		/* they both aren't null, and don't match! */
		/* choose magic number, it's probably more correct */
		viewer = type_magic->program;
		ext = type_magic->extension;
	}

	if (isremote) {
		footer_message("Starting %s viewer %s", 
			ext, viewer, (char *)NULL);
	} else {
		local_footer_message("Starting %s viewer %s", 
			ext, viewer, (char *)NULL);
	}
	fork_viewer(viewer, filename);
	return 0;
}

#ifdef USE_PROTOTYPES
void fork_viewer(char *program, char *filename)
#else
void fork_viewer(program, filename)
char	*program;
char	*filename;
#endif
{
	int	pid;
	char	**argv;
	int		argc;
	char	*arg;
	int		percentf=0;

	switch(pid = fork()) {
	case -1:   
		perror("fork");
		break; 
	case 0: /* child */
		/* count number of words */
		argc = 0;
		arg = program;
		while (*arg && isspace(*arg))
			arg++;
		while (*arg) {
			/* found an arg */
			argc++;
			/* skip arg */
			while (*arg && !isspace(*arg))	
				arg++;
			/* skip WS */
			while (*arg && isspace(*arg))
				arg++;
		}
		if (argc == 0) {
			fprintf(stderr, "Null viewer.\n");
			exit(1);
		}
		/* allocate argv[]  (extra entry in case no %f specified) */
		argv = (char **)malloc((argc+2) * sizeof(char *));
		if (argv == NULL) {
			fprintf(stderr, "No memory. Cannot start viewer '%s'.\n",
				program);
			exit(1);
		}
		/* can munge program here, since it's in the child */
		argc = 0;
		arg = strtok(program, " \t\n");
		while (arg) {
			if (!strcmp(arg, "%f")) {
				percentf++;
				argv[argc] = filename;
			} else {
				argv[argc] = arg;
			}
			argc++;
			arg = strtok((char *)NULL, " \t\n");
		}
		if (!percentf) {
			argv[argc] = filename;
			argc++;
		}
		argv[argc] = NULL;
		/* %f means program name */
		execvp(argv[0], argv);
		fprintf(stderr, "viewer '%s' failed:", program);
		perror("");
		exit(1);
	default:/* parent */
		notify_set_wait3_func(base_window.frame, notify_default_wait3, pid);                    
		break;                                  
	}
}

#ifdef USE_PROTOTYPES
int iscompressed(char *filename)
#else
int iscompressed(filename)
char	*filename;
#endif
{
	int		fd;
	char	buf[10];
	int		nread;
	char	*ext;

	if ((fd = open(filename, O_RDONLY)) == -1) {
		return 0;
	}
	notify_do_dispatch();
	nread = read(fd, buf, sizeof(buf));
	notify_no_dispatch();
	if (nread == -1) {
		close(fd);
		return 0;
	}
	close(fd);
	/* check for compressed file. '.Z' and magic value from /etc/magic */
	/*
	 0       string          \037\235        compressed data
	 */
	if (strlen(filename) > (unsigned int)2) {
		ext = filename + (strlen(filename) - 2);
		if (!strcmp(ext, ".Z") && (buf[0] == '\037') && (buf[1] == '\235')) {
				return 1;
		}
	}

	return 0;
}

#ifdef USE_PROTOTYPES
void compress_file(char *filename, char *options)
#else
void compress_file(filename, options)
char	*filename;
char	*options;
#endif
{
	static char *argv[] = {
		"compress",
		0, /* placeholder for -vf or -vdf */
		0,
		NULL,
	};

	argv[1] = options;
	argv[2] = filename;
	pipe_program(argv);

}

#ifdef USE_PROTOTYPES
void pipe_program(char *argv[])
#else
void pipe_program(argv)
char	*argv[];
#endif
{
	int	pid;
	int pipe_fds[2];
	char scratch[100];
	FILE	*fp;

	if (pipe(pipe_fds) == -1) {
		perror("ftptool:pipe");
		return;
	}

	switch(pid = fork()) {
	case -1:   
		perror("fork");
		break; 
	case 0: /* child */
		dup2(pipe_fds[1], 1);
		dup2(pipe_fds[1], 2);
		close(pipe_fds[0]);
		close(pipe_fds[1]);
		execvp(argv[0], argv);
		perror("can not start program");
		exit(1);
	default:/* parent */
		notify_set_wait3_func(base_window.frame, notify_default_wait3, pid);
		close(pipe_fds[1]);
		break;                                  
	}
	fp = fdopen(pipe_fds[0], "r");
	if (fp == NULL) {
		perror("fopen");
		close(pipe_fds[0]);
		return;
	}
	notify_do_dispatch();
	while (fgets(scratch, sizeof(scratch), fp) != NULL) {
		notify_no_dispatch();
		log_message(scratch);
		notify_do_dispatch();
	}
	notify_no_dispatch();
	fclose(fp);
}

#ifdef USE_PROTOTYPES
int view_local_file(char *name, int which, int *dirchanged)
#else
int view_local_file(name, which, dirchanged)
char	*name;
int		which;
int		*dirchanged;
#endif
{
	char *dot;
	struct stat buf;

	if (stat(name, &buf) == -1) {
		local_footer_message("%s: %s.", name, sys_errlist[errno]);
		return 1;
	}   
	if (!S_ISREG(buf.st_mode)) {
		local_footer_message("%s is not a regular file.", name);
		return 1;
	}
	if (start_viewer(name,which==DOREMOTEVIEW) == COMPRESSED) {
		dot = rindex(name, '.');
		if (dot) {
			*dot = '\0';
			if (which == DOLOCALVIEW && dirchanged != NULL)
				*dirchanged = 1;
			start_viewer(name, which == DOREMOTEVIEW);
		}
	}
	return 0;
}

char *newname;

#ifdef USE_PROTOTYPES
int view_remote_file(char *name, size_t size)
#else
int view_remote_file(name, size)
char	*name;
size_t	size;
#endif
{
	static char *tmpname;

	if (tmpname == NULL || (access(tmpname, F_OK) == -1 && errno == ENOENT)) {
		tmpname = tempnam("/var/tmp", "ftptl");
		if (tmpname == NULL || (mkdir(tmpname, 0777) == -1)) {
			footer_message("Can't create temporary directory.", (char *)NULL);
			return 1;
		}
	}
	sprintf(scratch, "%s/%s", tmpname, name);
	/* newname will be set if a unique local name is generated */
	newname = NULL;
	if (get_file(name, scratch, size)) {
		return 1;
	}

	if (newname)
		strcpy(scratch, newname);
	
	return view_local_file(scratch, DOREMOTEVIEW, (int *)NULL);
}

#ifdef USE_PROTOTYPES
struct extension_info *new_extension(char *extension, char *magic,
	char *program)
#else
struct extension_info *new_extension(extension, magic, program)
char	*extension;
char 	*magic;
char	*program;
#endif
{
	struct extension_info *tmp;

	tmp = (struct extension_info *)malloc(sizeof(*tmp));
	if (tmp == NULL)
		return NULL;

	tmp->next = NULL;
	tmp->extension = NULL;
	tmp->magic = NULL;
	tmp->program = NULL;

	tmp->extension = strdup(extension);
	if (tmp->extension == NULL)
		goto out;

	tmp->magic = strdup(magic);
	if (tmp->magic == NULL)
		goto out;

	tmp->program = strdup(program);
	if (tmp->program == NULL)
		goto out;

	return tmp;
out:
	return NULL;
}

#ifdef USE_PROTOTYPES
void free_extension(struct extension_info *cell)
#else
void free_extension(cell)
struct extension_info *cell;
#endif
{
	if (cell->extension)
		free(cell->extension);
	if (cell->magic)
		free(cell->magic);
	if (cell->program)
		free(cell->program);
	free(cell);
}

#ifdef USE_PROTOTYPES
struct extension_info *add_extension(struct extension_info *head,
	char *extension, char *magic, char *program)
#else
struct extension_info *add_extension(head, extension, magic, program)
struct extension_info *head;
char	*extension;
char 	*magic;
char	*program;
#endif
{
	struct extension_info *new;
	struct extension_info *tmp;
	int		rval;

	new = new_extension(extension, magic, program);
	if (new == NULL) {
		fprintf(stderr, "%s: no memory for new extension.\n",
			program_name);
		return NULL;
	}

	/* put in alphabetical order */
	for (tmp = head; tmp->next != NULL; tmp = tmp->next) {
		if (ignore_case)
			rval = strcasecmp(extension, tmp->next->extension);
		else
			rval = strcmp(extension, tmp->next->extension);
		if (rval < 0)
			break;
	}

	new->next = tmp->next;
	tmp->next = new;
	return head;
}

#ifdef USE_PROTOTYPES
int read_extensions(FILE *fp, char *extension, char *magic,
	char *program)
#else
int read_extensions(fp, extension, magic, program)
FILE	*fp;
char	*extension;
char	*magic;
char	*program;
#endif
{
	static char linebuf[2 * MAXPATHLEN + 1];

	/*
	 Extension:
	 Magic:
	 Program:
	 --
	 */
	if (fgets(linebuf, sizeof(linebuf), fp) == NULL) {
		if (feof(fp))
			return 0;
		else
			goto out;
	}
	*extension = '\0';
	sscanf(linebuf, "Extension:%[^\n]", extension);

	if (fgets(linebuf, sizeof(linebuf), fp) == NULL)
		goto out;
	*magic = '\0';
	sscanf(linebuf, "Magic:%[^\n]", magic);

	if (fgets(linebuf, sizeof(linebuf), fp) == NULL)
		goto out;
	*program = '\0';
	sscanf(linebuf, "Program:%[^\n]", program);

	/* Dump separator */
	if (fgets(linebuf, sizeof(linebuf), fp) == NULL)
		goto out;

	return 1;

out:
	fprintf(stderr, "%s: Bad entry for extension in %s.\n",
		program_name, FTPTOOL_TYPES);
	return 0;
}

#ifdef USE_PROTOTYPES
void load_extensions(void)
#else
void load_extensions()
#endif
{
	char	*ftptool_extensions;
	char	*archive_viewer;
	char	*postscript_viewer;
	FILE	*fp;
	static char extension[MAXPATHLEN + 1];
	static char magic[MAXPATHLEN + 1];
	static char program[MAXPATHLEN + 1];

	extension_list = new_extension("","","");
	if (extension_list == NULL) {
		fprintf(stderr, "%s: Out of memory for extension list.\n",
			program_name);
		return;
	}

	ftptool_extensions = find_dotfile(FTPTOOL_TYPES);
	if (ftptool_extensions == NULL) {
		/* not there, use resources/create initial one */

		archive_viewer = defaults_get_string(
			"ftptool.ArchiveViewer", "Ftptool.ArchiveViewer", program_name);
		add_extension(extension_list, ".tar", "", archive_viewer);

		postscript_viewer = defaults_get_string(
			"ftptool.PostScriptViewer", "Ftptool.PostScriptViewer", 
			"pageview %f");
		add_extension(extension_list, ".ps", "%!", postscript_viewer);

		/* other stuff */

		add_extension(extension_list, ".gif", "GIF", "xv %f");

		add_extension(extension_list, ".jpg", 
			"\\377\\330\\377\\340\\000\\020\\112\\106\\111", "xv %f");

		add_extension(extension_list, ".ras", "\\131\\246\\152\\225", "xv %f");

		extensions_changed = 1;

	} else {
		/* load list */
		fp = fopen(ftptool_extensions, "r");
		if (fp == NULL) {
			fprintf(stderr, "%s: Could not open %s for reading (%s).\n",
				program_name, ftptool_extensions, sys_errlist[errno]);
			free(ftptool_extensions);
			return;
		}
		while (read_extensions(fp, extension, magic, program)) {
			if (add_extension(extension_list, extension, magic, program)
				== NULL)
				break;
		}
		fclose(fp);
		free(ftptool_extensions);
	}

	clear_extension_list(tool_property_window.viewers.list);
	load_extension_list(extension_list, tool_property_window.viewers.list);
}

#ifdef USE_PROTOTYPES
void save_extensions(void)
#else
void save_extensions()
#endif
{
	char	*filename = NULL;
	struct extension_info *tmp;
	FILE	*fp;
	extern char *sys_errlist[];

	/* create will also truncate, which we want to do here */
	filename = create_dotfile(FTPTOOL_TYPES, 0644);
	if (filename == NULL) {
		fprintf(stderr, "%s: Could not create ftptooltypes file.\n",
			program_name);
		return;
	}

	fp = fopen(filename, "w");
	if (fp == NULL) {
		fprintf(stderr, "%s: Could not open %s for writing (%s).\n",
			program_name, filename, sys_errlist[errno]);
		free(filename);
		return;
	}
	for (tmp=extension_list->next; tmp != NULL; tmp=tmp->next) {
		fprintf(fp, "Extension:%s\n", tmp->extension);
		fprintf(fp, "Magic:%s\n", tmp->magic);
		fprintf(fp, "Program:%s\n", tmp->program);
		fprintf(fp, "--\n");
	}
	fclose(fp);
	
	extensions_changed = 0;

	free(filename);
}

#ifdef USE_PROTOTYPES
void load_extension_list(struct extension_info *extension_list, 
	Panel panel_list)
#else
void load_extension_list(extension_list, panel_list)
struct extension_info *extension_list;
Panel	panel_list;
#endif
{
	struct extension_info *tmp;
	int		row;

	row = 0;
	for (tmp=extension_list->next; tmp != NULL; tmp = tmp->next, row++) {
		xv_set(panel_list,
			PANEL_LIST_INSERT, row,
			PANEL_LIST_STRING, row, tmp->extension,
			PANEL_LIST_CLIENT_DATA, row, tmp,
			PANEL_PAINT, PANEL_NONE,
			NULL);
	}
	xv_set(panel_list,
		XV_SHOW, TRUE,
		NULL);
}

#ifdef USE_PROTOTYPES
void clear_extension_list(Panel panel_list)
#else
void clear_extension_list(panel_list)
Panel panel_list;
#endif
{
	int	nitems;
	int	row;

	nitems = xv_get(panel_list, PANEL_LIST_NROWS);

	for (row = nitems - 1; row >= 0; row--) {
		xv_set(panel_list,
			PANEL_LIST_DELETE, row,
			PANEL_PAINT, PANEL_NONE,
			NULL);
	}
	panel_paint(panel_list, PANEL_CLEAR);
}

#ifdef USE_PROTOTYPES
int extension_list_proc(Panel_item item, char *string, Xv_opaque client_data,
	Panel_list_op op, Event *event)
#else
int extension_list_proc(item,string,client_data,op,event)
Panel_item      item;
char            *string;
Xv_opaque       client_data;
Panel_list_op   op;
Event           *event;
#endif
{
	struct extension_info *tmp = (struct extension_info *)client_data;

	switch(op) {
	case PANEL_LIST_OP_SELECT:
		xv_set(tool_property_window.viewers.extension,
			PANEL_VALUE, tmp->extension,
			NULL);
		caret_to_first(tool_property_window.viewers.extension);
		xv_set(tool_property_window.viewers.magic,
			PANEL_VALUE, tmp->magic,
			NULL);
		caret_to_first(tool_property_window.viewers.magic);
		xv_set(tool_property_window.viewers.program,
			PANEL_VALUE, tmp->program,
			NULL);
		caret_to_first(tool_property_window.viewers.program);
	default:
		return (XV_OK);
	}
}

#ifdef USE_PROTOTYPES
struct extension_info *type_by_extension(char *filename)
#else
struct extension_info *type_by_extension(filename)
char	*filename;
#endif
{
	struct extension_info *tmp;
	unsigned int	len;
	char	*ext;

	for (tmp=extension_list->next; tmp != NULL; tmp = tmp->next) {
		len = strlen(tmp->extension);
		if (strlen(filename) < len)
			continue;
		ext = filename + (strlen(filename) - len);
		if (!strcmp(ext, tmp->extension))
			break;
	}
	return tmp;

}

#ifdef USE_PROTOTYPES
struct extension_info *type_by_magic(char *filename)
#else
struct extension_info *type_by_magic(filename)
char	*filename;
#endif
{
	struct extension_info *tmp;
	int		fd;
	char	buf[MAX_MAGIC_SIZE];
	char	*chp;
	char	ch;
	int		nread;
	int		count;

	if ((fd = open(filename, O_RDONLY)) == -1) {
		return NULL;
	}
	notify_do_dispatch();
	nread = read(fd, buf, sizeof(buf));
	notify_no_dispatch();
	close(fd);
	if (nread == -1) {
		return NULL;
	}

	for (tmp=extension_list->next; tmp != NULL; tmp=tmp->next) {
		count = 0;
		chp = tmp->magic;
		ch = *chp;
		if (ch == '\0') /* null magic number */
			continue;
		/* test and advance */
		while (count < nread && ch != '\0') {
			if (ch == '\\') {
				int	n = 3;
				int	val = 0;

				/* octal escape sequence */
				chp++;
				while (isdigit(*chp) && n > 0) {
					val *= 8;
					val += *chp - '0';
					chp++;
					n--;
				}
				/* point back one for later */
				chp--;
				ch = (char)val;
			}
			if (buf[count] == ch) {
				/* match */
				count++;
				chp++;
				ch = *chp;
			} else
				break;
		} 
		if (ch == '\0') {
			break;
		}
	}
	return tmp;
}

#ifdef USE_PROTOTYPES
void delete_extension_proc(Panel_item item, Event *event)
#else
void delete_extension_proc(item, event)
Panel_item	item;
Event 		*event;
#endif
{
	char	*extension;

	xv_set(tool_property_window.frame,
		FRAME_LEFT_FOOTER, "",
		NULL);

	extension = (char *)xv_get(tool_property_window.viewers.extension, 
		PANEL_VALUE);
	if (*extension == '\0') {
		xv_set(tool_property_window.frame,
			FRAME_SHOW_FOOTER, TRUE,
			FRAME_LEFT_FOOTER, "Please specify an extension.",
			NULL);
		return;
	}

	delete_extension(extension_list, extension);

	clear_extension_list(tool_property_window.viewers.list);
	load_extension_list(extension_list, tool_property_window.viewers.list);

	xv_set(item,
		PANEL_NOTIFY_STATUS, XV_ERROR,
		NULL);
}

#ifdef USE_PROTOTYPES
void add_extension_proc(Panel_item item, Event *event)
#else
void add_extension_proc(item, event)
Panel_item	item;
Event 		*event;
#endif
{
	enter_extension_info(1);
	xv_set(item,
		PANEL_NOTIFY_STATUS, XV_ERROR,
		NULL);
}

#ifdef USE_PROTOTYPES
void change_extension_proc(Panel_item item, Event *event)
#else
void change_extension_proc(item, event)
Panel_item	item;
Event 		*event;
#endif
{
	enter_extension_info(0);
	xv_set(item,
		PANEL_NOTIFY_STATUS, XV_ERROR,
		NULL);
}

#ifdef USE_PROTOTYPES
void enter_extension_info(int warnchange)
#else
void enter_extension_info(warnchange)
int	warnchange;
#endif
{
	int	answer;
	char *extension, *magic, *program;
#ifdef XVIEW3
	Xv_notice	notice;
#endif

	xv_set(tool_property_window.frame,
		FRAME_LEFT_FOOTER, "",
		NULL);
	extension = (char *)xv_get(tool_property_window.viewers.extension, 
		PANEL_VALUE);
	if (*extension == '\0') {
		xv_set(tool_property_window.frame,
			FRAME_SHOW_FOOTER, TRUE,
			FRAME_LEFT_FOOTER, "Please specify an extension.",
			NULL);
		return;
	}
	magic = (char *)xv_get(tool_property_window.viewers.magic, PANEL_VALUE);
	program = (char *)xv_get(tool_property_window.viewers.program, PANEL_VALUE);
	if (*program == '\0') {
		xv_set(tool_property_window.frame,
			FRAME_SHOW_FOOTER, TRUE,
			FRAME_LEFT_FOOTER, "Please specify a program to run.",
			NULL);
		return;
	}

    if (extension_exists(extension_list, extension)) {
        if (warnchange) {
#ifdef XVIEW3
            notice = xv_create(tool_property_window.viewers.panel, NOTICE,
                NOTICE_MESSAGE_STRINGS,
                    "That extension exists. Do you really want to change it?",
                    NULL,
                NOTICE_BUTTON_YES, "Yes",
                NOTICE_BUTTON_NO, "No",
                NOTICE_STATUS, &answer,
                XV_SHOW, TRUE,
                NULL);
            xv_destroy_safe(notice);
#else
            answer = notice_prompt(tool_property_window.viewers.panel, NULL,
                NOTICE_MESSAGE_STRINGS,
                    "That extension exists. Do you really want to change it?",
                    NULL,
                NOTICE_BUTTON_YES, "Yes",
                NOTICE_BUTTON_NO, "No",
                NULL);
#endif
            if (answer != NOTICE_YES)
                return;
        }
        delete_extension(extension_list, extension);
    }
	extensions_changed = 1;
	add_extension(extension_list, extension, magic, program);
	clear_extension_list(tool_property_window.viewers.list);
	load_extension_list(extension_list, tool_property_window.viewers.list);
}

#ifdef USE_PROTOTYPES
void delete_extension(struct extension_info *head, char *extension)
#else
void delete_extension(head, extension) 
struct extension_info *head;
char	*extension;
#endif
{
	struct extension_info *tmp, *deleted;

	for (tmp = head; tmp->next != NULL; tmp = tmp->next)
		if (!strcmp(extension, tmp->next->extension)) {
			deleted = tmp->next;
			tmp->next = deleted->next;
			deleted->next = NULL;
			free_extension(deleted);
			extensions_changed = 1;
			return;
		}
}

#ifdef USE_PROTOTYPES
int extension_exists(struct extension_info *head, char *extension)
#else
int extension_exists(head, extension)
struct extension_info *head;
char	*extension;
#endif
{
	struct extension_info *tmp;

	for (tmp = head->next; tmp != NULL; tmp = tmp->next)
		if (!strcmp(extension, tmp->extension))
			return 1;
	return 0;
}
