/*
 * Copyright (c) 1991 David A. Curry <davy@itstd.sri.com>
 * Copyright (c) 1996 Michael J. Hammel <mjhammel@csn.net>
 * Copyright (c) 1998 Guylhem Aznar <guylhem@oeil.qc.ca>
 * Copyright (c) 1998 Ingo Brueckl <ib@wupperonline.de>
 *
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * util.c - utility routines.
 * 
 * You can get David A. Curry's original public domain version on :
 * ftp://ftp.ers.ibm.com/pub/davy/xpostit3.3.2.tar.gz
 *      
 */

#include <X11/StringDefs.h>
#include <X11/Intrinsic.h>
#include <X11/Xaw/AsciiText.h>
#include <X11/Xaw/Form.h>
#include <X11/Xaw/Box.h>
#include <X11/Xaw/Label.h>
#include <X11/Xaw/Command.h>
#include <X11/Shell.h>
#include <sys/param.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pwd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include "xpostit.h"

extern void ErrPopUp ();

#ifdef PROMPT_FOR_SAVE
static void SOEPopDown ();
static void SOEAccept ();
static void SOECancel ();
static Widget soe_widget;
#endif

/*
 * ByeBye - clean up and exit.
 */
void
ByeBye ()
{

#ifdef PROMPT_FOR_SAVE
  Window root, child;
  int root_x, root_y, child_x, child_y;
  unsigned int buttons;
  Widget soe_form, soe_label, soe_buttons[2];
  Arg args[15];
  int nargs;
  XtCallbackRec callbacks[2];
#endif

#ifndef PROMPT_FOR_SAVE
  /*
   * If saving notes is on, save all notes.
   */
  if (app_res.save_notes)
    {
      SaveAllNotes (False);
      app_res.save_notes = False;	/* bad hack, but ByeBye is called twice */
    }

  XtUnmapWidget (toplevel);
  XCloseDisplay (display);
  exit (0);

#else
  if (app_res.save_notes)
    {
      SaveAllNotes (False);

      XtUnmapWidget (toplevel);
      XCloseDisplay (display);
      exit (0);
    }
  /*
   * else - ask the user if they want to save the notes
   * this should be a compile time option.
   */
  else
    {
      /* get mouse location */
      XQueryPointer (display, XtWindow (toplevel), &root, &child,
		     &root_x, &root_y, &child_x, &child_y, &buttons);

      /*
       * Create a popup shell widget
       */
      nargs = 0;
      SetArg (XtNtitle, PostItSaveOnExitPrompt);
      SetArg (XtNx, root_x);
      SetArg (XtNy, root_y);
      soe_widget = XtCreatePopupShell (
					"SOEShell",
					transientShellWidgetClass,
					toplevel,
					args, nargs);

      /* the form inside which all other windows will be put */
      nargs = 0;
      SetArg (XtNborderWidth, 0);
      soe_form = XtCreateManagedWidget (
					 "SOEForm",
					 formWidgetClass,
					 soe_widget,
					 args, nargs);

      nargs = 0;
      SetArg (XtNborderWidth, 0);
      SetArg (XtNtop, XtChainTop);
      SetArg (XtNbottom, XtChainTop);
      SetArg (XtNleft, XtChainLeft);
      SetArg (XtNright, XtChainLeft);
      SetArg (XtNvertDistance, 0);
      SetArg (XtNhorizDistance, 0);
      SetArg (XtNdefaultDistance, 0);
      SetArg (XtNlabel, "Save Changed Notes?");
      soe_label = XtCreateManagedWidget (
					  "SOELabel",
					  labelWidgetClass,
					  soe_form,
					  args, nargs);

      /*
       * create the Yes/No command buttons 
       */
      bzero (callbacks, sizeof (callbacks));
      SetCallback (SOEAccept, NULL);
      nargs = 0;
      SetArg (XtNcallback, callbacks);
      SetArg (XtNlabel, "Yes");
      SetArg (XtNborderWidth, 0);
      SetArg (XtNtop, XtChainTop);
      SetArg (XtNbottom, XtChainTop);
      SetArg (XtNleft, XtChainLeft);
      SetArg (XtNright, XtChainLeft);
      SetArg (XtNvertDistance, 0);
      SetArg (XtNhorizDistance, 0);
      SetArg (XtNdefaultDistance, 0);
      SetArg (XtNfromVert, soe_label);
      soe_buttons[0] = XtCreateManagedWidget (
					       "SOEButtons",
					       commandWidgetClass,
					       soe_form,
					       args, nargs);

      bzero (callbacks, sizeof (callbacks));
      SetCallback (SOECancel, NULL);
      nargs = 0;
      SetArg (XtNcallback, callbacks);
      SetArg (XtNlabel, "No");
      SetArg (XtNborderWidth, 0);
      SetArg (XtNtop, XtChainTop);
      SetArg (XtNbottom, XtChainTop);
      SetArg (XtNleft, XtChainLeft);
      SetArg (XtNright, XtChainLeft);
      SetArg (XtNvertDistance, 0);
      SetArg (XtNhorizDistance, 0);
      SetArg (XtNdefaultDistance, 0);
      SetArg (XtNfromVert, soe_label);
      SetArg (XtNfromHoriz, soe_buttons[0]);
      soe_buttons[1] = XtCreateManagedWidget (
					       "SOEButtons",
					       commandWidgetClass,
					       soe_form,
					       args, nargs);

      XtPopup (soe_widget, XtGrabNonexclusive);
    }
#endif

}

char *
PutHome (const char *path_with_home)
{
  char *home;			/* the HOME environment variable */
  char *realpath;

  /* home dir ? */
  if (!strncmp (path_with_home, "~/", 2))
    {
      /* get home */
      if ((home = getenv ("HOME")) == NULL)
	home = "./";
      /* alloc it */
      realpath = (char *) safemalloc (strlen (home) +
				      strlen (path_with_home) + 1
	);
      strcpy (realpath, home);
      strcat (realpath, "/");
      strcat (realpath, path_with_home + 2);
    }
  else
    {
      realpath = (char *) safemalloc (strlen (path_with_home) + 1);
      strcpy (realpath, path_with_home);
    }

  return (realpath);
}

#ifdef PROMPT_FOR_SAVE
/*
 * SOECancel - exit without saving notes
 */
void
SOECancel (w, client_data, call_data)
     caddr_t client_data, call_data;
     Widget w;
{
  SOEPopDown ();
  XtUnmapWidget (toplevel);
  XCloseDisplay (display);
  exit (0);
}

/*
 * SOEAccept - saving notes and then exit
 */
void
SOEAccept (w, client_data, call_data)
     caddr_t client_data, call_data;
     Widget w;
{
  SOEPopDown ();
  SaveAllNotes (False);
  XtUnmapWidget (toplevel);
  XCloseDisplay (display);
  exit (0);
}

/*
 * SOEPopDown - removes the Save On Exit Dialog box
 */
void
SOEPopDown ()
{
  XtPopdown (soe_widget);
  XtDestroyWidget (soe_widget);

}
#endif

/*
 * SetNoteDir - determine the path to the note directory.
 */
void
SetNoteDir ()
{
  char *getenv ();
  char *home;
  struct passwd *pwd;
  struct stat *stat_buf;
  int rc;

  /*
   * If it's an absolute path name,
   * we're done.
   */
  if (app_res.note_dir[0] != '/')
    {

      /*
       * Find the user's home directory.
       */
      if ((home = getenv ("HOME")) == NULL)
	{
	  if ((pwd = getpwuid (getuid ())) == NULL)
	    {
	      fprintf (stderr, "xpostit: who are you?\n");
	      exit (1);
	    }
	}

      /*
       * Get the real path.
       */
      app_res.note_dir = PutHome (app_res.note_dir);
    }

  /* now we have the name of the directory pointed at by app_res.note_dir
   * so check if it exists, and if it doesn't, try to create it
   */

  stat_buf = (struct stat *) malloc (sizeof (struct stat));
  if ((rc = stat (app_res.note_dir, stat_buf)) != 0)
    {
      switch (errno)
	{
	case EACCES:
	  printf ("Search permission denied for portion of path");
	  printf (" leading to %s\n", app_res.note_dir);
	  printf ("Aborting.\n");
	  free (stat_buf);
	  exit (-1);
	  break;
	case ENOTDIR:
	  printf ("A portion of the pathname to %s", app_res.note_dir);
	  printf (" is invalid\n");
	  printf ("Aborting.\n");
	  free (stat_buf);
	  exit (-1);
	  break;
	case ENOENT:
	  /* if it doesn't exist, try to create it */
	  printf ("%s: no such file or directory\n", app_res.note_dir);
	  printf ("Trying to create.\n");
	  rc = mkdir (app_res.note_dir, 0755);
	  if (rc != 0)
	    {
	      printf ("Can't create %s\n; Aborting", app_res.note_dir);
	      free (stat_buf);
	      exit (-1);
	    }
	  break;
	default:
	  printf ("Unknown error (%d) while stat()'ing %s\n", errno,
		  app_res.note_dir);
	  exit (-1);
	  break;
	}
    }

}

/*
 * SetTempDir - determine the path to the temporary directory.
 */
void
SetTempDir ()
{
  char *getenv ();
  char *home;
  struct passwd *pwd;
  struct stat *stat_buf;
  int rc;

  /*
   * If it's an absolute path name,
   * we're done.
   */
  /*printf("Temp dir : [%s]\n", app_res.tmp_dir);*/
  if (app_res.tmp_dir[0] != '/')
    {

      /*
       * Find the user's home directory.
       */
      if ((home = getenv ("HOME")) == NULL)
	{
	  if ((pwd = getpwuid (getuid ())) == NULL)
	    {
	      fprintf (stderr, "xpostit: who are you?\n");
	      exit (1);
	    }
	}

      /*
       * Get the real path.
       */
      app_res.tmp_dir = PutHome (app_res.tmp_dir);
    }
  /*printf("Temp dir : [%s]\n", app_res.tmp_dir);*/

  /* now we have the name of the directory pointed at by app_res.temp_dir
   * so check if it exists, and if it doesn't, try to create it
   */

  stat_buf = (struct stat *) malloc (sizeof (struct stat));
  if ((rc = stat (app_res.tmp_dir, stat_buf)) != 0)
    {
      switch (errno)
	{
	case EACCES:
	  printf ("Search permission denied for portion of path");
	  printf (" leading to %s\n", app_res.tmp_dir);
	  printf ("Aborting.\n");
	  free (stat_buf);
	  exit (-1);
	  break;
	case ENOTDIR:
	  printf ("A portion of the pathname to %s", app_res.tmp_dir);
	  printf (" is invalid\n");
	  printf ("Aborting.\n");
	  free (stat_buf);
	  exit (-1);
	  break;
	case ENOENT:
	  /* if it doesn't exist, try to create it */
	  printf ("%s: no such file or directory\n", app_res.tmp_dir);
	  printf ("Trying to create.\n");
	  rc = mkdir (app_res.tmp_dir, 0755);
	  if (rc != 0)
	    {
	      printf ("Can't create %s\n; Aborting", app_res.tmp_dir);
	      free (stat_buf);
	      exit (-1);
	    }
	  break;
	default:
	  printf ("Unknown error (%d) while stat()'ing %s\n", errno,
		  app_res.tmp_dir);
	  exit (-1);
	  break;
	}
    }

}

/*
 * MakeFname - make a file name from a note index number.
 */
char *
MakeFname (index)
     register int index;
{
  char *s;
  char fname[MAXPATHLEN];

  sprintf (fname, "%s/%s%d", app_res.note_dir, PostItNoteFname, index);
  s = SafeAlloc (strlen (fname) + 1);
  strcpy (s, fname);
  return (s);
}

/*
 * SafeAlloc - allocate n bytes of memory, exit if we run out.
 */
char *
SafeAlloc (nbytes)
     register int nbytes;
{
  register char *s;

  if ((s = (char *) malloc (nbytes)) == NULL)
    {
      fprintf (stderr, "xpostit: out of memory.\n");
      exit (1);
    }

  bzero (s, nbytes);
  return (s);
}

/*
 * AutoSave - save all notes automatically when timer expires
 */
void
AutoSave (client_data, id)
     XtPointer client_data;
     XtIntervalId *id;
{
  XtIntervalId timer;

  SaveAllNotes (False);
  timer = XtAppAddTimeOut (
			    XtWidgetToApplicationContext (toplevel),
			    timer_interval,
			    AutoSave,
			    NULL);
}

/*
 * Generic routine for inserting text into a note from a file
 */
void
InsertText (
	     PostItNote * pn,
	     char *tmpfile,
	     int remove,
	     int type
)
{
  Arg args[3];
  int nargs, position;
  int fp;
  char *buf;
  XawTextBlock text_block;
  struct stat stat_buf;
  char cmd[1024];

  /*
   * get the position of the cursor in the note
   */
  nargs = 0;
  SetArg (XtNinsertPosition, &position);
  XtGetValues (pn->pn_textwidget, args, nargs);

  if (type == FILE_TYPE)
    {

      /*
       * insert the text into the note at the cursor location
       */
      if (stat (tmpfile, &stat_buf) != 0)
	{
	  sprintf (cmd, "Can't stat file: %s", tmpfile);
	  ErrPopUp (cmd);
	  if (remove)
	    {
	      sprintf (cmd, "rm %s", tmpfile);
	      system (cmd);
	    }
	  return;
	}
      if ((fp = open (tmpfile, O_RDONLY)) == -1)
	{
	  fprintf (stderr, "xpostit: ");
	  perror (tmpfile);
	  sprintf (cmd, "Can't open file for\nreading: %s", tmpfile);
	  ErrPopUp (cmd);
	  if (remove)
	    {
	      sprintf (cmd, "rm %s", tmpfile);
	      system (cmd);
	    }
	  return;
	}
      buf = (char *) SafeAlloc ((int) stat_buf.st_size + 2);
      while (read (fp, buf, (int) stat_buf.st_size) != 0)
	{
	  text_block.firstPos = 0;
	  text_block.length = strlen (buf);
	  text_block.ptr = buf;
	  text_block.format = FMT8BIT;
	  if (XawTextReplace (pn->pn_textwidget, (long) position,
			      (long) position, &text_block) != XawEditDone)
	    {
	      ErrPopUp ("Can't add text from file to note!!");
	      free (buf);
	      close (fp);
	      return;
	    }
	  position += strlen (buf);
	}
      close (fp);
      free (buf);

      /*
       * cleanup
       */
      if (remove)
	{
	  sprintf (cmd, "rm %s", tmpfile);
	  system (cmd);
	}
    }
  else
    {
      /*
       * "tmpfile" is actually a text buffer that we want to insert.
       * This was added as an afterthought (does it show?)
       */
      text_block.firstPos = 0;
      text_block.length = strlen (tmpfile);
      text_block.ptr = tmpfile;
      text_block.format = FMT8BIT;
      if (XawTextReplace (pn->pn_textwidget, (long) position,
			  (long) position, &text_block) != XawEditDone)
	{
	  ErrPopUp ("Can't add text from buffer to note!!");
	  return;
	}
    }
}
