/*
 * $Source: /mit/sipb/src/src/xscreensaver/RCS/main.c,v $
 * $Author: ghudson $
 *
 * This file is part of xscreensaver.  It contains the code for main()
 * and a few other procedures used by main.
 *
 * Author: Jonathan Kamens, MIT Project Athena and
 *                          MIT Student Information Processing Board
 *
 * Copyright (c) 1989 by Jonathan Kamens.  This code may be
 * distributed freely as long as this notice is kept intact in its
 * entirety and every effort is made to send all corrections and
 * improvements to the code back to the author.  Also, don't try to
 * make any money off of it or pretend that you wrote it.
 */

#if !defined(lint) && !defined(SABER)
     static char rcsid_main_c[] = "$Header: /mit/sipb/src/src/xscreensaver/RCS/main.c,v 1.3 96/01/28 01:55:39 ghudson Exp $";
#endif

#include "xsaver.h"
#include <sys/types.h>
#include <signal.h>
/* time.h and resource.h are for the setrlimit in the error handlers */
#include <sys/time.h>
#include <sys/resource.h>
#if defined(_AIX) && defined(_IBMR2)
#include <sys/id.h>
#endif
#if defined(__svr4__) || defined(POSIX)
#include <unistd.h>
#endif
#include "resources.h"
#include "commLine.h"
#include "getstring.h"
#define MAIN
#include "globals.h"
#include "action.h"

#ifdef __hpux
/* RLIMIT_CORE is "unsupported BSD stuff" in HP-UX, so this may not always
 * work. */
#define RLIMIT_CORE	4
#endif

#ifdef XFILESEARCHPATH
static void AddPathToSearchPath();
#endif

#ifndef APPCLASS
#define APPCLASS "Xsaver"
#endif

extern char *get_passwd(), *getenv();
extern void build_menu(), build_icon(), build_root(), init_scaling();
#ifdef _IBMR2
extern void enable_hft_hotkey();
#endif
#ifdef PASSWORD_PORT
extern void check_password_port();
#endif

Boolean signal_caught = False;


handler(dpy, event)
Display *dpy;
XErrorEvent *event;
{
     char buf[80];
     struct rlimit limits;
     
     fprintf(stderr, "%s: X error occurred; aborting with core dump in /tmp.\n",
	     whoami);
     if (debug_file) {
	  fprintf(debug_file, "X error occurred, type %d\n", event->type);
	  fprintf(debug_file, "  serial number = %u\n", event->serial);
	  XGetErrorText(dpy, event->error_code, buf, 80);
	  fprintf(debug_file, "  error_code = %u\n (%s)", event->error_code,
		  buf);
	  fprintf(debug_file, "  request_code = %u\n", event->request_code);
	  fprintf(debug_file, "  minor_code = %u\n", event->minor_code);
	  fprintf(debug_file, "  resource ID = %u\n", event->resourceid);
     }
     chdir("/tmp");
#ifdef RLIMIT_CORE
     getrlimit(RLIMIT_CORE, &limits);
     limits.rlim_cur = limits.rlim_max;
     setrlimit(RLIMIT_CORE, &limits);
#endif
     clean_up_files();
     abort();
}


/*ARGSUSED*/
IOhandler(dpy)
Display *dpy;
{
#ifdef _IBMR2
     if (defs.disable_hotkey)
	  enable_hft_hotkey();
#endif
     clean_up_files();
     if (defs.debug) {
	  struct rlimit limits;

	  fprintf(stderr,
		 "%s: X IO error occurred; aborting with core dump in /tmp.\n",
		  whoami);
	  if (debug_file) {
	       fprintf(debug_file, "X IO error occurred\n");
	       fclose(debug_file);
	  }
	  chdir("/tmp");
	  getrlimit(RLIMIT_CORE, &limits);
	  limits.rlim_cur = limits.rlim_max;
	  setrlimit(RLIMIT_CORE, &limits);
	  abort();
     }
     else {
	  exit(1);
     }
}
     



SIGTYPE clean_up()
{
     wait(0);
}



clean_up_files()
{
     char buf[50];

     if (dpy && DisplayString(dpy)) {
	  sprintf(buf, "/tmp/%s.%s.pid", APPCLASS, DisplayString(dpy));
	  unlink(buf);
     }
     if (debug_file) {
	  fclose(debug_file);
	  debug_file = 0;
     }
}



clean_up_and_die(val)
int val;
{
     clean_up_files();

     exit(val);
}



SIGTYPE signal_die()
{
#ifdef _IBMR2
     if (defs.disable_hotkey)
	  enable_hft_hotkey();
#endif
     clean_up_and_die(0);
}

     

/*
 * Commenting this out until we can get the signal stuff to work
 * properly.  May end up needing a change in the toolkit :-)
 */
#ifdef NOTDEF
void SetSignal()
{     
     signal_caught = True;
}


/* ARGSUSED */
Boolean CheckSignal(garbage)
caddr_t garbage;
{
     if (signal_caught) {
	  lock_flag = True;
	  signal_caught = False;
	  ActivateRoot();
	  return(True);
     }
     return(False);
}
#endif






#ifndef XlibSpecificationRelease
main(int_argc, argv)
int int_argc;
#else
main(argc, argv)
int argc;
#endif
char *argv[];
{
     int child;
     FILE *pid_file;
     char pid_file_name[50];
     char debug_file_name[50];
     char error_buf[50];
     XrmDatabase db;
     XrmValue allBitmap;
     char bitmap_inst_buf[50];
     char bitmap_class_buf[50];
     char *rep_type;
#ifdef PASSWORD_PORT
     int password_port;
#endif
#ifndef XlibSpecificationRelease
     Cardinal argc = int_argc;
#endif
     char *encrypted_key;

     encrypted_key = get_passwd();
#if defined(_AIX) && defined(_IBMR2)
     setgidx(ID_EFFECTIVE, getgidx(ID_REAL));
     setuidx(ID_EFFECTIVE, getuidx(ID_REAL));
#endif
#if defined(__svr4__) && defined(__sun__)
     /* setuid/setgid will set the real, effective and saved IDs, not */
     /* just the real Id returned from getuid/getgid		      */
     setgid(getgid());
     setuid(getuid());
#endif
     whoami = ((whoami = rindex(argv[0], '/')) ? whoami + 1 : argv[0]);
#ifdef XFILESEARCHPATH
     AddPathToSearchPath(XFILESEARCHPATH);
#endif

     srandom((int) time((long *)0));
     csignal(SIGCHLD, clean_up);
     csignal(SIGINT, signal_die);
     csignal(SIGTERM, signal_die);
     csignal(SIGHUP, signal_die);
     csignal(SIGQUIT, signal_die);

#ifdef _IBMR2
     add_converter();
#endif

     top_widget = XtVaAppInitialize(&app_context,
				    APPCLASS,
				    app_options,
				    XtNumber(app_options),
				    &argc,
				    argv,
				    NULL,	/* fallback resources */
				    NULL);	/* args */

     if (argc != 1) {
	  usage();
	  exit(1);
     }
	  
     dpy = XtDisplay(top_widget);
     screen = DefaultScreen(dpy);
     real_root_window = RootWindow(dpy, screen);
     display_height = DisplayHeight(dpy, screen);
     display_width = DisplayWidth(dpy, screen);

     /*
      * If the .allBitmap resource is set, then we want to put it back
      * into the database as *float.bitmap and *icon.bitmap so that
      * when widgets get built the command-line bitmap will override
      * anything in the resources.
      */
     db = XtDatabase(dpy);
     sprintf(bitmap_inst_buf, "%s.allBitmap", whoami);
     sprintf(bitmap_class_buf, "%s.AllBitmap", APPCLASS);
     if (XrmGetResource(db, bitmap_inst_buf, bitmap_class_buf,
			&rep_type, &allBitmap)) {
	  sprintf(bitmap_inst_buf, "%s*float.bitmap", whoami);
	  XrmPutStringResource(&db, bitmap_inst_buf, allBitmap.addr);
	  sprintf(bitmap_inst_buf, "%s*icon.bitmap", whoami);
	  XrmPutStringResource(&db, bitmap_inst_buf, allBitmap.addr);
     }
     
     XtAppAddActions(app_context, actionTable, XtNumber(actionTable));

     XtGetApplicationResources(top_widget, &defs, app_resources,
			       XtNumber(app_resources), NULL, 0);

     /*
      * The strings in the defaults structure are not guaranteed to be
      * allocated strings, but we want to be able to free them at
      * will, so we'll turn them into allocated strings here.
      */
     if (*defs.key) {
	  char *tmp = XtNewString(defs.key);
	  bzero(defs.key, strlen(defs.key));
	  defs.key = tmp;
     }
     else {
	  defs.key = 0;
     }
     
     defs.ekey = *defs.ekey ? XtNewString(defs.ekey) : 0;
     defs.lock_message =
	  *defs.lock_message ? XtNewString(defs.lock_message) : 0;
     defs.lock_command =
	  *defs.lock_command ? XtNewString(defs.lock_command) : 0;
     defs.unlock_command =
	  *defs.unlock_command ? XtNewString(defs.unlock_command) : 0;
     defs.startup_message =
	  *defs.startup_message ? XtNewString(defs.startup_message) : 0;
     defs.logout_command =
	  *defs.logout_command ? XtNewString(defs.logout_command) : 0;
     
     if (!defs.ekey && defs.use_passwd  && !defs.key)
	  defs.ekey = XtNewString(encrypted_key);
     else if (!defs.ekey && defs.key)
	  install_password();

     if (defs.debug) {
	  XSetErrorHandler(handler);
     }
     XSetIOErrorHandler(IOhandler);
     if (defs.velocity <= 0) {
	  fprintf(stderr, "%s: Velocity must be greater than 0.\n",
		  whoami);
	  defs.velocity = 1;
     }
     
     init_getstring();
     
#ifdef MAXTIMEOUT
     if ((defs.timeout > MAXTIMEOUT) || (defs.timeout == 0)) {
	  fprintf(stderr, "%s: Timeout cannot be greater than %d minutes.\n",
		  whoami, MAXTIMEOUT);
	  defs.timeout = MAXTIMEOUT;
     }
#endif
     
     /* Init_scaling calculates various screen dimension proportion */
     /* necessary for the later calculation of the timeouts used for */
     /* the floating Form widget. */
     init_scaling();

/*
 * Commenting this out until we can figure out how to get it to work right.
     signal (SIGUSR1, SetSignal);
     XtAppAddWorkProc(app_context, CheckSignal, NULL);
*/

     if (defs.startup_message)
	  fprintf(stdout, "%s\n", defs.startup_message);

     if (! defs.no_fork) if ((child = fork()) == -1) {
	  sprintf(error_buf, "%s: forking", whoami);
	  perror(error_buf);
	  exit(1);
     }
     else if (child)
	  exit(0);

     sprintf(pid_file_name, "/tmp/%s.%s.pid", APPCLASS, DisplayString(dpy));
     if (pid_file = fopen(pid_file_name, "w")) {
	  fprintf(pid_file, "%d\n", getpid());
	  fclose(pid_file);
     }

     if (defs.debug) {
	  sprintf(debug_file_name, "/tmp/%s.%s.debug", APPCLASS,
		  DisplayString(dpy));
	  debug_file = fopen(debug_file_name, "w");
     }
     else
	  debug_file = NULL;
     
#ifdef PASSWORD_PORT
     /* Set up the stuff for listening for the password on a port */
     password_port = initialize_password_port();
     if (password_port > 0)
	  XtAppAddInput(app_context, password_port,
			(XtPointer) XtInputReadMask, check_password_port,
			(XtPointer) NULL);
#endif
     
     if (defs.display_icon) {
	  build_icon();
	  build_menu();

	  XtRealizeWidget(top_widget);
     }
     else {
	  build_root();
	  lock_flag = defs.start_locked || defs.auto_lock;
	  ActivateRoot();
     }
     
     XtAppMainLoop(app_context);
} /* main */



#ifdef XFILESEARCHPATH
static void
AddPathToSearchPath(path)
char *path;
{
     char *old, *new;

     old = getenv("XFILESEARCHPATH");
     if (old) {
#if defined(mips) || defined(hpux) || defined(sun) || \
	  (defined(ibm) && defined(SYSV) && defined(i386))
	  /* +1 for =, +2 for :, +3 for null */
	  new = XtMalloc((Cardinal) (strlen("XFILESEARCHPATH") +
				     strlen(old) +
				     strlen(path) + 3));
	  (void) strcpy(new, "XFILESEARCHPATH");
	  (void) strcat(new, "=");
	  (void) strcat(new, old);
	  (void) strcat(new, ":");
	  (void) strcat(new, path);
	  putenv(new);
#else
	  /* +1 for colon, +2 for null */
	  new = XtMalloc((Cardinal) (strlen(old) + strlen(path) + 2));
	  (void) strcpy(new, old);
	  (void) strcat(new, ":");
	  (void) strcat(new, path);
	  setenv("XFILESEARCHPATH", new, 1);
#endif
     }
     else {
#if defined(mips) || defined(hpux) || defined(sun) || \
	  (defined(ibm) && defined(SYSV) && defined(i386))
	  new = XtMalloc((Cardinal) (strlen("XFILESEARCHPATH") +
				     strlen(path) + 2));
	  (void) strcpy(new, "XFILESEARCHPATH");
	  (void) strcat(new, "=");
	  (void) strcat(new, path);
	  putenv(new);
#else
	  setenv("XFILESEARCHPATH", path, 1);
#endif
     }
}
#endif


usage()
{
     fprintf(stderr, "Usage: %s [toolkit options] [-B] [-v velocity] [-l] [-b bitmapfile]\n\t[-t timeout] [-xsaver] [+/-dtime] [+/-delapsed] [+/-dtimeout]\n\t[+/-dtimes] [-key key] [-ekey key] [-npw] [-lc command | +lc]\n\t[-uc command | +uc] [-nofork] [-mb button] [-cl] [-L] [-m message]\n\t[-debug] [+/-icon]\n",
	     whoami);
#ifdef PASSWORD_PORT
     fprintf(stderr, "\t[-port port | +port] [+/-requirePort]\n");
#endif
#ifdef _IBMR2
     fprintf(stderr, "\t[+/-disableHotkey] [+/-requireHotkey]\n");
#endif

     return;
}

/*
 *  csignal() -- signal that works right on Posix systems.
 */
int csignal (sig, proc)
    int sig;
    SIGTYPE (*proc)();
{
#ifdef NO_SIGACTION
    return (signal (sig, proc));
#else
    struct sigaction act, oact;
    act.sa_handler = proc;
    sigemptyset (&act.sa_mask);
    act.sa_flags = 0;
#ifdef SA_RESTART
    act.sa_flags |= SA_RESTART;
#endif
    return (sigaction (sig, &act, &oact));
#endif
}
