/*************************************************************************
 * File: file_act.c
 * Author: Jan Newmarch
 * Last modified: $Date: 1992/11/17 00:35:42 $
 * Version: $Revision: 1.6 $
 * Purpose: read in the set of actions for files, directories 
 *          and executables. See the user manual for the syntax used.
 *          The file for these actions is $HOME/.xmfmrc
 * Revision history:
 *	4 Aug 92	added filter actions as preamble
 *	7 Aug 92	looks in XAPPRESDIR for xmfmrc
 *	11 Aug 92	added description field to xmfmrc
 *	   Oct		input file format revised
 *       3 Nov 92       lint-ed
 *	15 Nov 92	used XtResolvePathname to find app-defaults
 *	20 Nov 92	changed char ch to int ch
 *	21 Nov 92	better handling of incomplete files
 *	21 Nov 92	can get xmfmrc from resource file
 ************************************************************************/ 

#include "copyright.h"

/*************************************************************************
 * System includes
 ************************************************************************/ 
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <Xm/Xm.h>
#include <X11/Intrinsic.h>

/*************************************************************************
 * Local includes
 ************************************************************************/ 
#include "RegExp.h"
#include "types.h"

/*************************************************************************
 * Functions exported
 ************************************************************************/ 
extern file_action * LoadFileActions (
#ifdef UseFunctionPrototypes
	dir_pane_info *dpi
#endif
);

/*************************************************************************
 * Variables exported
 ************************************************************************/ 

/*************************************************************************
 * Extern variables
 ************************************************************************/ 
Widget app_shell;

/*************************************************************************
 * Extern functions
 ************************************************************************/ 
extern int fatal (
#ifdef UseFunctionPrototypes
	char *s
#endif
);

/*************************************************************************
 * Forward functions
 ************************************************************************/ 

/*************************************************************************
 * Local variables
 ************************************************************************/ 
#define BUF_SIZE 2048

static char buf[BUF_SIZE];

static int line_number = 1;

#define NOT_NULL(s)	((s) == NULL ? \
				syntax_error ("unexpected end of file") : True)

#define IS_TYPE(s)	(strcmp ((s), "type") == 0)
#define IS_PATTERN(s)	(strcmp ((s), "pattern") == 0)
#define IS_PIXMAP(s)	(strcmp ((s), "pixmap") == 0)
#define IS_DESCRIPTION(s)	(strcmp ((s), "description") == 0)
#define IS_LABEL(s)	(strcmp ((s), "label") == 0)
#ifdef vax11c
#define IS_REFRESH_DIR(s)	(strcmp ((s), "refresh_dir") == 0)
#endif /* vax11c */
#define IS_RUN_IN_XTERM(s)	(strcmp ((s), "run_in_xterm") == 0)
#define IS_PAUSE_AFTER_EXEC(s)	(strcmp ((s), "pause_after_exec") == 0)
#define IS_ACTION(s)	(strcmp ((s), "action") == 0)
#define IS_PROMPT(s)	(strcmp ((s), "prompt") == 0)
#define IS_L_BRACE(s)	(strcmp ((s), "{") == 0)
#define IS_R_BRACE(s)	(strcmp ((s), "}") == 0)
#define IS_FILE_FILTER(s)	(strcmp ((s), "file_filter") == 0)
#define IS_DIR_FILTER(s)	(strcmp ((s), "dir_filter") == 0)
#define IS_EXECUTABLE_FILTER(s)	(strcmp ((s), "executable_filter") == 0)
#define IS_ALL_FILTER(s)	(strcmp ((s), "all_filter") == 0)

#define IS_WHITE_SPACE(ch)	((ch) == ' ' || \
				 (ch) == '\t' || (ch) == '\n')

#ifdef vax11c
#define DEFAULT_FILE_FILTER        "[^.]*.[^.]*"
#define DEFAULT_DIR_FILTER         "[^.]*.DIR;*"
#define DEFAULT_EXECUTABLE_FILTER  "[^.]*.EXE;*"
#else
#define DEFAULT_FILE_FILTER        "[^.]*"
#define DEFAULT_DIR_FILTER         "*"
#define DEFAULT_EXECUTABLE_FILTER  "*"
#endif /* vax11c */

/* stuff needed to extract configFile from the resource database */
typedef struct
	{String configFile;}
MyResourceType, *MyResourceTypePtr;

static	XtResource resources [] =
{	
	{	"configFile",	/* instance name */
		"ConfigFile",	/* class name */
		XmRString,	/* type */
		sizeof(String),	/* type size */
		XtOffset (MyResourceTypePtr, configFile),  /* where it is */
		XmRImmediate,	/* default follows */
		NULL		/* default value */
	}
};

static Bool token_put_back = False;

/*************************************************************************
 * Function: syntax_error ()
 * Purpose: complain about input errors
 * In parameters: err_msg
 * Function returns: False
 * Side effects: 
 * Precondition: 
 * Postcondition: false 
 ************************************************************************/ 
static Bool
syntax_error 
#ifdef UseFunctionPrototypes
	(char *err_msg)
#else
	(err_msg)
	char *err_msg;

#endif
{
	fprintf (stderr, "syntax error in xmfmrc near line %d\n\t%s\n",
			 line_number, err_msg);
	exit (1);
	/* NOTREACHED */
	return False;
}

/*************************************************************************
 * Function: GetC ()
 * Purpose: interface to getc () that increments line count
 * In parameters: fp
 * Function returns: char read in
 * Side effects: increments line_number on '\n'
 * Precondition: 
 * Postcondition: 
 ************************************************************************/ 
static int
GetC 
#ifdef UseFunctionPrototypes
	(FILE *fp)
#else
	(fp)
	FILE *fp;

#endif
{	int ch;

	ch = getc (fp);
	if (ch == '\n')
		line_number++;
	return ch;
}

/*************************************************************************
 * Function: unget_next_token ()
 * Purpose: push token back onto input queue (actually, just leaves it
 *	there, and sets a flag to reuse it. Only allows one token push back
 * In parameters: fp
 * Function returns:
 * Side effects: sets token_put_back
 * Precondition: 
 * Postcondition: 
 ************************************************************************/ 
static void
unget_next_token 
#ifdef UseFunctionPrototypes
	(void)
#else
	()

#endif
{
	token_put_back = True;
}

/*************************************************************************
 * Function: next_token ()
 * Purpose: read the next token from fp. token is all non-white, or in ""
 * In parameters: fp
 * Function returns: pointer to token or NULL on EOF
 * Side effects: 
 * Precondition: 
 * Postcondition: 
 ************************************************************************/ 
static char *
next_token 
#ifdef UseFunctionPrototypes
	(FILE *fp)
#else
	(fp)
	FILE *fp;

#endif
{	int ch;
	char *p = buf;

	if (token_put_back)
	{	token_put_back = False;
		return buf;
	}

	while ((ch = GetC (fp)) != EOF &&
		 IS_WHITE_SPACE (ch))
		/* empty */;
	if (ch == EOF)
		return NULL;
	if (ch == '"')
	 	while ((ch = GetC (fp)) != EOF && ch != '"')
			*p++ = ch;
	else
	{	*p++ = ch;
		while ((ch = GetC (fp)) != EOF &&
			! IS_WHITE_SPACE (ch))
			*p++ = ch;
	}
	*p = '\0';
#ifdef DEBUG
	fprintf (stderr, "token: %s\n", buf);
#endif
	return buf;
}

/*************************************************************************
 * Function: ParseFileAction ()
 * Purpose: parse a line into its actions
 * In parameters: 
 * Function returns: a list of actions for a file pattern
 * Side effects: 
 * Precondition: 
 * Postcondition: 
 ************************************************************************/ 
static file_action *
ParseFileAction 
#ifdef UseFunctionPrototypes
	(FILE *fp)
#else
	(fp)
	FILE *fp;

#endif
{
	file_action *pfa;
	action_pair *pap;
	char regexp[FSM_LENGTH];
	char *token, *token2;

	pfa = (file_action *) XtMalloc (sizeof (file_action));
	pfa -> actions = NULL;

	/* upto 4 fields: type, pattern, pixmap, description */
	/* some defaults in case no entry */
	pfa -> file_type = '\0';
	pfa -> name_pattern = " ";  /* make non-null to force Label size */
#ifdef vax11c
	pfa -> pixmap = "xmfm_pixmaps:xlogo32.xbm";  /* reasonable default */
#else
	pfa -> pixmap = "xlogo32";  /* reasonable default */
#endif /* vax11c */
	pfa -> description = "";

	while ( NOT_NULL (token = next_token (fp)) && ! IS_L_BRACE(token))
	{	
		if (IS_TYPE (token))
		{	token2 = next_token (fp);
			NOT_NULL (token2);
			pfa -> file_type = *token2;
			continue;
		}
		if (IS_PATTERN (token))
		{	token2 = next_token (fp);
			NOT_NULL (token2);
		 	pfa -> name_pattern =
				(char *) XtMalloc (strlen (token2) + 1);
			strcpy (pfa -> name_pattern, token2);
#ifdef vax11c
			/*
			 * Uppercase the pattern
			 */
			{
				register char *cp = pfa -> name_pattern;

				while(*cp++) if (islower(*cp)) *cp = _toupper(*cp);
			}
#endif /* vax11c */
			continue;
		}
		if (IS_PIXMAP (token))
		{	token2 = next_token (fp);
			NOT_NULL (token2);
			pfa -> pixmap = (char *) XtMalloc (strlen (token2) + 1);
			strcpy (pfa -> pixmap, token2);
			continue;
		}
		if (IS_DESCRIPTION (token))
		{	token2 = next_token (fp);
			NOT_NULL (token2);
			pfa -> description = (char *) XtMalloc (strlen (token2) + 1);
			strcpy (pfa -> description, token2);
			continue;
		}
		syntax_error ("illegal token in file description");
	}
	/* this will have swallowed up the left brace for the first file
	   action. put it back for later use */
	unget_next_token ();

	/* each action is { ... } delimited */
	while (NOT_NULL (token = next_token (fp)) && IS_L_BRACE (token))
	/* stops on closing R_BRACE */
	{	
		/* add action to linked list */
		if (pfa -> actions == NULL)
			pfa -> actions = 
			pap = (action_pair *) XtMalloc (sizeof (action_pair));
		else
		{ 	pap -> next = (action_pair *) XtMalloc (sizeof (action_pair));
			pap = pap -> next;
		}
		/* set default values */
		pap -> run_in_xterm = False;
		pap -> pause_after_exec = False;
		pap -> has_prompt = False;
#ifdef vax11c
		pap -> refresh_dir = False;
#endif /* vax11c */

		/* now the fields for the action */
		while ( (token = next_token (fp)) &&
				!IS_R_BRACE (token))
		{	if (IS_LABEL (token))
			{	token2 = next_token (fp);
				NOT_NULL (token2);
				pap -> action_label = 
					(char *) XtMalloc (strlen (token2) + 1);
				strcpy (pap -> action_label, token2);
				continue;
			}
#ifdef vax11c
			if (IS_REFRESH_DIR (token))
			{
				pap -> refresh_dir = True;
				continue;
			}
#endif /* vax11c */
			if (IS_RUN_IN_XTERM (token))
			{ 	pap -> run_in_xterm = True;
				continue;
			}
			if (IS_PAUSE_AFTER_EXEC (token))
			{ 	pap -> pause_after_exec = True;
				continue;
			}
			if (IS_ACTION (token))
			{	token2 = next_token (fp);
				NOT_NULL (token2);
			 	pap -> action =
					(char *) XtMalloc (strlen (token2) + 1);
				strcpy (pap -> action, token2);
				continue;
			}
			if (IS_PROMPT (token))
			{	token2 = next_token (fp);
				NOT_NULL (token2);
			 	pap -> prompt =
					(char *) XtMalloc (strlen (token2) + 1);
				strcpy (pap -> prompt, token2);
				pap -> has_prompt = True;
				continue;
			}
			syntax_error ("illegal token in file action");
		}
	}
	pap -> next = NULL;

	/* set the regexp filter pattern */
	RegExpPatternToRegExp (pfa -> name_pattern, regexp);
#ifdef vax11c
	pfa->fsm_ptr = RegExpCompile(regexp, (pfa -> fsm_ptr), FSM_LENGTH);
#else
	RegExpCompile (regexp, (pfa -> fsm_ptr), FSM_LENGTH);
#endif /* vax11c */

	return pfa;
}


/*************************************************************************
 * Function: DumpActions ()
 * Purpose: debugging tool to show acctions set to screen
 * In parameters: actions
 * Out parameters:
 * Precondition: valid list of actions
 * Postcondition: actions displayed to stdout
 ************************************************************************/ 
void
DumpActions 
#ifdef UseFunctionPrototypes
	(file_action *actions)
#else
	(actions)
	file_action *actions;

#endif
{	action_pair *ap;

	while (actions != NULL)
	{
		fprintf (stderr, "%c %s %s\n",
					actions -> file_type,
					actions -> name_pattern,
					actions -> pixmap);
		ap = actions -> actions;
		while (ap != NULL)
		{
			fprintf (stderr, "  %s %s\n",
					ap -> action_label,
					ap -> action);
			ap = ap -> next;
		}
		actions = actions -> next;
	}
}


/*************************************************************************
 * Function: OpenXmfmrc ()
 * Purpose: open the xmfmrc file from configFile HOME or app-defaults
 * In parameters:
 * Function returns: file pointer to xmfmrc file
 * Precondition: configFile or $HOME/.xmfmrc or app-defaults/xmfmrc exist
 * 	Under VMS this file is known as xmfmrc.dat and is located in
 *	DECW$USER_DEFAULTS:
 * Postcondition: file is parsed and files + actions lists built
 ************************************************************************/ 
FILE *
OpenXmfmrc 
#ifdef UseFunctionPrototypes
	(Display *display)
#else
	(display)
	Display *display;

#endif
{	FILE *fp;
	char *path;
	MyResourceType myresources;

	char *home_dir;
	char xmfmrc[MAXPATHLEN];

	/* look for the file first in the resource database */
	XtGetApplicationResources (app_shell,
			&myresources,
			resources,
			XtNumber (resources),
			NULL,
			0);
	if (myresources.configFile != NULL)
	{	if ((fp = fopen (myresources.configFile, "r")) != NULL)
			return fp;
		/* complain if not found */
		else
			fprintf (stderr, "can't open configuration file %s\n",
				myresources.configFile);
	}

#ifdef vax11c
	/* try DECW$USER_DEFAULTS:XMFMRC.DAT */
	strcpy (xmfmrc, "DECW$USER_DEFAULTS:");
	strcat (xmfmrc, "xmfmrc.dat");
	if ((fp = fopen (xmfmrc, "r")) != NULL)
		return fp;
#else
	/* try $HOME/.xmfmrc */
	if ((home_dir = getenv ("HOME")) != NULL)
	{
		strcpy (xmfmrc, home_dir);
		strcat (xmfmrc, "/.xmfmrc");
		if ((fp = fopen (xmfmrc, "r")) != NULL)
			return fp;
	}
#endif /* vax11c */

#ifdef vax11c
	strcpy(xmfmrc, "DECW$SYSTEM_DEFAULTS:");
	strcpy(xmfmrc, "xmfmrc.config");
	if ((fp = fopen(xmfmrc, "r")) != NULL)
		return fp;
#else
	/* is XAPPLRESDIR set? (expect it to end in /) */
	if ((path = getenv ("XAPPLRESDIR")) != NULL)
	{	
		strcpy (xmfmrc, path);
		strcat (xmfmrc, "xmfmrc");
		if ((fp = fopen (xmfmrc, "r")) != NULL)
			return fp;
	}
	if ((fp = fopen (XtResolvePathname (display,
				"app-defaults", "xmfmrc",
				NULL, NULL, NULL, 0, NULL),
			 "r"))
			!= NULL)
		return fp;

#endif /* vax11c */
	/* cant find anything useful anywhere so give up*/
	fatal ("Can't find xmfmrc file");
        /* keep lint happy */
        return NULL;
}

/*************************************************************************
 * Function: LoadFileActions ()
 * Purpose: to read in the set of actions from $HOME/.xmfmrc
 * In parameters:
 * Function returns: a list of file patterns with associated actions
 * Precondition: $HOME/.xmfmrc exists and contains a valid set of action
 *               strings
 * Postcondition: file is parsed and files + actions lists built
 ************************************************************************/ 
file_action *
LoadFileActions 
#ifdef UseFunctionPrototypes
	(dir_pane_info *dpi)
#else
	(dpi)
	dir_pane_info *dpi;

#endif
{	FILE *fp;
	file_action	*actions = NULL,
			*end_actions = NULL;
	char *token;
	char regexp[FSM_LENGTH];

	fp = OpenXmfmrc (XtDisplay (dpi -> toplevel));

	/* load the preamble - upto a first opening brace. set defaults:*/
        dpi -> file_filter = DEFAULT_FILE_FILTER;
	dpi -> dir_filter = DEFAULT_DIR_FILTER;
	dpi -> executable_filter = DEFAULT_EXECUTABLE_FILTER;

	while ( NOT_NULL (token = next_token (fp)) && ! IS_L_BRACE(token))
	{	if (IS_FILE_FILTER (token))
		{	token = next_token (fp);
			dpi -> file_filter = XtMalloc (strlen (token) + 1);
			strcpy (dpi -> file_filter, token);
			continue;
		}
		if (IS_DIR_FILTER (token))
		{	token = next_token (fp);
			dpi -> dir_filter = XtMalloc (strlen (token) + 1);
			strcpy (dpi -> dir_filter, token);
			continue;
		}
		if (IS_EXECUTABLE_FILTER (token))
		{	token = next_token (fp);
			dpi -> executable_filter = XtMalloc (strlen (token) + 1);
			strcpy (dpi -> executable_filter, token);
			continue;
		}
		syntax_error ("illegal token in preamble");
	}

	/* this will have swallowed up the left brace for the first file
	   action. put it back for later use */
	unget_next_token ();

	/* now compile to reg exps */
	RegExpPatternToRegExp (dpi -> file_filter, regexp);
#ifdef vax11c
	dpi->file_filter_regexp = 
		RegExpCompile (regexp, dpi -> file_filter_regexp, FSM_LENGTH);
#else
	RegExpCompile (regexp, dpi -> file_filter_regexp, FSM_LENGTH);
#endif /* vax11c */

	RegExpPatternToRegExp (dpi -> dir_filter, regexp);
#ifdef vax11c
	dpi->dir_filter_regexp = 
		RegExpCompile (regexp, dpi -> dir_filter_regexp, FSM_LENGTH);
#else
	RegExpCompile (regexp, dpi -> dir_filter_regexp, FSM_LENGTH);
#endif /* vax11c */

	RegExpPatternToRegExp (dpi -> executable_filter, regexp);
#ifdef vax11c
	dpi->executable_filter_regexp = 
		RegExpCompile (regexp, dpi -> executable_filter_regexp, FSM_LENGTH);
#else
	RegExpCompile (regexp, dpi -> executable_filter_regexp, FSM_LENGTH);
#endif /* vax11c */

	/* and the set of file types and their actions */
	while (next_token(fp) != NULL)	/* actually, is L_BRACE */
	{
		if (end_actions == NULL)
		{	end_actions = ParseFileAction (fp);
			actions = end_actions;
		}  else
		{	end_actions->next = ParseFileAction (fp);
			end_actions = end_actions->next;
		}
	}
	end_actions->next = NULL;

#ifdef DEBUG
	DumpActions (actions);
#endif

	return actions;
}
