/*************************************************************************

  rcd : 'rapid change directory', ou encore "R'alf change directory" !
  
  rcd.c
  essai de curses.h / ncurses.h

  Source started the ??/06/93
  Source not finished yet, the 27/12/93 (merry xmas!)

  the fortune is "Please do not graph the r'alf".
  (r'alf stands for registered-alien-life-form [thanx Nounours]
   yeh! that's me !)

   Since this program was written using GNU under Linux, the copyright
   is set to R'alf (c) (tm) 1993 and you have the right to change, split,
   destroy, modify or simply copy the source and the executable file.
   The only think I expect you to do is to preserv my name and to send me
   back your modified version.
   When you copy it, don't forget that the package includes:
     an exec + a source (gnu c) + a doc + a man page + a profile example

*************************************************************************/

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <ncurses.h>
#include <dirent.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>

/*-----------------------------------------------------------------------*/

#ifdef TRUE
#undef TRUE
#endif
#ifdef FALSE
#undef FALSE
#endif
#define TRUE 1
#define FALSE 0

/* some hints to change the behavior */
#define MANAGE_LINKS
/* undef' the following line if you want to use an xterm... */
#define USES_DRAWING_LINES


#ifndef min
#define min(x,y) ((x) < (y) ? (x) : (y))
#endif
#ifndef max
#define max(x,y) ((x) < (y) ? (y) : (x))
#endif

typedef struct s_dirEntry
{
  char *name;
  char *linkName;
  struct s_dirEntry *left, *right, *up, *down;
  int x,y;
} dirEntry;

/*-----------------------------------------------------------------------*/

int verbose = TRUE;
int autoRebuild = TRUE;
char *cwd = NULL;
FILE *newDir;

/* default file is in the home directory plus the following filename : */
#define DEFAULT_NAME "/rcd.dirList"
#define DEFAULT_OUTPUT "/tmp/rcd.lastdir"

/*-----------------------------------------------------------------------*/



/*************************************************************************/
dirEntry *addEntry(root, up, name, isLink, linkName)
dirEntry *root; dirEntry *up; char *name; int isLink; char *linkName;
/*************************************************************************/
/*
  Adds an entry to the directory tree.
  If this entry is a symbolic link, appends a '@' to the name.
  In fact, the coresponding code for the links is not used because the
  'stat 2V' function doesn't manage the links (and this is a pitty because
  firstly I don't understand why a documented function isn't properly managed,
  and secondly it should be very usefull not to duplicate the directory tree
  which occurs when processing a link (optimizes both memory and human
  faculties when looking at the tree)).
  I you know a good system to check if a directory is a link, tell me.
  What I need is a (uniXversal) system call, not a crazy idea like:
          sprintf(s, "ls %s | grep \"%s@\" | wc > /tmp/smilblics.troglodites ",
	          previouspath, entry);
	  system(s);
	  f = fopen("/tmp/smilblics.troglodites", "r");
	  etc...
  Don't bother, I found it.
*/
{
  dirEntry *cell;

  cell = malloc(sizeof(dirEntry));
  if (cell == NULL)
  {
    fprintf(stderr, "Error for malloc in 'addEntry' routine. Size = %ld\n", sizeof(dirEntry));
    return(NULL);
  }

#ifdef MANAGE_LINKS 
  if (isLink)
  {
    cell->name = malloc(strlen(name)+2);
    if (cell->name != NULL) sprintf(cell->name, "%s@", name);
    cell->linkName = strdup(linkName);
    if (cell->linkName == NULL)
    {
      fprintf(stderr, "Error for link strdup in 'addEntry' routine. Size = %ld\n", strlen(linkName));
      return(NULL);
    }
    fprintf(stderr,"is a link: %s --> %s\n", cell->name, cell->linkName);
  }
  else
#endif
  {
    cell->name = strdup(name);
    cell->linkName = NULL;
  }

  if (cell->name == NULL)
  {
    fprintf(stderr, "Error for strdup in 'addEntry' routine. Size = %ld\n", strlen(name));
    return(NULL);
  }

  cell->right = NULL;
  cell->left = root;
  if (root->right == NULL) root->right = cell; /* first cell of the root */
  cell->up = up;
  if (up != NULL) up->down = cell;
  cell->down = NULL;

  return(cell);

} /* de add entry */


/*************************************************************************/
int readAllDirs(root, baseName)
dirEntry *root; char *baseName;
/*************************************************************************/
/*
  Reads all directories and stores them in the root structure.
  Exit whith TRUE (1) if OK, else FALSE (0).
*/
{
  DIR * dir;
  struct dirent * entry;
  dirEntry *upDirEntry = NULL;
  dirEntry *currentDirEntry;
  char newBaseName[1024];
  char linkName[1024];
  struct stat tempoStatBuf, tempoLStatBuf;
  umode_t status;
  char reply[256];
  int i;

  strcpy(reply, "yes");

  if (!autoRebuild)
  {
    fprintf(stderr, "\nWarning: the directory tree is going to be rebuild\nDo you want this to happend ? (y/n) : ");
    scanf("%s", reply);
  }

  if (reply[0] != 'y' && reply[0] != 'Y') return(FALSE);

  if (verbose) fprintf(stderr, "%s\n", baseName);

  if (strcmp(baseName, "/") == 0)
    root->name = strdup(baseName);

  dir = opendir(baseName);

  while ((entry = readdir(dir)) != NULL)
  {
    if (strcmp(entry->d_name, ".") != 0
	&& strcmp(entry->d_name, "..") != 0
	&& strcmp(entry->d_name, "proc") != 0)
    {
      sprintf(newBaseName, "%s%s", baseName, entry->d_name);
      status = stat(newBaseName, &tempoStatBuf);
      if (S_ISDIR(tempoStatBuf.st_mode))
      {
	status = lstat(newBaseName, &tempoLStatBuf);
	i = readlink(newBaseName, linkName, 1024);
	if (i < 0) i = 0; linkName[i] = '\0';
	upDirEntry = addEntry(root, upDirEntry, entry->d_name, (S_ISLNK(tempoLStatBuf.st_mode)), linkName);
	if (upDirEntry == NULL) return(FALSE);
      }
    }
  }

  closedir(dir);

  currentDirEntry = root->right;
  while (currentDirEntry != NULL)
  {
    if (currentDirEntry->name[strlen(currentDirEntry->name)-1] != '@')
    {
      sprintf(newBaseName, "%s%s/", baseName, currentDirEntry->name);
      if (!readAllDirs(currentDirEntry, newBaseName)) return(FALSE);
    }
    currentDirEntry = currentDirEntry->down;
  }

  return(TRUE);

} /* de read all directories */



/*************************************************************************/
int saveEntry(f, root, n)
FILE *f; dirEntry *root; int *n;
/*************************************************************************/
/*
  File format: ASCII
  Syntax: ". name" for to add an entry to the current level,
          "- name" if this entry is the last one of the level,
	           (so we have to go one level down after this),
	  "+ name" if this entry contains up-leveled entries, so the next
	           entries are the entries for this name, and this name is
		   the root for the following ". name" or "- name" entries.

  The syntax of a symbolic link is:
          [.-+] name@ symbolicName

  Example:
  . /            --> this is the root
  + etc          --> /etc
  . defaults     --> /etc/defaults (has no soons)
  -+ national    --> /etc/national (is the last one of /etc and has soons)
  . french       --> /etc/national/french
  - english      --> /etc/national/english, finishes both /national and /etc
  + usr          --> /usr
  - openwin      --> /usr/openwin (no soons and last of /usr)
  - var          --> is the last one of "/" so it's the end of the file.

  The root "/" is a particular case: it should be put as "-+ /" because
  it has soons and is the only entry of the first level.

  Behavior for reading:
  "." --> add entry to the root cell,
  "+" --> add entry to the root cell and recursively add to the right ptr,
  "-" --> add entry to the root cell and return from the function,
  "-+" -> add entry to the root cell, recursively add to right and return.

  Behavior for writing:
  root cell:
    no soons:
      no down:     "- name"
      else (down): ". name" and go down
    else (soons)
      no down:     "-+ name" and process soons
      else (down): "+ name" and process soonsn then go down
*/
{
int isALink;

  while (root != NULL)
  {
    (*n)++;
    isALink = (root->name[strlen(root->name)-1] == '@');
    if (root->right == NULL)
    {
      if (root->down == NULL)
	fprintf(f, "- %s", root->name);
      else 
	fprintf(f, ". %s", root->name);
      if (isALink) fprintf(f, " %s", root->linkName);
      fprintf(f, "\n");
    }  
    else
    {
      if (root->down == NULL)
	fprintf(f, "-+ %s", root->name);
      else 
	fprintf(f, "+ %s", root->name);
      if (isALink) fprintf(f, " %s", root->linkName);
      fprintf(f, "\n");
      if (!saveEntry(f, root->right, n)) return(FALSE); /* n is not usefull */
    }
    root = root->down;
  }

  return(TRUE);
  /* the return TRUE is not usefull : never fails because I don't check the
   * fprintf routine.
   */

} /* de save entry */


/*************************************************************************/
int saveDirListFile(filename, root)
char *filename; dirEntry *root;
/*************************************************************************/
{
  FILE *f;
  int n = 0;

  f = fopen(filename, "w");
  if (f == NULL)
  {
    fprintf(stderr, "Cannot open filename %s for writing\n", filename);
    return(FALSE);
  }

  if (!saveEntry(f, root, &n)) return(FALSE);
  fclose(f);
  if (verbose)
    fprintf(stderr, "file %s saved, %d directories\n", filename, n);

  return(TRUE);

} /* de save dir list file */



/*************************************************************************/
int loadEntry(f, root, n, dy)
FILE *f; dirEntry *root; int *n; int *dy;
/*************************************************************************/
/*
  The 'dy' parameter must be set to 0 at the first call.
  Have a look to the comments of the saveEntry function to understand this.
*/
{
  int flagMinus, flagPlus;
  char flags[512], name[512], linkName[1024];
  dirEntry *cell, *oldRoot;

  do
  {
    fscanf(f, "%s %s", flags, name);
    if (name[strlen(name)-1] == '@') fscanf(f, "%s", linkName);
    (*n)++;

    flagMinus = (flags[0] == '-');
    flagPlus = (flags[0] == '+' || flags[1] == '+');

    root->name = strdup(name);
    assert(root->name);
    if (name[strlen(name)-1] == '@')
    {
      root->linkName = strdup(linkName);
      assert(root->linkName);
    }

    root->y = *dy;

    if (flagPlus)
    {
      cell = malloc(sizeof(dirEntry));
      if (cell == NULL)
      {
	fprintf(stderr, "Malloc error in loadEntry/flagPlus function. Size = %ld\n", sizeof(dirEntry));
	return(FALSE);
      }

      cell->left = root;
      cell->up = NULL;
      cell->down = NULL;
      cell->right = NULL;
      cell->name = NULL;
      cell->linkName = NULL;
      cell->x = root->x + strlen(root->name) + 5;
      /* I know, I know, I didn't use a define for the 5 above...
	 This is the space that separates horizontaly two entries.
       */
      cell->y = root->y;

      root->right = cell;

      if (!loadEntry(f, cell, n, dy)) return(FALSE);
    }

    if (!flagMinus)
    {

      cell = malloc(sizeof(dirEntry));
      if (cell == NULL)
      {
	fprintf(stderr, "Malloc error in loadEntry/flagMinus function. Size = %ld\n", sizeof(dirEntry));
	return(FALSE);
      }

      (*dy)++;  /* space that separates verticaly two entries */

      cell->up = root;
      cell->left = root->left;
      cell->right = NULL;
      cell->down = NULL;
      cell->name = NULL;
      cell->linkName = NULL;
      cell->x = root->x;
      cell->y = *dy;

      root->down = cell;
      root = cell;
    }
  }
  while (!flagMinus);

  return(TRUE);

} /* de load entry */


/*************************************************************************/
int loadDirListFile(filename, root, dy)
char *filename; dirEntry *root; int *dy;
/*************************************************************************/
{
  FILE *f;
  int nl = 0;
 
  *dy = root->y;

  f = fopen(filename, "r");
  if (f == NULL)
  {
    fprintf(stderr, "Error openning file %s\n", filename);
    return(FALSE);
  }

  if (!loadEntry(f, root, &nl, dy)) return(FALSE);

  fclose(f);

  if (verbose)
    fprintf(stderr, "file %s loaded, %d directories\n", filename, nl);

  return(TRUE);

} /* de load dir list file */


/*-----------------------------------------------------------------------*/


/*************************************************************************/
void drawWindow(win, current, sx, sy, cx, cy, selectedPath, px, py,
		forceClearFlag, oldCurrentY)
WINDOW *win;
dirEntry *current;
int sx; int sy;
int cx; int cy;
char *selectedPath;
int px; int py;
int forceClearFlag;
int oldCurrentY;
/*************************************************************************/
/*
  Draw the entire window.
  Never fails.
*/
{
  dirEntry *root;
  int csy, i;
  char *staticLine = NULL;


  sx-=1;
  sy-=1;
  csy = sy+cy;

  root = current;
  while (root->left != NULL) root = root->left;

#ifdef A_NORMAL
  wattron(win, A_NORMAL);
#endif


  if (forceClearFlag)
  {
    wclear(win);
#ifdef USES_DRAWING_LINES
    box(win, ACS_VLINE, ACS_HLINE);
    wmove(win, sy, 0);
    whline(win, ACS_HLINE, sx-2);
#else
    box(win, ':', '=');
    if (staticLine == NULL)
    {
      staticLine = malloc(sizeof(char) * (sx+1));
      if (staticLine != NULL) staticLine[0] = 0;
      for (i = 1; i < sx; i++) 
      { 
	mvwaddch(win, sy, i, '-');
	if (staticLine != NULL) strcat(staticLine, "-");
      }
    }
    else
      mvwaddstr(win, sy, 1, staticLine);
#endif
  }
  else
  {
    /* only clear the old currentRoot line and the current path name line */
    wmove(win, oldCurrentY, 1);
    wclrtoeol(win);
#ifdef USES_DRAWING_LINES
    mvwaddch(win, oldCurrentY, sx, ACS_VLINE);
#else
    mvwaddch(win, oldCurrentY, sx, ':');
#endif
    wmove(win, py, 1);
    wclrtoeol(win);
#ifdef USES_DRAWING_LINES
    mvwaddch(win, py, sx, ACS_VLINE);
#else
    mvwaddch(win, py, sx, ':');
#endif
  }

  while (root != NULL && root->y < csy)
  {
    if (root->y > cy)
    {
#ifdef USES_DRAWING_LINES
      int x,y;

      x = root->x-cx;
      y = root->y-cy;

      if (root->up == NULL)
      {
	wmove(win, y, x-6);
	whline(win, ACS_HLINE, 5);
       	if (root->down != NULL)
	  mvwaddch(win, y, x-3, ACS_TTEE);
      }
      else
      {
	wmove(win, y, x-3);
	whline(win, ACS_HLINE, 2);
	if (root->down == NULL)
	  mvwaddch(win, y, x-3, ACS_LLCORNER);
	else
	  mvwaddch(win, y, x-3, ACS_LTEE);
      }
#else
      char s[6];
      int rx, x;

      if (root->up == NULL)
      {
	rx = 5;
	if (root->down == NULL)
	  strcpy(s, "-----");
	else
	  strcpy(s, "--+--");
      }
      else
      {
	rx = 3;
	if (root->down == NULL)
	  strcpy(s, "`--");
	else
	  strcpy(s, "|--");
      }

      for (x = 0; x < rx; x++)
	if (root->x-cx-rx+x > 0) 
	  mvwaddch(win, root->y-cy, root->x-cx-rx+x, s[x]);
#endif      

#ifdef A_STANDOUT
      if (root == current) wattron(win, A_STANDOUT);
#endif

      if (root->x-cx > 0)
	mvwaddstr(win, root->y-cy, root->x-cx, root->name);
      else
      {
	int x;

	for (x = 0; x < strlen(root->name); x++)
	  if (root->x-cx+x > 0)
	    mvwaddch(win, root->y-cy, root->x-cx+x, root->name[x]);
      }

      if (root == current)
      {
	mvwaddch(win, root->y-cy, root->x-cx-1, '<');
	mvwaddch(win, root->y-cy, root->x-cx+strlen(root->name), '>');
      }
      else if (root->left == current && current->right == root)
      {
#ifdef A_STANDOUT
	wattron(win, A_STANDOUT);
#endif
	mvwaddch(win, current->y-cy, current->x+strlen(current->name)-cx, '>');
      }
      else
	mvwaddch(win, root->y-cy, root->x-cx+strlen(root->name), ' ');

#ifdef A_STANDOUT
      wattroff(win, A_STANDOUT);
#endif
    }

    if (root->down != NULL)
    {
      int y1, y2, x;

      x = root->x-cx - 3;
      y1 = root->y+1-cy;
      y2 = root->down->y-cy;

      if (y1 < sy && y2 > 0 && x > 0 && x < sx)
	for (y1 = max(1, y1), y2 = min(y2, sy); y1 < y2 && y1 < sy; y1++)
#ifdef USES_DRAWING_LINES
	  mvwaddch(win, y1, x, ACS_VLINE);
#else
          mvwaddch(win, y1, x, '|');
#endif
    }

    if (root->right != NULL) root = root->right;
    else
    {
      if (root->down == NULL)
	while (root != NULL && root->down == NULL) root = root->left;
      if (root != NULL) root = root->down;
    }
  }

  if (current->linkName == NULL)
    if (strlen(selectedPath) > sx-px)
      mvwprintw(win, py, px, "...%*s", sx-px-3,
		&selectedPath[strlen(selectedPath)-(sx-px-3)]);
    else
      mvwaddstr(win, py, px, selectedPath);
  else
    mvwprintw(win, py, px, "--> %-*s", sx-px-4, current->linkName);

  wrefresh(win);

} /* de draw window */



/*************************************************************************/
dirEntry * getCurrentPath(root, cwdPath)
dirEntry * root; char **cwdPath;
/*************************************************************************/
/*
  This function reads the current working directory and try to match it
  in the loaded directory tree.
  Returns NULL if cannot match it.
*/
{
  int t;
  char s[257], *s1;

  if (*cwdPath == NULL) *cwdPath = getcwd(NULL, 256);

  if (*cwdPath == NULL)
    return(NULL);
  else
    strcpy(s, *cwdPath);

  if (strcmp(*cwdPath, "/") == 0)
  {
    while (root->left != NULL) root = root->left;
    return(root);
  }

  root = root->right; /* step over the root '/' entry */
 
  s1 = strtok(s, "/");
  do
  {
    while ((t = strcmp(root->name, s1)) != 0 && root->down != NULL)
      root = root->down;

    s1 = strtok(NULL, "/");

    if (s1 != NULL)
      if (t != 0)
	/* there is no matching name. Exit with a NULL ptr */
	return(NULL);
      else
	root = root->right;

  } while (s1 != NULL && root != NULL);

  return(root);

} /* de get current path */


/*************************************************************************/
void setSelectedPath(pathname, root)
char *pathname; dirEntry *root;
/*************************************************************************/
/*
  This function writes the current selected path as a string in 'pathname'.
  Never fails.
 */
{

  if (root == NULL)
  {
    strcpy(pathname, "/");
    return;
  }

  strcpy(pathname, root->name);
  while ((root = root->left) != NULL)
  {
    char s[512];

    strcpy(s, pathname);
    if (root->name[strlen(root->name)-1] != '/' && s[0] != '/')
      sprintf(pathname, "%s/%s", root->name, s);
    else
      sprintf(pathname, "%s%s", root->name, s);
  }

  if (pathname[strlen(pathname)-1] == '@') pathname[strlen(pathname)-1] = '\0';

} /* de set selected path */


/*************************************************************************/
void test(root, rootCWD, cwd, nli)
dirEntry *root; dirEntry *rootCWD; char *cwd; int nli;
/*************************************************************************/
{
  dirEntry *mainRoot = root;
  WINDOW * stdscr, * win, * winPath;
  int bx, by,  /* top left of the window 'win' */
      sx, sy,  /* size of 'win' */
      cx, cy,  /* scrolling actuel de 'win' */
      px, py;  /* position of the path screen on 'win' */
  int quit = FALSE, redraw = FALSE, follow = FALSE;
  char selectedPath[512], dummy[512];
  int key;

  root = rootCWD;  /* the visible current path is the current work dir */

  stdscr = initscr();
  cbreak();
  noecho();
  nonl();

  sx = COLS;
  sy = LINES-2;
  win = newwin(sy+2, sx, 0, 0);
  scrollok(win, FALSE);
  clearok(win, FALSE);
  keypad(win, TRUE);

  py = sy;
  px = 2;
  cx = 0;
  cy = 0;
  follow = TRUE;
  do
  {
    if (root == NULL)
    {
      /* we have dramatically lost the root entry during pointer
	 manipulations.
	 We try to resynchronise to the toplevel root and we reset the
	 window position.
	 */
      root = mainRoot; 
      cx = 0;
      cy = 0;
      redraw = TRUE;
    }

    if (follow)  /* path has changed */
    {
      int x,y,x1;

      x = root->x-cx-1;
      x1 = root->x+strlen(root->name)-cx;
      y = root->y-cy;

      if (x < 1)
	cx -= 1-x;
      else if (x1 >= sx)
	cx += x1-sx+1;

      if (y < 1)
	cy -= 1-y;
      else if (y >= sy-1)
	cy += y-sy+2;

      setSelectedPath(selectedPath, root);

      redraw = TRUE;
      follow = FALSE;
    }

    if (redraw)  /* screen has changed */
    {
      static int
	oldCx = -1,
	oldCy = -1,
	new = TRUE,
	oldRootY;
      
      if (cx != oldCx || cy != oldCy)
      {
	oldCx = cx;
	oldCy = cy;
	new = TRUE;
      }

      drawWindow(win, root, sx, sy, cx, cy, selectedPath, px, py,
		 new, oldRootY);
      redraw = FALSE;
      new = FALSE;
      oldRootY = root->y-cy;
    }

    key = wgetch(win);

    if (key >= 'A' && key <= 'Z') key += 'a'-'A';

    switch(key)
    {
    case KEY_A3:
    case 'e':
    case '9':  /* page up : scroll up */
      if (cy > 0) { cy--; redraw = TRUE; }
      break;

    case KEY_C3:
    case 'x':
    case '3':  /* page down : scroll down */
      if (cy < nli) { cy++; redraw = TRUE; }
      break;

    case KEY_A1:
    case 's':
    case '7':  /* home of line : scroll left */
      if (cx > 0) { cx--; redraw = TRUE; }
      break;

    case KEY_C1:
    case 'd':
    case '1':  /* end of line : scroll right */
      cx++;
      redraw = TRUE;
      break;

    case KEY_UP:
    case 'i':
    case '8':  /* up arrow : find up entry */
      follow = TRUE;
      if (root->up != NULL)
      {
	root = root->up;
	while (root->right != NULL)
	{
	  root = root->right;
	  while (root->down != NULL) root = root->down;
	}
      }
      else if (root->left != NULL)
      {
	root = root->left;
	while (root != NULL && root->left != NULL && root->up == NULL)
	  root = root->left;
	if (root->up != NULL) 
	{
	  root = root->up;
	  while (root->right != NULL)
	  {
	    root = root->right;
	    while (root->down != NULL) root = root->down;
	  }
	}
      }
      else 
	follow = FALSE;
      break;

    case KEY_DOWN:
    case ',':  /* (for azerty keyboards) */
    case 'm':  /* (for qwerty keyboards) */
    case '2':  /* down arrow : find down entry */
      follow = TRUE;
      if (root->down != NULL)
	root = root->down;
      else if (root->right != NULL)
      {
	root = root->right;
	while (root!=NULL && root->right != NULL && root->down == NULL)
	  root = root->right;
	if (root->down != NULL)
	  root = root->down;
      }
      else if (root->left != NULL)
      {
	dirEntry *cell = root;

	root = root->left;
	while (root != NULL && root->left != NULL && root->down == NULL)
	  root = root->left;
	if (root->down != NULL)
	  root = root->down;

	if (root == mainRoot) root = cell;
      }
      else 
	follow = FALSE;
      break;

    case KEY_LEFT:
    case 'j':
    case '4': /* left arrow : find left entry, if any */
      follow = TRUE;
      if (root->left != NULL)
	root = root->left;
      else
	follow = FALSE;
      break;

    case KEY_RIGHT:
    case 'k':
    case '6':  /* right arrow : find right entry, if any */
      follow = TRUE;
      if (root->right != NULL)
	root = root->right;
      else if (root->down != NULL)
	root = root->down;
      else if (root->left != NULL)
      {
	root = root->left;
	while (root != NULL && root->left != NULL && root->down == NULL)
	  root = root->left;
	if (root->down != NULL)
	  root = root->down;
      }
      else 
	follow = FALSE;
      break;

    case 13:  /* 'return' or 'enter' key */
#ifdef ARGGG_THIS_STUFF_REFUSES_TO_WORK
      if (chdir(selectedPath) != 0)  /* chdir is in <unistd.h>, man 2 chdir */
      {
	/* we were not allowed to change the directory ! */
	char s[256];

	if (errno < sys_nerr)
	  strcpy(s, sys_errlist[errno]);
	else
	  strcpy(s, "Error while changing directory. Bad permissions or bad path ?");

	wmove(win, py, px);
	wclrtoeol(win);
	mvwprintw(win, py, px, "%-*s", sx-px-1, s);
	wrefresh(win);
	/* flash(); */

	break;
      }
      /* if successfull ( == 0), no break here: we quit now */
#else
      /* so if the 'right' stuff doesn't seem to work well, here is the
	 bad stuf that is REALLY going to work !!
	 */

      /* i = execlp("cwd", "cwd", selectedPath, (char *)0); */

      /*
	strcpy(dummy, "cd ");
	strcat(dummy, selectedPath);
	system(dummy);
	*/

      /*
	This also doesn't work properly since the current path is only changed
	in the subprocess environnements. What we want is to change the
	path of the father's environnement.
	The method I thougth off is to lauch the program by an alias like
	this one:

	   alias rcd = 'cd $( /usr/local/bin/c )' if you use /bin/bash
       or
           alias rcd = 'cd `/usr/local/bin/c`' if you use a Bourne shell, etc.

       The way it works is rather easy: the standard output of the command
       is given litteraly to the alias string.
       This means that the stdout of the 'c' program should only consist
       on the path to change to.
       Other outputs should be put on stderr.
       (if you know another way to do such a thing, tell me).

       the way of doing it as changed a little by now.
       In fact, the $(c) creates some confusion in the alias because of
       curses: the $(c) extract the stdout to the alias. But as curses uses
       the stdout to draw the windows, the user doesn't see anything and
       the all thing (window + stdout output) is piped to the alias...

       The idea is to change the alias like this:

            alias rcd = '/usr/local/bin/c; cd $(cat /tmp/rcd.lastdir)'
       or
            alias rcd = '/usr/local/bin/c -f file1 -d file2; cd $(cat file2)'

       You may want to use the Bourne shell syntax (`` instead of $()).
       In the later command, file1 overrides the default list file name,
       and file2 overrides the default name of the file which is used to
       pipe to cd (this file can be put in /tmp since it is only used
       when rcd is active and can be delete when rcd is not used -- but the
       file must keep open when rcd is used).
       This as the advantage of fixing the problem of default attributes.

       */

      /* printf("%s", selectedPath); */

      /* yeah, the final stuff looks a bit shorter, doesn't it ? */

      quit = TRUE;
      break;
#endif

    case 'q':
    case 'Q':

      /* get out from here with the initial directory we were in */
      /* printf("%s", cwd); */

      strcpy(selectedPath, cwd);

      quit = TRUE;
      break;

    } /* du switch key */

  }
  while (!quit);

  nl();  /* retablir avant la fin sinon le shell est en 'nonl' ! */
         /* oops! I'm sorry, I translate: do not forget the nl()
	    because if you forget it, the shell will find itself in nonl mode !
	    (humm maybe I forgot to tell you that nl=auto-insert-newline-char
	    and nonl=do not insert newline char after the return char !)
	    Ahhh, I personally dislike whose people who put source in DP
	    with german comments - I'm french and I can't stand with
	    german. I personally prefer reading a bad-written english comment
	    thant a well-written german comment.
	    Humm it's possible I finally prefer to read a well-written
	    french comment instead of a bad-written english comment but
	    you may bother with such a thought...
	 */
  wclear(win);
  wmove(win, 0, 0);
  wrefresh(win);
  endwin();

  /* print out the path to change to */

  fprintf(newDir, "%s\n", selectedPath);

} /* de test */


/*-----------------------------------------------------------------------*/



/*************************************************************************/
void freeSubRoot(root)
dirEntry *root;
/*************************************************************************/
{
  dirEntry *cell;

  while (root)
  {
    if (root->name) free(root->name);
    if (root->linkName) free(root->linkName);
    if (root->right) freeSubRoot(root->right);
    cell = root->down;
    free(root);
    root = cell;
  }
} /* de free sub root */


/*************************************************************************/
void freeRoot(root)
dirEntry *root;
/*************************************************************************/
{
  if (root->name) free(root->name);
  if (root->linkName) free(root->linkName);

  freeSubRoot(root->right);

  root->up = NULL;
  root->down = NULL;
  root->left = NULL;
  root->right = NULL;
  root->name = NULL;
  root->x = 2;
  root->y = 1;

} /* de free root */


/*-----------------------------------------------------------------------*/


/*************************************************************************/
int main(argc, argv)
int argc; char **argv;
/*************************************************************************/
{
  dirEntry root = {NULL, NULL, NULL, NULL, NULL, NULL, 2, 1};
  dirEntry *rootCWD = NULL;
  int numberOfLines = 1;
  int rebuild = FALSE;
  int mustReload = FALSE;
  char fileDirList[256], newDirName[256];
  char *s;

  strcpy(newDirName, DEFAULT_OUTPUT);

  s = getenv("HOME");
  if (s)
    strcpy(fileDirList, s);
  else
    strcpy(fileDirList, "");
  strcat(fileDirList, DEFAULT_NAME);

  s = getenv("RCDFILE");
  if (s) 
  {
    strcpy(fileDirList, s);
    if (verbose) fprintf(stderr, "File changed for %s\n", s);
  }

  s = getenv("RCDNOAUTOREBUILD");
  if (s)
  {
    autoRebuild = FALSE;
    if (verbose) fprintf(stderr, "Auto Rebuild Off\n");
  }
 
  if (argc > 1)
  {
    int c;
    extern char *optarg;
    extern int optind;

    while ((c = getopt(argc, argv, "d:hrqnafHRQNAF:")) != -1)
      switch(c)
      {
      case 'N':
      case 'n':
	autoRebuild = FALSE;
	if (verbose) fprintf(stderr, "auto rebuild is set off\n");
	break;

      case 'A':
      case 'a':
	autoRebuild = TRUE;
	if (verbose) fprintf(stderr, "auto rebuild is set on\n");
	break;

      case 'Q':
      case 'q':
	verbose = FALSE;
	break;

      case 'R':
      case 'r':
	rebuild = TRUE;
	if (verbose) fprintf(stderr, "rebuild mode entered\n");
	break;

      case 'F':
      case 'f':
	strcpy(fileDirList, optarg);
	if (verbose) fprintf(stderr, "list file changed to %s\n", optarg);
	break;

      case 'D':
      case 'd':
	strcpy(newDirName, optarg);
	if (verbose) fprintf(stderr, "temporary dir name file changed to %s\n", optarg);

      case 'H':
      case 'h':
      case '?':
	fprintf(stderr, "Usage: rcd [-hnaqr] [-f filename] [entry]\n");
	fprintf(stderr, "\nFlags:\n-n\tauto rebuild off\n-a\tauto rebuild on\n");
	fprintf(stderr, "-q\tquiet mode (default = verbose mode)\n-r\tforce rebuild mode\n");
	fprintf(stderr, "-h\tthis little help -- use 'man 1 rcd' instead\n");
	fprintf(stderr, "-f file\tselect this file as the list file (see man)\n");
	fprintf(stderr, "entry\tthe entry of the tree to switch for\n");
	fprintf(stderr, "Upper-case letters are allowed\n");
	return(0);
      }

    for (; optind < argc; optind++); /* use argv[optind] */

  }

  /* first try to load the list file, but if it isn't possible, rebuild */
  if (!rebuild)
  {
    rebuild = !loadDirListFile(fileDirList, &root, &numberOfLines);

    /* if we loaded it, is the current working directory a valid one ? */
    if (!rebuild)
      rebuild = !(rootCWD = getCurrentPath(&root, &cwd));
  }

  mustReload = rebuild;

  newDir = fopen(newDirName, "w");
  if (newDir == NULL)
  {
    fprintf(stderr, "Error opening file %d for writing\nPlease change name with -d flag if necessary\n", newDirName);
    return(0);
  }

  /* are we going to rebuild ? */
  if (rebuild)
    if (readAllDirs(&root, "/"))
      saveDirListFile(fileDirList, &root);
    else
    {
      fprintf(stderr,"Error reading directories. Aborting!\n");
      fprintf(newDir, ".\n");
      return(0);
    }
	
  if (mustReload)
  {
    freeRoot(&root);
    if (!loadDirListFile(fileDirList, &root, &numberOfLines))
    {
      fprintf(stderr,"Aborting!\n");
      fprintf(newDir, ".\n");
      return(0);
    }
  }
  rootCWD = getCurrentPath(&root, &cwd);

  test(&root, rootCWD, cwd, numberOfLines);

  fclose(newDir);

  freeRoot(&root);

} /* de main */





/*------------------------------- emacs defs ----------------------------*/

/*
  Local Variables:
  c-argdecl-indent: 0
  c-brace-offset: -2
  c-label-offset: -2
  compile-command: "doit"
  End:
  */

/*---------------------------------- eos --------------------------------*/

