/* $Id: callback.c,v 1.3 92/12/18 17:54:36 bert Exp */

/*****   Callbacks, actions and registrations.  *****/

#include <stdio.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <pwd.h>
#include <hesiod.h>

#include <X11/Intrinsic.h>
#include <X11/Wc/WcCreate.h>

#include <X11/Xaw/Viewport.h>
#include "OptionList.h"
#include <com_err.h>

#include "q.h"
#include "callback.h"
#include "main.h"

/**** local variables ****/

static char whoami[] = "XQ";

Boolean dir_changed = FALSE;
Boolean stat_this_dir = FALSE;
Files files = (Files)NULL;
int num_files, curr;
char *current_dir;
struct stat *file_stat;
Boolean *stat_ok;

/**** "temporary" stuff (to be nuked away soon) ****/

static char* foobar[] = {"bert", "bert:me", "bert:myself", "bert:i", NULL};

/**** CB/ACT declarations ****/

static void XqUpdateDirACT(), XqSetFileLabelsACT();

/**** sanity wrappers, abstraction barriers, etc. ****/

static Widget
XqFullNameToWidget (widget, widgetName)
     Widget widget;
     char* widgetName;
{
  Widget fnw = WcFullNameToWidget (widget, widgetName);
  if (fnw == (Widget)NULL) {
    printf("%s: app-defaults corrupt (cannot find widget '%s')\n",
	   whoami, widgetName);
    exit(1);
  }
  return (fnw);
}

static int
XqSelectedFile (widget)
     Widget widget;
{
  int idx;
  XawOptionListReturnStruct *ret =
    (XawOptionListReturnStruct*) XawOptionListShowCurrent (widget);

  if (ret != (XawOptionListReturnStruct*)NULL)
    idx = ret->list_index;
  else
    idx = XAW_LIST_NONE;

  XtFree ((XtPointer) ret);
  return idx;
}

#define XqRemoveTrailingComponent(path) \
    *((char*)rindex(path,'/')) = '\0';   if (path[0] == '\0') strcpy(path,"/");

#define XqAppendTrailingComponent(path,trail) \
    if (strcmp(path,"/")) strcat(path,"/");    strcat(path, trail);

static void
XqStatDirectory(lw)
     Widget lw;
{
  char fpath[MAXPATHLEN];
  char *fnptr;
  int i;
  Widget vw;

  file_stat = (struct stat*) Q_realloc (file_stat,
					num_files*sizeof(struct stat));
  stat_ok = (Boolean*) Q_realloc (stat_ok, num_files*sizeof(Boolean));

  if (stat_this_dir) {
    strcpy (fpath, current_dir);
    strcat (fpath, "/");
    for ( fnptr = &fpath[0] ; *fnptr != '\0' ; fnptr++ );

    for (i=0; i<num_files; i++) {
      strcpy(fnptr, files[i]);
      stat_ok[i] = TRUE;

      if (lstat(fpath, &file_stat[i])) {
	if (errno == EACCES) {
	  stat_ok[i] = FALSE;
	  file_stat[i].st_mode = 0;
	}
	else {
	  com_err(whoami, errno, "while trying to stat '%s'\n", fpath);
	  exit (1);
	}
      }
    }
    stat_this_dir = FALSE;
  }
  else {
    for (i=0; i<num_files; i++) {
      stat_ok[i] = TRUE;
      file_stat[i].st_mode = 0;
    }
  }

  XawOptionListChange(lw, files, stat_ok, 0, 0, TRUE);
  XawOptionListHighlight(lw, curr);
  vw = XtParent(lw);
  if (XtIsSubclass(vw,viewportWidgetClass))
    XawViewportSetCoordinates(vw, 0, 0);
}

#define XqFileMode(i) file_stat[i].st_mode
#define XqFileSize(i) file_stat[i].st_size
#define XqFileUid(i)  file_stat[i].st_uid
#define XqFileGid(i)  file_stat[i].st_gid

static int
XqStatCurrentFile(lw)
     Widget lw;
{
  char fpath[MAXPATHLEN];

  if (stat_ok[curr]) {
    if (file_stat[curr].st_mode == 0) {
      strcpy (fpath, current_dir);
      strcat (fpath, "/");
      strcat (fpath, files[curr]);

      if (file_stat[curr].st_mode == 0) {
	if (lstat(fpath, &file_stat[curr])) {
	  if (errno == EACCES) {
	    stat_ok[curr] = FALSE;
	    file_stat[curr].st_mode = 0;
	    XawOptionListChange(lw, files, stat_ok, 0, 0, TRUE);
	    XawOptionListUnhighlight(lw);
	    return 0;
	  }
	  else {
	    com_err(whoami, errno, "while trying to stat '%s'\n", fpath);
	    exit (1);
	  }
	}
      }
    }
    return 1;
  }
  else
    return 0;
}

static char*
XqFileType (mode, fname)
     mode_t mode;
     char* fname;
{
  /* note that S_ISLNK and S_ISSOCK must come before S_ISREG */

  if (S_ISLNK(mode)) return ("Symbolic link");
  else if (S_ISSOCK(mode)) return ("Socket");
  else if (S_ISREG(mode))  return ("File");
  else if (S_ISDIR(mode)) return ("Directory");
  else if (S_ISCHR(mode)) return ("Character-special");
  else if (S_ISBLK(mode)) return ("Block-special");
  else if (S_ISFIFO(mode)) return ("FIFO");
  else {
    printf("%s: unknown file type (%o) for file '%s'\n", whoami, mode, fname);
    exit(1);
  }
}

static char*
XqGetUsername(uid)
     uid_t uid;
{
  char* name;
  struct passwd* passwd_entry = getpwuid(uid);

  if ((passwd_entry == NULL)&&((passwd_entry = hes_getpwuid(uid))== NULL)) {
    name = (char*) Q_malloc(8);       /* max 5 digits in an UID */
    sprintf(name, "(%d)", uid);
  }
  else {
    name = (char*) Q_malloc(strlen(passwd_entry->pw_name)+1);
    strcpy(name, passwd_entry->pw_name);
    free(passwd_entry);
  }
  return name;
}

static int
XqShouldIStat()
{
/* this will hopefully get a meaningful user interface... */

  return (strcmp(current_dir,"/afs")
	  && strcmp(current_dir,"/afs/sipb.mit.edu/project")
	  && strcmp(current_dir,"/afs/sipb/project"));
}

/**** callbacks ****/

/* ARGSUSED */
static void
XqDirSetupCB( widget, argval, ignored )
     Widget  widget;
     char*   argval;
     caddr_t ignored;
{
  current_dir = (char*) Q_malloc (MAXPATHLEN * sizeof(char));
  if (getwd(current_dir) == 0) {
    printf("%s: can't get the current directory (%s)\n", whoami, current_dir);
    exit(1);
  }

  dir_changed = TRUE;
  files = (Files) NULL;
  curr = 0;

  WcInvokeAction((XtActionProc) &XqUpdateDirACT, widget, argval);
}

/* ARGSUSED */
static void
XqFileSetupCB( widget, argval, ignored )
     Widget  widget;
     char*   argval;
     caddr_t ignored;
{
  WcInvokeAction((XtActionProc) &XqSetFileLabelsACT, widget, argval);
}

/* ARGSUSED */
static void
XqNukeWidgetTreeCB( widget, widgetName, ignored )
     Widget  widget;
     char*   widgetName;
     caddr_t ignored;
{
  Widget nuke_me = WcFullNameToWidget( widget, widgetName );

  if (nuke_me == (Widget) NULL)
    XtDestroyWidget( appShell );
  else
    XtDestroyWidget( nuke_me );
}

/* ARGSUSED */
static void
XqFileListFreeCB( widget, widgetName, ignored )
     Widget  widget;
     char*   widgetName;
     caddr_t ignored;
{
  Q_free_directory (files);
  Q_free ((void*) file_stat);
  Q_free ((void*) stat_ok);
  Q_free ((void*) current_dir);

  exit(0);    /* nothing to stay around for */
}

/* ARGSUSED */
static void
XqStatCurrentDirCB( widget, fwName, ignored )
     Widget  widget;
     char*   fwName;
     caddr_t ignored;
{
  Widget fw = XqFullNameToWidget (widget, fwName);

  if (! stat_this_dir) {
    stat_this_dir = TRUE;
    XqStatDirectory(fw);
  }
}

static void
XqGloopCB (widget, fwName, ignored)
     Widget  widget;
     char*   fwName;
     caddr_t ignored;
{
  Widget fw = XqFullNameToWidget (widget, fwName);
  XawOptionListChange(fw, foobar, NULL, 0, 0, TRUE);
}

/**** actions ****/

/* ARGSUSED */
static void
XqSetFileLabelsACT(widget, event, params, num_params)
     Widget widget;
     XEvent * event;
     String * params;
     Cardinal *num_params;
{
  Widget fw;
  int ncurr;
  static Arg args[1];

  if ((*num_params < 3)||(*num_params > 4)) {
    printf("%s: app-defaults corrupt (%d args to XqSetFileLabels)\n",
	   whoami, *num_params);
    exit(1);
  }

  if (*num_params == 3)
    fw = widget;
  else
    fw = XqFullNameToWidget (widget, params[3]);

  /* If a valid item is selected, change curr */

  ncurr = XqSelectedFile(fw);
  if (ncurr != XAW_LIST_NONE)
    curr = ncurr;

  if (XqStatCurrentFile(fw)) {
    char cf[MAXPATHLEN];
    char *tmp;

    fw = XqFullNameToWidget (widget, params[0]);

    /* set currentFile w/file type */

    fw = XqFullNameToWidget (widget, params[0]);

    (void) sprintf (cf, "%s:  %s",
		    XqFileType(XqFileMode(curr), files[curr]), files[curr] );

    XtSetArg(args[0], "label", (XtArgVal) &cf );
    XtSetValues(fw, args, 1);

    /* set current fileSize */

    fw = XqFullNameToWidget (widget, params[1]);

    sprintf(cf, "Size:  %d bytes", XqFileSize(curr));

    XtSetArg(args[0], "label", (XtArgVal) cf );
    XtSetValues(fw, args, 1);

    /* set current createdBy */

    fw = XqFullNameToWidget (widget, params[2]);

    tmp = XqGetUsername(XqFileUid(curr));
    sprintf(cf, "Created by:  %s", tmp);
    Q_free(tmp);

    XtSetArg(args[0], "label", (XtArgVal) cf );
    XtSetValues(fw, args, 1);
  }
}

/* ARGSUSED */
static void
XqChDirACT(widget, event, params, num_params)
     Widget widget;
     XEvent * event;
     String * params;
     Cardinal *num_params;
{
  int ncurr = XqSelectedFile(widget);
  if (ncurr != XAW_LIST_NONE)
    curr = ncurr;

  if (XqStatCurrentFile(widget)) {

    if (!strcmp(files[curr],"."))
      return;
    else if (!strcmp(files[curr],"..")) {
      XqRemoveTrailingComponent (current_dir);
      dir_changed = TRUE;
    }
    else if (S_ISDIR(XqFileMode(curr))) {
      XqAppendTrailingComponent (current_dir, files[curr]);
      dir_changed = TRUE;
    }
  }
}

/* ARGSUSED */
static void
XqUpdateDirACT(widget, event, params, num_params)
     Widget widget;
     XEvent * event;
     String * params;
     Cardinal *num_params;
{
  Widget fw;
  static Arg args[1];
  Files old_files;

  if (dir_changed) {
    if (*num_params != 2) {
      printf("%s: app-defaults corrupt (%d args to XqUpdateDir)\n",
	     whoami, *num_params);
      exit(1);
    }
  
    /* set currentDirectory */

    fw = XqFullNameToWidget (widget, params[0]);

    XtSetArg(args[0], "label", (XtArgVal) current_dir );
    XtSetValues(fw, args, 1);

    /* set fileNames for current directory */

    fw = XqFullNameToWidget (widget, params[1]);

    old_files = files;
    num_files = Q_get_directory(current_dir, &files);

    curr = 0;
    stat_this_dir = XqShouldIStat();
    XqStatDirectory(fw);  /* this also updates the file list on the widget */
    if (! XqStatCurrentFile(fw)) {
      printf("%s: Directory is fascist (can't stat .)\nCheck your tickets.\n",
	     whoami);
      exit(1);
    }

    Q_free ((void*) old_files);
    dir_changed = FALSE;
  }
}

/**** callback/action registration 'n' stuff ****/

void
RegisterStuff ( app )
     XtAppContext app;
{
#define RCB( name,func ) WcRegisterCallback(app,name,(XtCallbackProc)func,NULL)
#define RACT( name,func ) WcRegisterAction  (app, name, (XtActionProc)func)
#define RCN( name,class ) WcRegisterClassName ( app, name, class )
#define RCP( name,class ) WcRegisterClassPtr  ( app, name, class )

  AriRegisterAthena(app);

  RCN("OptionList",                 optionListWidgetClass);
  RCP("optionListWidgetClass",      optionListWidgetClass);

  RCB( "XqNukeWidgetTree",             XqNukeWidgetTreeCB);
  RCB( "XqDirSetup",                   XqDirSetupCB);
  RCB( "XqFileSetup",                  XqFileSetupCB);
  RCB( "XqFileListFree",               XqFileListFreeCB);
  RCB( "XqStatCurrentDir",             XqStatCurrentDirCB);
  RCB( "XqGloopCB",                    XqGloopCB);

  RACT( "XqSetFileLabels",             XqSetFileLabelsACT);
  RACT( "XqChDir",                     XqChDirACT);
  RACT( "XqUpdateDir",                 XqUpdateDirACT);

#undef RCB
#undef RACT
#undef RCN
#undef RCP
}

void
XqSetUpStuff ( app )
     XtAppContext app;
{
  RegisterStuff (app);
}
