/****************************************************************************
 *
 * Copyright (c) 1999 Sasha Vasko <sasha at aftercode.net>
 *
 * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 ****************************************************************************/

#define LOCAL_DEBUG
#include "../configure.h"

#include <stdarg.h>
#include <string.h>
#include <signal.h>
#include <fcntl.h>
#include <unistd.h>

#include "asapp.h"
#include "afterstep.h"
#include "parser.h"
#include "screen.h"
#include "functions.h"
#include "session.h"
#include "balloon.h"
#include "mystyle.h"
#include "mylook.h"
#include "wmprops.h"
#include "desktop_category.h"
#include "../libAfterImage/asimage.h"
#include "../libAfterImage/xpm.h"
#include "../libAfterImage/char2uni.h"


ASProgArgs as_app_args = { 0, NULL, 0, NULL, NULL, NULL, 0, 0 };	/* some typical progy cmd line options - set by SetMyArgs( argc, argv ) */

ASProgArgs *MyArgsPtr = &as_app_args;
char *MyName = NULL;						/* name are we known by */
char MyClass[MAX_MY_CLASS + 1] = "unknown";	/* application Class name ( Pager, Wharf, etc. ) - set by SetMyClass(char *) */
void (*MyVersionFunc) (void) = NULL;
void (*MyUsageFunc) (void) = NULL;

char *as_afterstep_dir_name = AFTER_DIR;
char *as_save_dir_name = AFTER_DIR "/" AFTER_SAVE;
char *as_start_dir_name = AFTER_DIR "/" START_DIR;
char *as_share_dir_name = AFTER_SHAREDIR;

char *as_background_dir_name = BACK_DIR;
char *as_look_dir_name = LOOK_DIR;
char *as_theme_dir_name = THEME_DIR;
char *as_theme_file_dir_name = THEME_FILE_DIR;
char *as_feel_dir_name = FEEL_DIR;
char *as_colorscheme_dir_name = COLORSCHEME_DIR;
char *as_font_dir_name = FONT_DIR;
char *as_icon_dir_name = ICON_DIR;
char *as_tile_dir_name = TILE_DIR;


int fd_width;

unsigned int nonlock_mods = 0;	/* a mask for non-locking modifiers */
unsigned int lock_mods[MAX_LOCK_MODS] = { 0 };	/* all combinations of lock modifier masks */

/* Now for each display we may have one or several screens ; */
Display *dpy = NULL;
ScreenInfo *ASDefaultScr;				/* ScreenInfo for the default screen */

//#define Scr   (*DefaultScr);
int x_fd = 0;										/* descriptor of the X Windows connection  */


int SingleScreen = -1;					/* if >= 0 then [points to the only ScreenInfo structure available */
int PointerScreen = 0;					/* screen that currently has pointer */
unsigned int NumberOfScreens = 0;	/* number of screens on display */

/* unused - future development : */
ScreenInfo **all_screens = NULL;	/* all ScreenInfo structures for NumberOfScreens screens */
ASHashTable *screens_window_hash = NULL;	/* so we can easily track what window is on what screen */

/* end of: unused - future development : */


struct ASFeel *DefaultFeel = NULL;	/* unused - future development : */
struct MyLook *DefaultLook = NULL;	/* unused - future development : */

void (*CloseOnExec) () = NULL;

struct ASSession *Session = NULL;	/* filenames of look, feel and background */
struct ASEnvironment *Environment = NULL;

struct ASDatabase *Database = NULL;

struct ASCategoryTree *StandardCategories = NULL;
struct ASCategoryTree *AfterStepCategories = NULL;
struct ASCategoryTree *KDECategories = NULL;
struct ASCategoryTree *GNOMECategories = NULL;
struct ASCategoryTree *OtherCategories = NULL;
struct ASCategoryTree *CombinedCategories = NULL;



/* names of AS functions - used all over the place  :*/

#define FUNC_TERM(keyword,func)         {TF_NO_MYNAME_PREPENDING|TF_NAMED,keyword,sizeof(keyword)-1,TT_FUNCTION,func,NULL}
#define FUNC_TERM2(flags,keyword,func)  {TF_NO_MYNAME_PREPENDING|TF_NAMED|(flags),keyword,sizeof(keyword)-1,TT_FUNCTION,func,NULL}

TermDef FuncTerms[F_FUNCTIONS_NUM + 1] = {
	FUNC_TERM2 (NEED_NAME, "Nop", F_NOP),	/* Nop      "name"|"" */
	FUNC_TERM2 (NEED_NAME, "Title", F_TITLE),	/* Title    "name"    */
	FUNC_TERM ("Beep", F_BEEP),		/* Beep               */
	FUNC_TERM ("Quit", F_QUIT),		/* Quit     ["name"] */
	FUNC_TERM2 (NEED_NAME | NEED_CMD, "Restart", F_RESTART),	/* Restart "name" WindowManagerName */
	FUNC_TERM ("SystemShutdown", F_SYSTEM_SHUTDOWN),	/* Shutdown "name" | only available under gnome-session */
	FUNC_TERM ("Logout", F_LOGOUT),	/* Logout "name" | only available under gnome-session */
	FUNC_TERM ("QuitWM", F_QUIT_WM),	/* Will only work when not running under gnome-session */
	FUNC_TERM ("Suspend", F_SUSPEND),	/* If provided by UPower */
	FUNC_TERM ("Hibernate", F_HIBERNATE),	/* If provided by UPower */

	FUNC_TERM ("Refresh", F_REFRESH),	/* Refresh  ["name"] */
#ifndef NO_VIRTUAL
	FUNC_TERM2 (USES_NUMVALS, "Scroll", F_SCROLL),	/* Scroll     horiz vert */
	FUNC_TERM2 (USES_NUMVALS, "GotoPage", F_GOTO_PAGE),	/* GotoPage   x     y    */
	FUNC_TERM ("TogglePage", F_TOGGLE_PAGE),	/* TogglePage ["name"]   */
#endif
	FUNC_TERM2 (USES_NUMVALS, "CursorMove", F_MOVECURSOR),	/* CursorMove horiz vert */
	FUNC_TERM2 (NEED_WINIFNAME, "WarpFore", F_WARP_F),	/* WarpFore ["name" window_name] */
	FUNC_TERM2 (NEED_WINIFNAME, "WarpBack", F_WARP_B),	/* WarpBack ["name" window_name] */
	FUNC_TERM2 (NEED_NAME | NEED_CMD, "Wait", F_WAIT),	/* Wait      "name" attributes  */
	FUNC_TERM2 (USES_NUMVALS, "Desk", F_DESK),	/* Desk arg1 [arg2] */
	FUNC_TERM2 (USES_NUMVALS, "GotoDeskViewport", F_GOTO_DESKVIEWPORT),	/* GotoDeskViewport DESK+VX+VY */
#ifndef NO_WINDOWLIST
	FUNC_TERM2 (USES_NUMVALS, "WindowList", F_WINDOWLIST),	/* WindowList [arg1 arg2] */
#endif
	FUNC_TERM ("StopModuleList", F_STOPMODULELIST),	/* StopModuleList "name" */
	FUNC_TERM ("RestartModuleList", F_RESTARTMODULELIST),	/* RestartModuleList "name" */
	FUNC_TERM2 (NEED_NAME, "PopUp", F_POPUP),	/* PopUp    "popup_name" [popup_name] */
	FUNC_TERM2 (NEED_NAME, "Function", F_FUNCTION),	/* Function "function_name" [function_name] */
	FUNC_TERM2 (NEED_NAME | NEED_CMD, "Category", F_CATEGORY),	/* Category "function_name" category_name */
	FUNC_TERM2 (NEED_NAME | NEED_CMD, "CategoryTree", F_CATEGORY_TREE),	/* CategoryTree "function_name" category_name */
	FUNC_TERM ("MiniPixmap", F_MINIPIXMAP),	/* MiniPixmap "name" */
	FUNC_TERM ("SmallMiniPixmap", F_SMALL_MINIPIXMAP),	/* SmallMiniPixmap "name" */
	FUNC_TERM ("LargeMiniPixmap", F_SMALL_MINIPIXMAP),	/* LargeMiniPixmap "name" */
	FUNC_TERM ("Preview", F_Preview),	/* Preview "name" */
	FUNC_TERM2 (NEED_NAME, "DesktopEntry", F_DesktopEntry),	/* DesktopEntry "name" */
	FUNC_TERM2 (NEED_NAME | NEED_CMD, "Exec", F_EXEC),	/* Exec   "name" command */
	FUNC_TERM2 (NEED_NAME | NEED_CMD, "ExecInDir", F_ExecInDir),	/* Exec   "name" [path command] */
	FUNC_TERM2 (NEED_NAME | NEED_CMD, "Module", F_MODULE),	/* Module "name" command */
	FUNC_TERM2 (NEED_NAME | NEED_CMD, "ExecInTerm", F_ExecInTerm),	/* ExecInTerm   "name" command */
	FUNC_TERM2 (NEED_NAME | NEED_CMD, "ExecBrowser", F_ExecBrowser),	/* ExecBrowser   "name" url */
	FUNC_TERM2 (NEED_NAME | NEED_CMD, "ExecEditor", F_ExecEditor),	/* ExecEditor   "name" filename */
	FUNC_TERM2 (NEED_NAME | NEED_CMD, "KillModuleByName", F_KILLMODULEBYNAME),	/* KillModuleByName "name" module */
	FUNC_TERM2 (NEED_NAME | NEED_CMD, "RestartModuleByName", F_RESTARTMODULEBYNAME),	/* RestartModuleByName "name" module */
	FUNC_TERM2 (NEED_NAME | NEED_CMD, "KillAllModulesByName", F_KILLALLMODULESBYNAME),	/* KillAllModulesByName "name" module */
	FUNC_TERM2 (NEED_NAME | NEED_CMD, "QuickRestart", F_QUICKRESTART),	/* QuickRestart "name" what */
	FUNC_TERM2 (NEED_NAME | NEED_CMD, "Background", F_CHANGE_BACKGROUND),	/* Background "name" file_name */
	FUNC_TERM2 (NEED_NAME | NEED_CMD, "BackgroundForeign", F_CHANGE_BACKGROUND_FOREIGN),	/* BackgroundForeign "name" file_name */
	FUNC_TERM2 (NEED_NAME | NEED_CMD, "ChangeLook", F_CHANGE_LOOK),	/* ChangeLook "name" file_name */
	FUNC_TERM2 (NEED_NAME | NEED_CMD, "ChangeFeel", F_CHANGE_FEEL),	/* ChangeFeel "name" file_name */
	FUNC_TERM2 (NEED_NAME | NEED_CMD, "ChangeTheme", F_CHANGE_THEME),	/* ChangeTheme "name" file_name */
	FUNC_TERM2 (NEED_NAME | NEED_CMD, "ChangeThemeFile", F_CHANGE_THEME_FILE),	/* "name" file_name */
	FUNC_TERM2 (NEED_NAME | NEED_CMD, "ChangeColorscheme", F_CHANGE_COLORSCHEME),	/* ChangeColorscheme "name" file_name */
	FUNC_TERM2 (NEED_NAME | NEED_CMD, "InstallLook", F_INSTALL_LOOK),	/*  "name" file_name */
	FUNC_TERM2 (NEED_NAME | NEED_CMD, "InstallFeel", F_INSTALL_FEEL),	/*  "name" file_name */
	FUNC_TERM2 (NEED_NAME | NEED_CMD, "InstallBackground", F_INSTALL_BACKGROUND),	/* "name" file_name */
	FUNC_TERM2 (NEED_NAME | NEED_CMD, "InstallFont", F_INSTALL_FONT),	/* "name" file_name */
	FUNC_TERM2 (NEED_NAME | NEED_CMD, "InstallIcon", F_INSTALL_ICON),	/*  "name" file_name */
	FUNC_TERM2 (NEED_NAME | NEED_CMD, "InstallTile", F_INSTALL_TILE),	/*  "name" file_name */
	FUNC_TERM2 (NEED_NAME | NEED_CMD, "InstallThemeFile", F_INSTALL_THEME_FILE),	/*  "name" file_name */
	FUNC_TERM2 (NEED_NAME | NEED_CMD, "InstallColorscheme", F_INSTALL_COLORSCHEME),	/*  "name" file_name */
	FUNC_TERM2 (NEED_NAME | NEED_CMD, "SaveWorkspace", F_SAVE_WORKSPACE),	/* SaveWorkspace "name" file_name */
	FUNC_TERM ("SignalReloadGTKRCFile", F_SIGNAL_RELOAD_GTK_RCFILE),
	FUNC_TERM ("KIPCsendMessageAll", F_KIPC_SEND_MESSAGE_ALL),
	FUNC_TERM2 (TF_SYNTAX_TERMINATOR, "EndFunction", F_ENDFUNC),
	FUNC_TERM2 (TF_SYNTAX_TERMINATOR, "EndPopup", F_ENDPOPUP),
	FUNC_TERM ("TakeScreenShot", F_TAKE_SCREENSHOT),

	FUNC_TERM2 (NEED_CMD, "Set", F_SET),	/* Set "name" <variable>=<value> */

	FUNC_TERM2 (NEED_NAME | NEED_CMD, "Test", F_Test),
	FUNC_TERM2 (NEED_NAME | NEED_CMD, "Remap", F_Remap),

	/* this functions require window as aparameter */
	FUNC_TERM ("&nonsense&", F_WINDOW_FUNC_START),	/* not really a command */
	FUNC_TERM2 (USES_NUMVALS, "Move", F_MOVE),	/* Move     ["name"] [whereX whereY] */
	FUNC_TERM2 (USES_NUMVALS, "Resize", F_RESIZE),	/* Resize   ["name"] [toWidth toHeight] */
	FUNC_TERM ("Raise", F_RAISE),	/* Raise    ["name"] */
	FUNC_TERM ("Lower", F_LOWER),	/* Lower    ["name"] */
	FUNC_TERM ("RaiseLower", F_RAISELOWER),	/* RaiseLower ["name"] */
	FUNC_TERM ("PutOnTop", F_PUTONTOP),	/* PutOnTop  */
	FUNC_TERM ("PutOnBack", F_PUTONBACK),	/* PutOnBack */
	FUNC_TERM2 (USES_NUMVALS, "SetLayer", F_SETLAYER),	/* SetLayer    layer */
	FUNC_TERM2 (USES_NUMVALS, "ToggleLayer", F_TOGGLELAYER),	/* ToggleLayer layer1 layer2 */
	FUNC_TERM ("Shade", F_SHADE),	/* Shade    ["name"] */
	FUNC_TERM ("Delete", F_DELETE),	/* Delete   ["name"] */
	FUNC_TERM ("Destroy", F_DESTROY),	/* Destroy  ["name"] */
	FUNC_TERM ("Close", F_CLOSE),	/* Close    ["name"] */
	FUNC_TERM ("Iconify", F_ICONIFY),	/* Iconify  ["name"] value */
	FUNC_TERM2 (USES_NUMVALS, "Maximize", F_MAXIMIZE),	/* Maximize ["name"] [hori vert] */
	FUNC_TERM ("Fullscreen", F_FULLSCREEN),	/* Maximize ["name"] [hori vert] */
	FUNC_TERM ("Stick", F_STICK),	/* Stick    ["name"] */
	FUNC_TERM ("Focus", F_FOCUS),	/* Focus */
	FUNC_TERM2 (NEED_WINIFNAME, "ChangeWindowUp", F_CHANGEWINDOW_UP),	/* ChangeWindowUp   ["name" window_name ] */
	FUNC_TERM2 (NEED_WINIFNAME, "ChangeWindowDown", F_CHANGEWINDOW_DOWN),	/* ChangeWindowDown ["name" window_name ] */
	FUNC_TERM2 (NEED_WINIFNAME, "GoToBookmark", F_GOTO_BOOKMARK),	/* GoToBookmark ["name" window_bookmark ] */
	FUNC_TERM ("GetHelp", F_GETHELP),	/* */
	FUNC_TERM ("PasteSelection", F_PASTE_SELECTION),	/* */
	FUNC_TERM2 (USES_NUMVALS, "WindowsDesk", F_CHANGE_WINDOWS_DESK),	/* WindowDesk "name" new_desk */
	FUNC_TERM ("BookmarkWindow", F_BOOKMARK_WINDOW),	/* BookmarkWindow "name" new_bookmark */
	FUNC_TERM ("PinMenu", F_PIN_MENU),	/* PinMenu ["name"] */
	FUNC_TERM ("TakeWindowShot", F_TAKE_WINDOWSHOT),
	FUNC_TERM ("TakeFrameShot", F_TAKE_FRAMESHOT),
	FUNC_TERM ("SwallowWindow", F_SWALLOW_WINDOW),	/* SwallowWindow "name" module_name */
	/* end of window functions */
	/* these are commands  to be used only by modules */
	FUNC_TERM ("&nonsense&", F_MODULE_FUNC_START),	/* not really a command */
	FUNC_TERM ("Send_WindowList", F_SEND_WINDOW_LIST),	/* */
	FUNC_TERM ("SET_MASK", F_SET_MASK),	/* SET_MASK  mask lock_mask */
	FUNC_TERM2 (NEED_NAME, "SET_NAME", F_SET_NAME),	/* SET_NAME  name */
	FUNC_TERM ("UNLOCK", F_UNLOCK),	/* UNLOCK    1  */
	FUNC_TERM ("SET_FLAGS", F_SET_FLAGS),	/* SET_FLAGS flags */
	/* these are internal commands */
	FUNC_TERM ("&nonsense&", F_INTERNAL_FUNC_START),	/* not really a command */
	FUNC_TERM ("&raise_it&", F_RAISE_IT),	/* should not be used by user */
	/* wharf functions : */
	{TF_NO_MYNAME_PREPENDING, "Folder", 6, TT_FUNCTION, F_Folder, NULL},
	{TF_NO_MYNAME_PREPENDING | NEED_NAME | NEED_CMD | TF_NAMED, "Swallow", 7,
	 TT_FUNCTION, F_Swallow, NULL},
	{TF_NO_MYNAME_PREPENDING | NEED_NAME | NEED_CMD | TF_NAMED, "MaxSwallow",
	 10, TT_FUNCTION, F_MaxSwallow, NULL},
	{TF_NO_MYNAME_PREPENDING | NEED_NAME | NEED_CMD | TF_NAMED,
	 "SwallowModule", 13, TT_FUNCTION, F_SwallowModule,
	 NULL},
	{TF_NO_MYNAME_PREPENDING | NEED_NAME | NEED_CMD | TF_NAMED,
	 "MaxSwallowModule", 16, TT_FUNCTION, F_MaxSwallowModule,
	 NULL},
	{TF_NO_MYNAME_PREPENDING, "Size", 4, TT_FUNCTION, F_Size, NULL},
	{TF_NO_MYNAME_PREPENDING, "Transient", 9, TT_FUNCTION, F_Transient,
	 NULL},

	{0, NULL, 0, 0, 0}
};

struct SyntaxDef FuncSyntax = {
	'\0', '\n', FuncTerms,
	0, ' ', "", "\t",
	"AfterStep Function",
	"Functions",
	"built in AfterStep functions",
	NULL, 0
};

SyntaxDef PopupFuncSyntax = {
	'\n', '\0', FuncTerms,
	0, ' ', "\t", "\t",
	"Popup/Complex function definition",
	"Popup",
	"",
	NULL, 0
};



struct SyntaxDef *pFuncSyntax = &FuncSyntax;
struct SyntaxDef *pPopupFuncSyntax = &PopupFuncSyntax;

TermDef *func2fterm (FunctionCode func, int quiet)
{
	register int i;

	/* in most cases that should work : */
	if (func < F_FUNCTIONS_NUM)
		if (FuncTerms[func].id == func)
			return &(FuncTerms[func]);

	/* trying fallback if it did not : */
	for (i = 0; i < F_FUNCTIONS_NUM; i++)
		if (FuncTerms[i].id == func)
			return &(FuncTerms[i]);

	/* something terribly wrong has happened : */
	return NULL;
}

/************************************************************************************/
/* Command Line Processing/ App initialization here :                               */
/************************************************************************************/
void SetMyClass (const char *app_class)
{
	if (app_class != NULL) {
		strncpy (MyClass, (char *)app_class, MAX_MY_CLASS);
		MyClass[MAX_MY_CLASS] = '\0';
	}
}

void SetMyName (char *argv0)
{
	char *temp = strrchr (argv0, '/');

	/* Save our program name - for error messages */
	MyName = temp ? temp + 1 : argv0;
	set_application_name (argv0);
}

/* If you change/add options please change InitMyApp below and option flags in aftersteplib.h */

CommandLineOpts as_standard_cmdl_options[STANDARD_CMDL_OPTS_NUM] = {
#define  SHOW_VERSION   0
#define  SHOW_CONFIG    1
#define  SHOW_USAGE     2
/* 0*/ {"v", "version", "Display version information and stop", NULL,
				handler_show_info, NULL, SHOW_VERSION},
/* 1*/ {"c", "config", "Display Config information and stop", NULL,
				handler_show_info, NULL, SHOW_CONFIG},
/* 2*/ {"h", "help", "Display usage information and stop", NULL,
				handler_show_info, NULL, SHOW_USAGE},
/* 3*/ {NULL, "debug", "Debugging: Run in Synchronous mode", NULL,
				handler_set_flag, &(as_app_args.flags),
				ASS_Debugging},
/* 4*/ {"s", "single", "Run on single screen only", NULL,
				handler_set_flag, &(as_app_args.flags), ASS_SingleScreen},
/* 5*/ {"r", "restart", "Run as if it was restarted",
				"same as regular startup, only \nruns RestartFunctioninstead of InitFunction",
				handler_set_flag, &(as_app_args.flags), ASS_Restarting},
#define OPTION_HAS_ARGS     6
/* 6*/ {"d", "display", "Specify what X display we should connect to",
				"Overrides $DISPLAY environment variable",
				handler_set_string, &(as_app_args.display_name), 0, CMO_HasArgs},
/* 7*/ {"f", "config-file", "Read all config from requested file",
				"Use it if you want to use .steprc\ninstead of standard config files",
				handler_set_string, &(as_app_args.override_config), 0,
				CMO_HasArgs},
/* 8*/ {"p", "user-dir", "Read all the config from requested dir",
				"Use it to override config location\nrequested in compile time",
				handler_set_string, &(as_app_args.override_home), 0, CMO_HasArgs},
/* 9*/ {"g", "global-dir", "Use requested dir as a shared config dir",
				"Use it to override shared config location\nrequested in compile time",
				handler_set_string, &(as_app_args.override_share), 0, CMO_HasArgs},
/*10*/ {"V", "verbosity-level",
				"Change verbosity of the AfterStep output",
				"0 - will disable any output;\n1 - will allow only error messages;\n5 - both errors and warnings(default)",
				handler_set_int, &(as_app_args.verbosity_level), 0, CMO_HasArgs},
/*11*/ {NULL, "window", "Internal Use: Window in which action occured",
				"interface part which has triggered our startup",
				handler_set_int, &(as_app_args.src_window), 0, CMO_HasArgs},
/*12*/ {NULL, "context", "Internal Use: Context in which action occured",
				"interface part which has triggered our startup",
				handler_set_int, &(as_app_args.src_context), 0, CMO_HasArgs},
/*13*/ {NULL, "look", "Read look config from requested file",
				"Use it if you want to use different look\ninstead of what was selected from the menu",
				handler_set_string, &(as_app_args.override_look), 0, CMO_HasArgs},
/*14*/ {NULL, "feel", "Read feel config from requested file",
				"Use it if you want to use different feel\ninstead of what was selected from the menu",
				handler_set_string, &(as_app_args.override_feel), 0, CMO_HasArgs},
/*15*/ {NULL, "theme", "Read theme config from requested file",
				"Use it if you want to use different theme\ninstead of what was selected from the menu",
				handler_set_string, &(as_app_args.override_feel), 0, CMO_HasArgs},
#ifdef DEBUG_TRACE_X
/*16*/ {NULL, "trace-func",
				"Debugging: Trace calls to a function with requested name", NULL,
				handler_set_string, &(as_app_args.trace_calls), 0, CMO_HasArgs},
#endif
/*17*/ {"l", "log", "Save all output into the file",
				"(instead of printing it to console)",
				handler_set_string, &(as_app_args.log_file), 0, CMO_HasArgs},
/*18*/ {"L", "locale", "Set language locale",
				"to be used while displaying text",
				handler_set_dup_string, &(as_app_args.locale), 0, CMO_HasArgs},
/*19*/ {NULL, "myname", "Overrides module name",
				"will be used while parsing config files\nand reporting to AfterStep",
				handler_set_string, &(MyName), 0, CMO_HasArgs},
/*20*/ {NULL, "geometry", "Overrides module's geometry", NULL,
				handler_set_geometry, &(as_app_args.geometry), 0, CMO_HasArgs},
/*21*/ {NULL, "gravity", "Overrides module's gravity", NULL,
				handler_set_gravity, &(as_app_args.gravity), 0, CMO_HasArgs},
	{NULL, NULL, NULL, NULL, NULL, NULL, 0}
};

void standard_version (void)
{
	show_debug (__FILE__, __FUNCTION__, __LINE__,
							"version = \"%s\", MyVersionFunc = %p", VERSION,
							MyVersionFunc);

	if (MyVersionFunc)
		MyVersionFunc ();
	else
		printf ("%s version %s\n", MyClass, VERSION);
}

void
print_command_line_opt (const char *prompt, CommandLineOpts * options,
												ASFlagType mask)
{
	register int i;
	ASFlagType bit = 0x01;

	if (options == NULL)
		options = as_standard_cmdl_options;
	printf ("%s:\n", prompt);

	for (i = 0; options[i].handler != NULL; i++) {
		if (!get_flags (bit, mask)) {
			if (options[i].short_opt)
				printf (OPTION_SHORT_FORMAT, options[i].short_opt);
			else
				printf (OPTION_NOSHORT_FORMAT);

			if (!get_flags (options[i].flags, CMO_HasArgs))
				printf (OPTION_DESCR1_FORMAT_NOVAL, options[i].long_opt,
								options[i].descr1);
			else
				printf (OPTION_DESCR1_FORMAT_VAL, options[i].long_opt,
								options[i].descr1);

			if (options[i].descr2) {
				register char *start = options[i].descr2;
				register char *end;

				do {
					end = strchr (start, '\n');
					if (end == NULL)
						printf (OPTION_DESCR2_FORMAT, start);
					else {
						static char buffer[81];
						register int len = (end > start + 80) ? 80 : end - start;

						strncpy (buffer, start, len);
						buffer[len] = '\0';
						printf (OPTION_DESCR2_FORMAT, buffer);
						start = end + 1;
					}
				}
				while (end != NULL);
			}
		}
		bit = bit << 1;
	}
}

void standard_usage ()
{
	standard_version ();
	if (MyUsageFunc)
		MyUsageFunc ();
	else
		printf (OPTION_USAGE_FORMAT "\n", MyName);
	print_command_line_opt ("standard_options are :",
													as_standard_cmdl_options, as_app_args.mask);
}

void handler_show_info (char *argv, void *trg, long param)
{
	switch (param) {
	case SHOW_VERSION:
		standard_version ();
		break;
	case SHOW_CONFIG:
		standard_version ();
		printf ("BinDir            %s\n", AFTER_BIN_DIR);
		printf ("ManDir            %s\n", AFTER_MAN_DIR);
		printf ("DocDir            %s\n", AFTER_DOC_DIR);
		printf ("ShareDir          %s\n", AFTER_SHAREDIR);
		printf ("AfterDir          %s\n", AFTER_DIR);
		break;
	case SHOW_USAGE:
		standard_usage ();
		break;
	}
	exit (0);
}

void handler_set_flag (char *argv, void *trg, long param)
{
	register ASFlagType *f = trg;

	set_flags (*f, param);
}

void handler_set_string (char *argv, void *trg, long param)
{
	register char **s = trg;

	if (argv)
		*s = argv;
}

void handler_set_dup_string (char *argv, void *trg, long param)
{
	register char **s = trg;

	if (argv) {
		if (*s)
			free (*s);
		*s = mystrdup (argv);
	}
}

void handler_set_int (char *argv, void *trg, long param)
{
	register int *i = trg;

	if (argv)
		*i = atoi (argv);
}

void handler_set_geometry (char *argv, void *trg, long param)
{
	register ASGeometry *geom = trg;

	if (argv) {
		memset (geom, 0x00, sizeof (ASGeometry));
		parse_geometry (argv, &(geom->x), &(geom->y), &(geom->width),
										&(geom->height), &(geom->flags));
	}
}

void handler_set_gravity (char *argv, void *trg, long param)
{
	register int *i = trg;

	*i = ForgetGravity;
	if (argv) {

		if (mystrncasecmp (argv, "NorthWest", 9) == 0)
			*i = NorthWestGravity;
		else if (mystrncasecmp (argv, "SouthWest", 9) == 0)
			*i = SouthWestGravity;
		else if (mystrncasecmp (argv, "NorthEasth", 10) == 0)
			*i = NorthEastGravity;
		else if (mystrncasecmp (argv, "SouthEast", 9) == 0)
			*i = SouthEastGravity;
		else if (mystrncasecmp (argv, "Center", 6) == 0)
			*i = CenterGravity;
		else if (mystrncasecmp (argv, "Static", 6) == 0)
			*i = StaticGravity;
		else
			show_warning
					("unknown gravity type \"%s\". Use one of: NorthWest, SouthWest, NorthEast, SouthEast, Center, Static",
					 argv);
	}
}


int match_command_line_opt (char *argvi, CommandLineOpts * options)
{
	register char *ptr = argvi;
	register int opt;

	if (ptr == NULL)
		return -1;
	show_debug (__FILE__, __FUNCTION__, __LINE__, "ptr = \"%s\"", ptr);
	if (*ptr == '-') {
		++ptr;
		if (*ptr == '-') {
			++ptr;
			show_debug (__FILE__, __FUNCTION__, __LINE__, "ptr = \"%s\"", ptr);
			for (opt = 0; options[opt].handler; ++opt)
				if (strcmp (options[opt].long_opt, ptr) == 0)
					break;
		} else {
			show_debug (__FILE__, __FUNCTION__, __LINE__, "ptr = \"%s\"", ptr);
			for (opt = 0; options[opt].handler; ++opt)
				if (options[opt].short_opt)
					if (strcmp (options[opt].short_opt, ptr) == 0)
						break;
		}
	} else
		opt = -1;
	if (options[opt].handler == NULL)
		opt = -1;
	return opt;
}

static DeadPipe_handler _as_dead_pipe_handler = NULL;

DeadPipe_handler set_DeadPipe_handler (DeadPipe_handler new_handler)
{
	DeadPipe_handler old = _as_dead_pipe_handler;

	_as_dead_pipe_handler = new_handler;
	return old;
}

void ASDeadPipe (int nonsense)
{
	if (_as_dead_pipe_handler)
		_as_dead_pipe_handler (nonsense);
	else
		exit (0);
}

static char **AS_environ = NULL;

void override_environ (char **envp)
{
	AS_environ = envp;
}

void
InitMyApp (const char *app_class, int argc, char **argv,
					 void (*version_func) (void), void (*custom_usage_func) (void),
					 ASFlagType opt_mask)
{
	/* first of all let's set us some nice signal handlers */
#ifdef HAVE_SIGSEGV_HANDLING
	set_signal_handler (SIGSEGV);
#endif

	set_signal_handler (SIGUSR2);
	signal (SIGPIPE, ASDeadPipe);	/* don't forget DeadPipe should be provided by the app */

	SetMyClass (app_class);
	MyVersionFunc = version_func;
	MyUsageFunc = custom_usage_func;

	memset (&as_app_args, 0x00, sizeof (ASProgArgs));
	as_app_args.locale = mystrdup (AFTER_LOCALE);
	if (as_app_args.locale[0] == '\0') {
		free (as_app_args.locale);
		as_app_args.locale = mystrdup (getenv ("LANG"));
	}

	as_app_args.mask = opt_mask;
	as_app_args.gravity = ForgetGravity;
#ifndef NO_DEBUG_OUTPUT
	as_app_args.verbosity_level = OUTPUT_VERBOSE_THRESHOLD;
#else
	as_app_args.verbosity_level = OUTPUT_DEFAULT_THRESHOLD;
#endif

/* Uncomment this to enable cmd line args tracing/debugging :
 * set_output_threshold(20); */

	ASDefaultScr = safecalloc (1, sizeof (ScreenInfo));
	init_ScreenInfo (ASDefaultScr);
	show_debug (__FILE__, __FUNCTION__, __LINE__, "argc = %d", argc);

	if (argc > 0 && argv) {
		int i;

		as_app_args.saved_argc = argc;
		as_app_args.saved_argv = safecalloc (argc, sizeof (char *));
		for (i = 0; i < argc; ++i)
			as_app_args.saved_argv[i] = mystrdup (argv[i]);

		SetMyName (argv[0]);

		for (i = 1; i < argc; i++) {
			register int opt;

			show_debug (__FILE__, __FUNCTION__, __LINE__,
									"i = %d, argv[i] = 0x%p", i, argv[i]);
			if ((opt =
					 match_command_line_opt (&(argv[i][0]),
																	 as_standard_cmdl_options)) < 0)
				continue;
			if (get_flags ((0x01 << opt), as_app_args.mask))
				continue;
			if (get_flags (as_standard_cmdl_options[opt].flags, CMO_HasArgs)) {
				argv[i] = NULL;
				if (++i >= argc)
					continue;
				else
					as_standard_cmdl_options[opt].handler (argv[i],
																								 as_standard_cmdl_options
																								 [opt].trg,
																								 as_standard_cmdl_options
																								 [opt].param);
			} else
				as_standard_cmdl_options[opt].handler (NULL,
																							 as_standard_cmdl_options
																							 [opt].trg,
																							 as_standard_cmdl_options
																							 [opt].param);
			argv[i] = NULL;
		}
	}

	set_output_threshold (as_app_args.verbosity_level);
	if (as_app_args.log_file)
		if (freopen (as_app_args.log_file,
								 /*get_flags( as_app_args.flags, ASS_Restarting)? */
								 "a" /*:"w" */ , stderr)
				== NULL)
			show_system_error ("failed to redirect output into file \"%s\"",
												 as_app_args.log_file);


	fd_width = get_fd_width ();

	if (FuncSyntax.term_hash == NULL)
		PrepareSyntax (&FuncSyntax);
	if (as_app_args.locale == NULL)
		as_app_args.locale = mystrdup ("");
	if (as_app_args.locale && strlen (as_app_args.locale) > 0) {
		as_set_charset (parse_charset_name (as_app_args.locale));
#ifdef I18N
		if (strlen (as_app_args.locale) > 0)
			if (setlocale (LC_CTYPE, as_app_args.locale) == NULL) {
				show_error ("unable to set locale");
			}
#endif
	} else {
#ifdef I18N
		show_warning
				("LANG environment variable is not set - use -L \"locale\" command line option to define locale");
#endif
	}

#ifdef DEBUG_TRACE_X
	trace_enable_function (as_app_args.trace_calls);
#endif

	asxml_var_insert (ASXMLVAR_IconButtonWidth, 64);
	asxml_var_insert (ASXMLVAR_IconButtonHeight, 64);
	asxml_var_insert (ASXMLVAR_IconWidth, 48);
	asxml_var_insert (ASXMLVAR_IconHeight, 48);
	asxml_var_insert (ASXMLVAR_MinipixmapWidth, 24);
	asxml_var_insert (ASXMLVAR_MinipixmapHeight, 24);
	asxml_var_insert (ASXMLVAR_TitleFontSize, 14);
	asxml_var_insert (ASXMLVAR_MenuFontSize, 16);
	asxml_var_insert (ASXMLVAR_MenuShowMinipixmaps, 1);
	asxml_var_insert (ASXMLVAR_MenuShowUnavailable, 1);

}

void free_func_hash ()
{
	if (FuncSyntax.term_hash) {
		FreeSyntaxHash (&FuncSyntax);
	}
}

/*********** end command line parsing **************************/
static char *_as_known_terms[] = {
	"x-terminal-emulator",
	"urxvt",
	"aterm",
	"rxvt  -tr -fg yellow -bg black",
	"Eterm -tr -tint blue -fg yellow -bg black",
	"xterm -fg yellow -bg blue",
	NULL
};

static char *_as_known_browsers[] = {
	"$BROWSER",
	"sensible-browser",
	"x-www-browser",							/* don't like default debian selection of konqueror */
	"firefox",
	"mozilla-firefox",
	"mozilla",
	"opera",
	NULL
};

static char *_as_known_editors[] = {
	"editor",
	"$EDITOR",
	"nedit",
	"xemacs",
	"gedit",
	"kedit",
	"kate",
	NULL
};

static char **_as_known_tools[ASTool_Count] = {
	_as_known_terms,
	_as_known_browsers,
	_as_known_editors
};

static char *_as_tools_name[ASTool_Count] = {
	"Terminal",
	"Browser",
	"Editor"
};


char *as_get_default_tool (ASToolType type)
{
	int i;

	for (i = 0; _as_known_tools[type][i]; ++i) {
		char *tmp = _as_known_tools[type][i];
		char *fullname = NULL;
		int res;

		if (tmp[0] == '$')
			tmp = copy_replace_envvar (tmp);
		res = get_executable_in_path (tmp, &fullname);
		if (tmp != _as_known_tools[type][i])
			free (tmp);
		if (res > 0)
			return fullname;
	}
	return NULL;
}

void
set_environment_tool_from_list (ASEnvironment * e, ASToolType type,
																char **list, int list_len)
{
	int i;

	destroy_string (&(e->tool_command[type]));
	for (i = 0; i < list_len; ++i)
		if (list[i]) {
			char *tmp = list[i];
			char *fullname = NULL;

			if (tmp[0] == '$')
				tmp = copy_replace_envvar (tmp);
			if (get_executable_in_path (tmp, &fullname)) {
				e->tool_command[type] = fullname;
				break;
			} else
				show_warning ("%s command %s is not in the path",
											_as_tools_name[type], tmp);
			if (tmp != list[i])
				free (tmp);
		}
	if (e->tool_command[type] == NULL)
		e->tool_command[type] = as_get_default_tool (type);
	show_progress ("%s is set to: \"%s\"", _as_tools_name[type],
								 e->tool_command[type] ? e->tool_command[type] : "none");
}

static char *make_themed_icon_category_path (const char *category,
																						 int desired_size_idx,
																						 Bool fallback)
{
	char *tmp, *tmp2, *theme_path;
	static char *standard_sizes[] =
			{ "16x16", "32x32", "48x48", "64x64", "128x128", NULL };

	if (!fallback && Environment->IconTheme == NULL)
		return NULL;
	if (fallback && Environment->IconThemeFallback == NULL)
		return NULL;

	tmp =
			make_file_name (fallback ? Environment->
											IconThemeFallback : Environment->IconTheme,
											standard_sizes[desired_size_idx]);
	tmp2 = make_file_name (Environment->IconThemePath, tmp);
	free (tmp);
	theme_path = make_file_name (tmp2, category);
	free (tmp2);

	return theme_path;
}

Bool is_themable_icon (const char *name)
{
	return (Environment && Environment->IconTheme
					&& Environment->IconThemePath && strchr (name, '/') == NULL);
}

ASImage *load_environment_icon (const char *category, const char *name,
																int desired_size)
{
	ASImage *icon = NULL;
	char *png_name = NULL;
	char *svg_name = NULL;
	Bool add_to_man = False;
	int len;
	int desired_size_idx;

	if (name == NULL || ASDefaultScr == NULL)
		return NULL;

	desired_size_idx = (desired_size + 15) / 16 - 1;
	if (desired_size_idx > 4)
		desired_size_idx = 4;
	else if (desired_size_idx < 0)
		desired_size_idx = 0;

	show_debug (__FILE__, __FUNCTION__, __LINE__, "IconTheme = %s",
							Environment->IconTheme);

	len = strlen (name);
	if (len < 4 || name[len - 4] != '.') {
		png_name = add_file_extension (name, "png");
		svg_name = add_file_extension (name, "svg");
	}
	/* we maybe already loaded so try with just the name : */
	icon = fetch_asimage (ASDefaultScr->image_manager, name);

	/* nope we are not, so try loading as themed icon first: */
	if (icon == NULL && is_themable_icon (name) && category) {
		int fallback;
		for (fallback = 0; fallback <= 1; fallback++) {
			int try, try_size_idx = desired_size_idx;
			for (try = 0; icon == NULL && try < 3; ++try) {
				char *theme_path =
						make_themed_icon_category_path (category, try_size_idx,
																						fallback);
				if (theme_path == NULL)
					break;

				if (icon == NULL && png_name) {
					char *themed_name = make_file_name (theme_path, png_name);
					icon =
							get_asimage_quiet (ASDefaultScr->image_manager, themed_name,
																 ASFLAGS_EVERYTHING, 100);
					free (themed_name);
				}
				if (icon == NULL && svg_name) {
					char *themed_name = make_file_name (theme_path, svg_name);
					icon =
							get_asimage_quiet (ASDefaultScr->image_manager, themed_name,
																 ASFLAGS_EVERYTHING, 100);
					free (themed_name);
				}
				if (icon == NULL) {
					char *themed_name = make_file_name (theme_path, name);
					icon =
							get_asimage_quiet (ASDefaultScr->image_manager, themed_name,
																 ASFLAGS_EVERYTHING, 100);
					free (themed_name);
				}
				free (theme_path);
				switch (try) {
				case 0:
					try_size_idx =
							desired_size_idx <
							4 ? desired_size_idx + 1 : desired_size_idx - 1;
					break;
				case 1:
					try_size_idx = desired_size_idx == 0
							|| desired_size_idx == 4 ? 2 : desired_size_idx - 1;
					break;
				default:
					break;
				}
			}
		}
		add_to_man = True;
	}

	/* finally, try loading straight (for native AS icons primarily): */
	if (icon == NULL) {
		icon =
				get_asimage_quiet (ASDefaultScr->image_manager, name,
													 ASFLAGS_EVERYTHING, 100);
		if (icon == NULL) {
			add_to_man = True;
			if (png_name)
				icon =
						get_asimage_quiet (ASDefaultScr->image_manager, png_name,
															 ASFLAGS_EVERYTHING, 100);
			if (icon == NULL && svg_name)
				icon =
						get_asimage_quiet (ASDefaultScr->image_manager, svg_name,
															 ASFLAGS_EVERYTHING, 100);
		}
	}

	if (icon && add_to_man && icon->ref_count == 1) {
		forget_asimage (icon);
		if (icon->imageman == NULL)
			store_asimage (ASDefaultScr->image_manager, icon, name);
	}
	if (png_name)
		free (png_name);
	if (svg_name)
		free (svg_name);
	return icon;
}

ASImage *load_environment_icon_any (const char *filename, int desired_size)
{
	ASImage *icon = NULL;
	if (filename) {
		Bool possibly_themed = is_themable_icon (filename);
		icon = load_environment_icon ("apps", filename, desired_size);
		if (!icon && possibly_themed)
			icon = load_environment_icon ("actions", filename, desired_size);
		if (!icon && possibly_themed)
			icon = load_environment_icon ("places", filename, desired_size);
		if (!icon && possibly_themed)
			icon = load_environment_icon ("categories", filename, desired_size);
		if (!icon && possibly_themed)
			icon = load_environment_icon ("devices", filename, desired_size);
	}
	return icon;
}

ASEnvironment *make_default_environment ()
{
	int i;
	ASEnvironment *e = safecalloc (1, sizeof (ASEnvironment));
	static const char *default_pixmap_path_format =
			"%s/desktop/icons/:"
			"%s/desktop/icons/:"
			"%s/desktop/:"
			"%s/desktop/:" "%s/desktop/buttons/:" "%s/desktop/buttons/:"
			"%s/backgrounds/:" "%s/backgrounds/:" "%s";

	static const char *default_font_path_format =
			"%s/desktop/fonts/:" "%s/desktop/fonts/:"
			"/usr/share/fonts/default/TrueType/:" "%s";

	static const char *default_cursor_path_format =
			"%s/desktop/cursors:" "%s/desktop/cursors";

	e->desk_scale = 24;
	e->desk_pages_h = 2;
	e->desk_pages_v = 2;
	e->module_path = mystrdup (AFTER_BIN_DIR);
	e->icon_path = mystrdup (DEFAULT_ICON_DIR);
	e->pixmap_path =
			safemalloc (strlen ((char *)default_pixmap_path_format) +
									strlen (AFTER_DIR) * 4 + strlen (AFTER_SHAREDIR) * 4 +
									strlen (DEFAULT_PIXMAP_DIR) + 1);
	sprintf (e->pixmap_path, default_pixmap_path_format, AFTER_DIR,
					 AFTER_SHAREDIR, AFTER_DIR, AFTER_SHAREDIR, AFTER_DIR,
					 AFTER_SHAREDIR, AFTER_DIR, AFTER_SHAREDIR, DEFAULT_PIXMAP_DIR);

	e->font_path = safemalloc (strlen ((char *)default_font_path_format) +
														 strlen (AFTER_DIR) + strlen (AFTER_SHAREDIR) +
														 strlen (DEFAULT_TTF_DIR) + 1);
	sprintf (e->font_path, default_font_path_format, AFTER_DIR,
					 AFTER_SHAREDIR, DEFAULT_TTF_DIR);

	e->cursor_path =
			safemalloc (strlen ((char *)default_cursor_path_format) +
									strlen (AFTER_DIR) + strlen (AFTER_SHAREDIR) + 1);
	sprintf (e->cursor_path, default_cursor_path_format, AFTER_DIR,
					 AFTER_SHAREDIR);

	for (i = 0; i < ASTool_Count; ++i)
		e->tool_command[i] = as_get_default_tool (i);

	/* by default - don't do overwrite gtkrc files so to not aggrave people */
	e->gtkrc_path = NULL;					/* make_session_rc_file(Session, GTKRC_FILE); */
	e->gtkrc20_path = NULL;				/* make_session_rc_file(Session, GTKRC20_FILE) ; */
	e->IconTheme = mystrdup ("oxygen");
	e->IconThemePath = mystrdup ("/usr/chare/icons");
	e->IconThemeFallback = mystrdup ("hicolor");
	return e;
}

void destroy_asenvironment (ASEnvironment ** penv)
{
	if (penv) {
		ASEnvironment *e = *penv;

		if (e) {
			int i;

			if (e->module_path)
				free (e->module_path);
			if (e->sound_path)
				free (e->sound_path);
			if (e->icon_path)
				free (e->icon_path);
			if (e->pixmap_path)
				free (e->pixmap_path);
			if (e->font_path)
				free (e->font_path);
			if (e->cursor_path)
				free (e->cursor_path);
			for (i = 0; i < ASTool_Count; ++i)
				destroy_string (&(e->tool_command[i]));

			destroy_string (&(e->gtkrc_path));
			destroy_string (&(e->gtkrc20_path));
			destroy_string (&(e->IconTheme));
			destroy_string (&(e->IconThemePath));
			destroy_string (&(e->IconThemeFallback));

			free (e);
			*penv = NULL;
		}
	}
}

/*
 * Initialize database variables
 */

void destroy_asdatabase ()
{
	if (Database)
		destroy_asdb (&Database);
	/* XResources : */
	destroy_user_database ();
}

static ASCategoryTree *name2desktop_category_tree (const char *name,
																									 int *tree_name_len)
{
	ASCategoryTree *ct = CombinedCategories;
	int offset = 0;

/*	fprintf( stderr, __FUNCTION__ ": checking \"%s\" (AfterSTep categories = %p)\n", name, AfterStepCategories );*/

	if (!mystrncasecmp (name, "AfterStep:", 10)) {
		ct = AfterStepCategories;
		offset = 10;
	} else if (!mystrncasecmp (name, "KDE:", 4)) {
		ct = KDECategories;
		offset = 4;
	} else if (!mystrncasecmp (name, "GNOME:", 6)) {
		ct = GNOMECategories;
		offset = 6;
	} else if (!mystrncasecmp (name, "OTHER:", 7)) {
		ct = OtherCategories;
		offset = 7;
	} else if (!mystrncasecmp (name, "COMBINED:", 9)) {
		ct = CombinedCategories;
		offset = 9;
	}
	if (tree_name_len)
		*tree_name_len = offset;
	return ct;
}

ASDesktopCategory *name2desktop_category (const char *name,
																					ASCategoryTree ** tree_return)
{
	int offset = 0;
	ASCategoryTree *ct = name2desktop_category_tree (name, &offset);

	if (tree_return)
		*tree_return = ct;

	return fetch_desktop_category (ct, name + offset);
}

ASDesktopEntry *name2desktop_entry (const char *name,
																		ASCategoryTree ** tree_return)
{
	int offset = 0;
	ASCategoryTree *ct = name2desktop_category_tree (name, &offset);

	if (tree_return)
		*tree_return = ct;

	return fetch_desktop_entry (ct, name + offset);
}

void InitSession ()
{
	/* initializing our dirs names */
	if (Session == NULL) {
		Session =
				GetNCASSession (ASDefaultScr, as_app_args.override_home,
												as_app_args.override_share);
		if (as_app_args.override_config)
			set_session_override (Session, as_app_args.override_config, 0);
		if (as_app_args.override_look)
			set_session_override (Session, as_app_args.override_look,
														F_CHANGE_LOOK);
		if (as_app_args.override_feel)
			set_session_override (Session, as_app_args.override_feel,
														F_CHANGE_FEEL);
	}
}

void free_as_app_args ()
{
	int i;

	for (i = 0; i < as_app_args.saved_argc; ++i)
		if (as_app_args.saved_argv[i])
			free (as_app_args.saved_argv[i]);
	free (as_app_args.saved_argv);
	as_app_args.saved_argv = NULL;

	destroy_string (&(as_app_args.locale));

}

void FreeMyAppResources ()
{
	cleanup_default_balloons ();
	destroy_asdatabase ();
	mystyle_destroy_all ();
	mylook_init (&(ASDefaultScr->Look), True, ASFLAGS_EVERYTHING);
	destroy_image_manager (ASDefaultScr->image_manager, False);
	destroy_font_manager (ASDefaultScr->font_manager, False);
	clientprops_cleanup ();
	destroy_wmprops (ASDefaultScr->wmprops, False);
	wmprops_cleanup ();
	free_func_hash ();
	flush_keyword_ids ();
	purge_asimage_registry ();
	asxml_var_cleanup ();
	custom_color_cleanup ();
	build_xpm_colormap (NULL);
	destroy_screen_gcs (ASDefaultScr);
	if (ASDefaultScr->RootImage) {
		safe_asimage_destroy (ASDefaultScr->RootImage);
		ASDefaultScr->RootImage = NULL;
	}
	destroy_asvisual (ASDefaultScr->asv, False);
	free_as_app_args ();
	destroy_assession (Session);
	Session = NULL;
	destroy_asenvironment (&Environment);
	is_executable_in_path (NULL);
#ifdef XSHMIMAGE
	flush_shm_cache ();
#endif
	free (ASDefaultScr);
	flush_default_asstorage ();
	flush_asbidirlist_memory_pool ();
	flush_ashash_memory_pool ();

}


/************ child process spawning ***************************/
/***********************************************************************
 *  Procedure:
 *  general purpose child launcher - spawn child.
 *  Pass along all the cmd line args if needed.
 *  returns PID of the spawned process
 ************************************************************************/
static int as_singletons[MAX_SINGLETONS_NUM];
static Bool as_init_singletons = True;

void as_sigchild_handler (int signum)
{
	int pid;
	int status;

	signal (SIGCHLD, as_sigchild_handler);
	DEBUG_OUT ("Entering SigChild_handler(%lu)", time (NULL));
	if (as_init_singletons)
		return;
	while ((pid = WAIT_CHILDREN (&status)) > 0) {
		register int i;

		LOCAL_DEBUG_OUT ("pid = %d", pid);
		for (i = 0; i < MAX_SINGLETONS_NUM; i++)
			if (pid == as_singletons[i]) {
				as_singletons[i] = 0;
				break;
			}
	}
	DEBUG_OUT ("Exiting SigChild_handler(%lu)", time (NULL));
}

/*
 * This should return 0 if process of running external app to draw background completed or killed.
 * otherwise it returns > 0
 */
int check_singleton_child (int singleton_id, Bool kill_it_to_death)
{
	int i;
	int pid, status;

	if (as_init_singletons || singleton_id < 0)
		return -1;

	if (singleton_id >= MAX_SINGLETONS_NUM)
		singleton_id = MAX_SINGLETONS_NUM - 1;

	DEBUG_OUT ("CheckingForDrawChild(%lu)....", time (NULL));
	if ((pid = as_singletons[singleton_id]) > 0) {
		DEBUG_OUT ("checking on singleton child #%d started with PID (%d)",
							 singleton_id, pid);
		if (kill_it_to_death) {
			kill (pid, SIGTERM);
			for (i = 0; i < 100; i++) {	/* give it 10 sec to terminate */
				sleep_a_millisec (100);
				if (WAIT_CHILDREN (&status) == pid
						|| as_singletons[singleton_id] <= 0)
					break;
			}
			if (i >= 100)
				kill (pid, SIGKILL);		/* no more mercy */
			as_singletons[singleton_id] = 0;
		}
	} else if (as_singletons[singleton_id] < 0)
		as_singletons[singleton_id] = 0;

	DEBUG_OUT ("Done(%lu). Child PID on exit = %d.", time (NULL),
						 as_singletons[singleton_id]);
	return as_singletons[singleton_id];
}

int
spawn_child (const char *cmd, int singleton_id, int screen,
						 const char *orig_display, Window w, int context, Bool do_fork,
						 Bool pass_args, ...)
{
	int pid = 0;

	if (cmd == NULL)
		return 0;
	if (as_init_singletons) {
		register int i;

		for (i = 0; i < MAX_SINGLETONS_NUM; i++)
			as_singletons[i] = 0;
		signal (SIGCHLD, as_sigchild_handler);
		as_init_singletons = False;
	}

	if (singleton_id >= 0) {
		if (singleton_id >= MAX_SINGLETONS_NUM)
			singleton_id = MAX_SINGLETONS_NUM - 1;
		if (as_singletons[singleton_id] > 0)
			check_singleton_child (singleton_id, True);
	}

	if (do_fork)
		pid = fork ();

	if (pid != 0) {
		/* there is a possibility of the race condition here
		 * but it really is not worse the trouble to try and avoid it.
		 */
		if (singleton_id >= 0)
			as_singletons[singleton_id] = pid;
		return pid;
	} else {											/* we get here only in child process. We now need to spawn new proggy here: */
		int len;
		char *display = mystrdup (XDisplayString (dpy));
		char **envp;
		register char *ptr;

		char *cmdl;
		char *arg, *screen_str = NULL, *w_str = NULL, *context_str = NULL;
		int env_s = 0;
		char **envvars = AS_environ;
		int font_path_slot = -1, image_path_slot = -1;

		va_list ap;

		LOCAL_DEBUG_OUT ("dpy = %p, DisplayString = \"%s\"", dpy, display);
		LOCAL_DEBUG_OUT ("pid(%d), entered child process to spawn ...", pid);

#if HAVE_DECL_ENVIRON
		if (envvars == NULL) {
			envvars = environ;
		}
#else
/* how the hell could we get environment otherwise ? */
#endif
		if (envvars) {
			int font_path_len = strlen (ASFONT_PATH_ENVVAR);
			int image_path_len = strlen (ASIMAGE_PATH_ENVVAR);

			for (env_s = 0; envvars[env_s] != NULL; ++env_s) {
				if (font_path_slot < 0 && strlen (envvars[env_s]) > font_path_len)
					if (strncmp (envvars[env_s], ASFONT_PATH_ENVVAR, font_path_len)
							== 0)
						font_path_slot = env_s;
				if (image_path_slot < 0
						&& strlen (envvars[env_s]) > image_path_len)
					if (strncmp (envvars[env_s], ASIMAGE_PATH_ENVVAR, image_path_len)
							== 0)
						image_path_slot = env_s;
			}
		}
		if (font_path_slot < 0)
			++env_s;
		if (image_path_slot < 0)
			++env_s;
		envp = safecalloc (env_s + 2, sizeof (char *));

		/* environment variabless to pass to child process */
		if (envvars) {
			int dst = 0;

			for (env_s = 0; envvars[env_s] != NULL; ++env_s) {
				/* don't want to path DESKTOP_AUTOSTART_ID to our children -
				   its set by gnome-session for AfterStep proper specifically,
				   otherwise children will attempt to re-use it for SessionManagement registration, failing miserably */
				if (strlen (envvars[env_s]) < sizeof (SESSION_ID_ENVVAR)
						|| strncmp (envvars[env_s], SESSION_ID_ENVVAR,
												sizeof (SESSION_ID_ENVVAR) - 1) != 0)
					envp[dst++] = envvars[env_s];
			}
			env_s = dst;
		}

		envp[env_s] =
				safemalloc (8 + strlen (orig_display ? orig_display : display) +
										1);
		sprintf (envp[env_s], "DISPLAY=%s",
						 orig_display ? orig_display : display);
		++env_s;
		if (Environment) {
			if (Environment->pixmap_path != NULL) {
				int slot_no = image_path_slot;

				if (slot_no < 0)
					slot_no = env_s++;

				envp[slot_no] =
						safemalloc (strlen (ASIMAGE_PATH_ENVVAR) + 1 +
												strlen (Environment->pixmap_path) + 1);
				sprintf (envp[slot_no], "%s=%s", ASIMAGE_PATH_ENVVAR,
								 Environment->pixmap_path);
			}
			if (Environment->font_path) {
				int slot_no = font_path_slot;

				if (slot_no < 0)
					slot_no = env_s++;
				envp[slot_no] =
						safemalloc (strlen (ASFONT_PATH_ENVVAR) + 1 +
												strlen (Environment->font_path) + 1);
				sprintf (envp[slot_no], "%s=%s", ASFONT_PATH_ENVVAR,
								 Environment->font_path);
			}
		}

		len = strlen ((char *)cmd);
		if (pass_args) {
/*
			This bit of code seems to break AS restarting
			on Fedora 8. causing DISPLAY=":0.0" to
			become DISPLAY=":0.".  -- Jeremy
			Fixed by moving code under if(screen_str) -- Sasha Vasko
*/
			if (screen >= 0)
				screen_str = string_from_int (screen);

			if (screen_str) {
				register int i = 0;

				while (display[i])	++i;
				while (i > 0 && isdigit (display[--i])) ;
				if (display[i] == '.')
					display[i] = '\0';
			}

			if (w != None)
				w_str = string_from_int (w);
			if (context != C_NO_CONTEXT)
				context_str = string_from_int (context);

			len += 1 + 2 + 1 + strlen (orig_display ? orig_display : display);
			if (screen_str)
				len += strlen (screen_str);
			len += 3;									/* for "-s " */
			if (get_flags (as_app_args.flags, ASS_Debugging))
				len += 8;
			if (get_flags (as_app_args.flags, ASS_Restarting))
				len += 3;
			if (as_app_args.override_config)
				len += 4 + strlen (as_app_args.override_config);
			if (as_app_args.override_home)
				len += 4 + strlen (as_app_args.override_home);
			if (as_app_args.override_share)
				len += 4 + strlen (as_app_args.override_share);

			if (as_app_args.locale)
				len += 4 + strlen (as_app_args.locale);

			if (as_app_args.verbosity_level != OUTPUT_DEFAULT_THRESHOLD)
				len += 4 + 32;
#ifdef DEBUG_TRACE_X
			if (as_app_args.trace_calls)
				len += 13 + strlen (as_app_args.trace_calls);
#endif
			if (w_str)
				len += 1 + 8 + 1 + strlen (w_str);
			if (context_str)
				len += 1 + 9 + 1 + strlen (context_str);
		}
		/* now we want to append arbitrary number of arguments to the end of command line : */
		va_start (ap, pass_args);
		while ((arg = va_arg (ap, char *)) != NULL)
			 len += 1 + strlen (arg);

		va_end (ap);

		len += 4;

		ptr = cmdl = safemalloc (len);
		strcpy (cmdl, (char *)cmd);
		while (*ptr)
			ptr++;
		if (pass_args) {
			if (orig_display)
				ptr += sprintf (ptr, " -d %s -s", orig_display);
			else if (screen_str)
				ptr += sprintf (ptr, " -d %s.%s -s", display, screen_str);
			else
				ptr += sprintf (ptr, " -d %s -s", display);


			if (get_flags (as_app_args.flags, ASS_Debugging)) {
				strcpy (ptr, " --debug");
				ptr += 8;
			}
			if (get_flags (as_app_args.flags, ASS_Restarting)) {
				strcpy (ptr, " -r");
				ptr += 3;
			}
			if (as_app_args.override_config)
				ptr += sprintf (ptr, " -f %s", as_app_args.override_config);
			if (as_app_args.override_home)
				ptr += sprintf (ptr, " -p %s", as_app_args.override_home);
			if (as_app_args.override_share)
				ptr += sprintf (ptr, " -g %s", as_app_args.override_share);
			if (as_app_args.verbosity_level != OUTPUT_DEFAULT_THRESHOLD)
				ptr += sprintf (ptr, " -V %d", as_app_args.verbosity_level);
			LOCAL_DEBUG_OUT
					("len = %d, cmdl = \"%s\" strlen = %d, locale = \"%s\", ptr-cmdl = %d",
					 len, cmdl, (int)strlen (cmdl), as_app_args.locale,
					 (int)(ptr - cmdl));
			if (as_app_args.locale && as_app_args.locale[0]
					&& !isspace (as_app_args.locale[0]))
				ptr += sprintf (ptr, " -L %s", as_app_args.locale);

#ifdef DEBUG_TRACE_X
			if (as_app_args.trace_calls)
				ptr += sprintf (ptr, " --trace-func %s", as_app_args.trace_calls);
#endif
			if (w_str)
				ptr += sprintf (ptr, " --window %s", w_str);
			if (context_str)
				ptr += sprintf (ptr, " --context %s", context_str);
		}

		va_start (ap, pass_args);
		while ((arg = va_arg (ap, char *)) != NULL) {
			*(ptr++) = ' ';
			strcpy (ptr, arg);
			while (*ptr)
				ptr++;
			LOCAL_DEBUG_OUT ("len = %d, cmdl = \"%s\" strlen = %d", len, cmdl,
											 (int)strlen (cmdl));

		}
		va_end (ap);
		if (do_fork) {
			int i = ptr - cmdl;

			while (--i >= 0)
				if (!isspace (cmdl[i]))
					break;
			do_fork = (i < 0 || cmdl[i] != '&');
		}
		strcpy (ptr, do_fork ? " &\n" : "\n");

		LOCAL_DEBUG_OUT ("len = %d, cmdl = \"%s\" strlen = %d", len, cmdl,
										 (int)strlen (cmdl));
#if defined(LOCAL_DEBUG) && !defined(NO_DEBUG_OUTPUT)
		{
			FILE *fff = fopen ("/tmp/afterstep.exec.log", "a");

			if (fff) {
				fprintf (fff, "%s:%ld: [%s]", MyName, time (NULL), cmdl);
				fclose (fff);
			}
		}
#endif

		LOCAL_DEBUG_OUT ("execle(\"%s\")", cmdl);
		/* fprintf( stderr, "len=%d: execl(\"%s\")", len, cmdl ); */

		/* CYGWIN does not handle close-on-exec gracefully - whave to do it ourselves : */
		if (CloseOnExec)
			CloseOnExec ();

		{
			const char *shell;
			char *argv0;

			if ((shell = getenv ("SHELL")) == NULL || *shell == '\0')
				shell = mystrdup ("/bin/sh");

			parse_file_name (shell, NULL, &argv0);
			/* argv0 = basename(shell); */

			execle (shell, argv0, "-c", cmdl, (char *)0, envp);
		}

		if (screen >= 0)
			show_error ("failed to start %s on the screen %d", cmd, screen);
		else
			show_error ("failed to start %s", cmd);
		show_system_error (" complete command line: \"%s\"\n", cmdl);
		exit (128);
	}
}

/******************************************************************************/
/* web download functions : */
/******************************************************************************/
Bool is_url (const char *url)
{
	return (strncmp (url, "http://", 7) == 0
					|| strncmp (url, "ftp://", 6) == 0);
}

char *make_log_name (const char *cachedFileName)
{
	char *logName = safemalloc (strlen (cachedFileName) + 4 + 1);

	sprintf (logName, "%s.log", cachedFileName);
	return logName;
}

int spawn_download (const char *url, const char *cachedFileName)
{
	/* cmdl: wget -b -nv -o <escaped_url>.log -O <cache_file> <url> */
#define WGET_CMDL_HEADER "wget -b -nv -o "
	int cacheFNLen = strlen (cachedFileName);
	char *wget_cmdl =
			safemalloc (sizeof (WGET_CMDL_HEADER) + cacheFNLen + 4 + 1 + 2 + 1 +
									cacheFNLen + 1 + strlen (url) + 1);
	int res;

	sprintf (wget_cmdl, WGET_CMDL_HEADER "%s.log -O %s %s", cachedFileName,
					 cachedFileName, url);
	LOCAL_DEBUG_OUT ("spawning \"%s\"", wget_cmdl);
	res =
			spawn_child (wget_cmdl, -1, -1, NULL, None, C_NO_CONTEXT, True,
									 False, NULL);
	free (wget_cmdl);
	return res;
}

Bool
check_download_complete (int pid, const char *cachedFileName,
												 int *sizeDownloaded, int *size)
{
	if (pid != 0) {
		/* TODO : possibly check if process exited ? */

	}
	/* verify log file */
	{
		char *logName = make_log_name (cachedFileName);
		char *log = logName ? load_file (logName) : NULL;

		if (log && log[0] != '\0') {
			int s1 = 0, s2 = 1;

			char *openBracket = strchr (log, '[');

			LOCAL_DEBUG_OUT ("Open Bracket:%s", openBracket);
			if (openBracket) {
				sscanf (openBracket + 1, "%d/%d", &s1, &s2);
				LOCAL_DEBUG_OUT ("s1 = %d, s2 = %d", s1, s2);
			}
			if (sizeDownloaded)
				*sizeDownloaded = s1;
			if (size)
				*size = s2;
			free (log);
			return True;
		}
	}
	return False;
}


/************ end child process spawning ***************************/
