/*       ccd
 *   Cedric's ccd
 *
 *  (C) 1991 Cedric BEUST (beust@taloa.unice.fr)
 *
 * This is _NOT_ public domain. Just freeware. Redistribute to whoever
 * you want, use it for your own sake but don't spread modified versions
 * without my prior consent. Thanks!
 *
 * Compiled with DICE v2.x (a mere 'dcc -o ccd ccd.c' will do)
 *
 */
 

#include <stdio.h>
#include <assert.h>
#include <proto/dos.h>
#include <proto/intuition.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <exec/memory.h>
#include <exec/types.h>
#include <intuition/intuition.h>
#include <graphics/gfxbase.h>

#include "ccd.h"
#include "win.h"

#define GRAPHICS
#define VERSION "24"
#define FVERSION "2.4"
#define WINDOWTITLE "ccd v2.4         by Cédric BEUST"

#define CCDCONFIGFILE "ccd:.ccdconfig"
#define WIDTH 640             /* width of the display */
int HEIGHT = 199;            /* its height (bigger if PAL) */
#define DELTAX 80           /* # of pixels between two entries */
#define DELTAY 8           /* vertical step */
#define UPPERX 25         /* upper corner where we start to write */
#define UPPERY 10        /* ditto */
#define LINESPAL 30     /* # of lines when in PAL mode */
#define EXTRALINES 2   /* # of lines the config file that are not dirs */

#define BETWEEN_GADGETS 6        /* # of vert pixels between gadgets */

#define min(a,b) (a < b ? a : b)

#define NEW(v, t) v = (t *) malloc(sizeof(*v))

static char actualdir[128];
struct Window *mainwindow;
struct Screen *mainscreen;
long IntuitionBase;
struct GfxBase *GfxBase;
int configlines = 0;          /* # of lines in ccdconfig */
int actualline = 1;
int nbvolumes = 0;          /* # of volumes found */
int volumesid = 0;         /* id of the first Volume gadgets */
int linesperwindow = 24;  /* # of lines on the display (more if PAL) */

USHORT *chippointer = NULL, *chippointer2 = NULL;

struct Tree_t {
  struct Tree_t *brother;
  struct Tree_t *son;      /* could be sister and daughter... */
  char name[128];         /* last name */
  char fullname[128];    /* fully qualified name */
  int x;                /* position on the screen */
  int line;            /* its line in the config file */
} *the_tree = NULL;

WORD color0 = 0x0555,
     color1 = 0x0FFF,
     color2 = 0x0000,
     color3 = 0x0F00;

/* Flags set through the command line */

int show_ambiguities = 0;
int update_list = 0;
int no_expand = 0;
int no_graphics = 0;
int exact_cd = 0;
int verbose = 0;
int workbenchscreen = 0;

/* An attempt to keep track of all the Locks I use */
BPTR locklist[128];
int lockpt = 0;

BPTR
get_a_lock(char *name, long code)
{
  locklist[lockpt] = Lock(name, code);
  if (locklist[lockpt])
    return locklist[lockpt++];
  else
    return 0;
}

void
unlock(BPTR lock)
{
/*
  UnLock(lock);
*/
}

WORD
atow(char *s)
/* Ascii to word. s is a hex number (e.g. "3FE") */
/* Return its true value */
{
  char *inits = s;
  WORD result = 0, temp; 
  while (*s) {
    if (*s >= 'a') *s -= 0x20;
    temp = *s - 'A' + 10;
    if (*s <= '9') temp += 7;
    result = result * 16 + temp;
    s++;
  }
  return result;
}

void
free_all_locks()
{
  int i;
  for (i = 0; i < lockpt; i++)
    UnLock(locklist[i]);
}

void
myputs(char *s)
{
  Write(Output(), s, strlen(s));
}

void
Read_Dirs(char *dir, FILE *f)
/* Read directories in dir and add them to the file f (should be .ccdconfig) */
{
  BPTR lock;
  struct FileInfoBlock info, *fib;

  lock = Lock(dir, ACCESS_READ);
  if (lock == 0) {
    fprintf(stderr,"*** Yup! Couldn't lock %s\n", dir);
    return;
  }

/* Strange statements, uh? */
  fib = (struct FileInfoBlock *) Examine(lock, &info);
  fib = &info;

  while (1) {
    if (ExNext(lock, fib) == 0) break;
    if (fib -> fib_DirEntryType > 0) {
      char t[50],c;
      strcpy(t, dir);
      if ((c = t[strlen(t) - 1]) != ':' && c != '/')
        strcat(t,"/");
      strcat(t, fib -> fib_FileName);
      fprintf(f, "%s\n", t);
      if (verbose) printf("%s\n", t);
      Read_Dirs(t,f);
    }
  }
  unlock(lock);
}

FILE *
Open_Config_File(char *mode)
{
  FILE *f;
  f = fopen(CCDCONFIGFILE, mode);
  if (f == NULL) {
    fprintf(stderr, "*** Couldn't open '%s', maybe an assign is missing?\n", CCDCONFIGFILE);
    if (mainwindow) CloseWindow(mainwindow);
    if (mainscreen) CloseScreen(mainscreen);
    exit(0);
  }
  if (mode[0] == 'r') Check_Version(f);
  return f;
}

void
Add_Current_Dir()
/* Add to the end of the config file the current dir (and its subdirs) */
{
   struct Process *pr = (struct Process *) FindTask(0L);
   struct CommandLineInterface *cli =
     (struct CommandLineInterface *) (pr -> pr_CLI) << 2;
   char *p = (char *) (cli -> cli_SetName) << 2;
   FILE *f;
   char name[128], n2[128], *pn = name;
   int i, l = *p++;

/* Copy the current dir into name */
   for (i = 0; i < l; i++) *pn++ = *p++;
   *pn++ = '\0';
   f = Open_Config_File("a");
   if (f == NULL) {
     fprintf(stderr, "*** Couldn't open '%s', maybe an assign is missing?\n", CCDCONFIGFILE);
     if (mainwindow) CloseWindow(mainwindow);
     if (mainscreen) CloseScreen(mainscreen);
     exit(0);
   }

   fprintf(f, "%s\n", name);

   printf("Adding %s to the config file... ", name);
   fflush(stdout);
   Read_Dirs(name, f);
   printf(" -- done!\n");
}

void
Update(char **dirname, int ndirs)
/* Create the config file with the list of dirs (dirname) of length ndirs */
{
  FILE *f = Open_Config_File("w");
  int i;

  Put_Version(f);
  Put_Dirs(f, dirname);
  printf("Creating %s with", CCDCONFIGFILE);
  for (i = 0; i < ndirs; i++) {
    printf(" %s", *dirname);
    fflush(stdout);
    fprintf(f, "%s\n", *dirname);
    Read_Dirs(*dirname++, f);
  }
  printf(" -- done!\n");
  fclose(f);
}

int
mystricmp(char *bigdir, char *shortdir)
/* Check if shortdir is part of the path bigdir, return 0 if it is */
{
  char t[50], *pt = t, *ps;
  int end = 0;
  int i = 0;

  while (*bigdir) {
    while (*bigdir && *bigdir != ':' && *bigdir != '/' && *bigdir != '\n')
      *pt++ = *bigdir++;
    if (*bigdir) bigdir++;
    *pt++ = '\0';
    pt = t;
  }

  if (exact_cd) {    /* strings must be equal */
     return stricmp(t, shortdir);
  }
  else {

/* We must see if shortdir is included in t */

   for (i = 0; i <= strlen(t) - strlen(shortdir); i++) {
      pt = &t[i]; ps = shortdir;
      while (*pt && *ps && (*pt | 0x20) == (*ps | 0x20)) {
         ps++; pt++;
      };
      if (*pt == 0 || *ps == 0) return(0);   /* match! */
   }
  }
  return(1);
}

void
Update_Prompt(char *currentdir)
/* Update the concerned field with the new current dir                 */
/* This routine is for users of wshell or such, thar display this name */
/* as the shell prompt.                                                */
/* This trick was previously pointed to me by Henry J. Cobb on Usenet  */
/* for my 'find' program (another great utility of mine :-)).          */
/* Let him be thanked again!                                           */
{
  struct Process *pr = (struct Process *) FindTask(0L);
  struct CommandLineInterface *cli =
    (struct CommandLineInterface *) (pr -> pr_CLI) << 2;
  char *p = (char *) (cli -> cli_SetName) << 2;

  *p++ = strlen(currentdir);          /* it is a BSTR, so length first */
  while (*currentdir) *p++ = *currentdir++;  /* don't add '\0' */
}

int
Check_Version(FILE *f)
{
   char v[100], l[128], c;

   fscanf(f, "%s\n", v);
   if (! isdigit(v[0]) || atoi(v) < atoi(VERSION)) {
      printf("*** config file is obsolete for this version\n");
      printf("*** you should run 'ccd -c <dirs>', I think...\n");
      fclose(f);
      if (mainwindow) CloseWindow(mainwindow);
      if (mainscreen) CloseScreen(mainscreen);
      exit(0);
   }
/*
      printf("*** Do you want me to create it for you with the following dirs:\n*** ");
      fgets(l, 128, f);
      l[strlen(l) - 1] = '\0';
      printf("%s ", l);
      printf("? (y/n) ");
      c = getchar();
      if (c != 'y' && c != 'Y') {
         printf("Alright, do it yourself, then!\n");
         fclose(f);
         if (mainwindow) CloseWindow(mainwindow);
         if (mainscreen) CloseScreen(mainscreen);
         exit(0);
      }
      else {
         char *fakelist[10], **pf = fakelist;
                     /*@@ not 10, should be dynamically alloc'ed *
         char vol[20], *pvol = vol;
         char *p = l;

         nbvolumes = 0;
         while (1) {
            if (*p == '\0') break;
            if (*p == '\n' || *p == ' ') {
               nbvolumes++;
               *pvol++ = '\0';
               *pf = (char *) malloc(strlen(pvol));
               strcpy(*pf, vol);
               *pf++; p++;
               pvol = vol;
            }
            else *pvol++ = *p++;
         }
         fclose(f);
         Update(fakelist, nbvolumes);
      }
   }
*/
   else   /* Version is okay, skip the dir list */
      fgets(l, 128, f);
}

int
Put_Version(FILE *f)
{
   fprintf(f, "%s\n", VERSION);
}

int
Put_Dirs(FILE *f, char **dirnames)
{
   while (*dirnames) {
      fprintf(f, "%s ", *dirnames++);
   }
   fprintf(f, "\n");
}

char *
locate_dir(char *dir, FILE *f)
/* Return the complete path for the fragment given, if any, in file *f */
{
  int match = 0;
  static char onedir[100];

  while (! feof(f) && ! match) {
    int i;
    fgets(onedir, 100, f);
    i = strlen(onedir) - 1;
    if (onedir[i] == '\n')
      onedir[i] = '\0';     /* strip trailing \n */
    if (mystricmp(onedir, dir) == 0) {
      match = 1;
      break;
    }
  }
  if (match) return(onedir);
  else return(NULL);
}

char *
reverse(char *s)
/* Return the string s backwards (modify s)*/
{
  register char c;
  char *result = s + strlen(s) - 1, *r2 = s;

  while (result > s) {
    c = *result;
    *result = *s;
    *s = c;
    s++; result--;
  }
  return(r2);
}

void
Get_Real_Name(BPTR lock, char *result)
/* This is a very useful function! Put in result path of lock */
/* Assume lock is not null */
/* Result always device:dir/dir/...   */
{
  char *p = result;
  struct FileInfoBlock ib;  

  assert(lock);

  if (no_expand) return;
  Examine(lock, &ib);
  strcpy(result, reverse(ib.fib_FileName));
  strcat(result, "/");
  while (lock) {
    lock = ParentDir(lock);
    if (lock) {
      Examine(lock, &ib);
      strcat(result, reverse(ib.fib_FileName));
      strcat(result,"/");
      unlock(lock);
    }
  }
  p = &p[strlen(p) - 1];
  *p-- = '\0';
  while (*p != '/') p--;
  *p = ':';
  reverse(result);
}

void
Call_CurrentDir(char *actualdir)
{
   BPTR lock;

   if (actualdir[0]) {
      lock = Lock(actualdir, ACCESS_READ);
      if (lock == 0) {
         fprintf(stderr,
      "*** Couldn't cd to %s, '%s' probably outdated (re-run ccd -c)\n",
            actualdir, CCDCONFIGFILE);
         exit(0);
      }
      CurrentDir(lock);
      Get_Real_Name(lock, actualdir);
      printf("Current directory is now %s\n", actualdir);
      Update_Prompt(actualdir);
      unlock(lock);
  }
  else
    printf("%s: no such dir\n", actualdir);
}

void
Change_Dir(char *dir, int occ)
/* The main function to change to the fragment of dir given,  */
/* to the occ'th occurence found in the config file */
/* New 1.3: first, try to cd right into 'dir' */
{
  char cmd[50];
  FILE *f = Open_Config_File("r");
  int match = 0;
  BPTR lock;

  strcpy(actualdir, dir);
  if ((lock = Lock(actualdir, ACCESS_READ))) {   /* can we cd directly? */
    BPTR old;
    Get_Real_Name(lock, actualdir);
    lock = get_a_lock(actualdir, ACCESS_READ);
    printf("Current directory is now %s\n", actualdir);
    Update_Prompt(actualdir);
    old = CurrentDir(lock);                     /* yes! Do it and end */
/*
    UnLock(lock);
    UnLock(old);
*/
    return;
  }

                           /* no, dir is a path fragment */
   if (f == NULL) {
     fprintf(stderr,
        "*** Couldn't open '%s'; run ccd -c first, will you?\n", CCDCONFIGFILE);
     exit(0);
   };

   while (occ--) {
     strcpy(actualdir, locate_dir(dir, f));
   }

   Call_CurrentDir(actualdir);

  fclose(f);
}

void
Show_Ambiguities(char *dir)
{
  char *onedir = NULL;
  char cmd[50];
  FILE *f = Open_Config_File("r");
  int match = 0;

  while ((onedir = locate_dir(dir, f))) {
     myputs(onedir);
     myputs("\n");
  }

  fclose(f);  
}

void
Usage()
{
  myputs("\nccd v");
  myputs(FVERSION);
  myputs(",  by [33mCedric Beust[31m    (C) 1991\n\n");
  myputs("    [4mUsage[0m:\n");
  myputs("           ccd                           Bring up the graphic display\n");
  myputs("     or    ccd -c[v] <dir> <dir> ...     Create dir list with dirs\n");
  myputs("     or    ccd -a <partial dir>          Show ambiguities\n");
  myputs("     or    ccd -u                        Update file with current dir\n");
  myputs("     or    ccd [opt] <partial dir> [n]   Change to this dir, nth occurence\n");
  myputs("             where opt is one of\n");
  myputs("                 -n    No path expansion\n");
  myputs("                 -w    Open on Workbench screen\n");
  myputs("                 -nxxx Set color n (0-3) to RGB xxx (hex digits)\n");
  myputs("                 -e    CD to this exact dir\n\n");

  exit(0);
}

void
get_vol(char *path, char *vol)
/* String is a line of the .ccdconfig (volume:a/b/c) */
/* Return volume in the variable vol */
{
   char *p = path;
   while (*p && *p != ':') *vol++ = *p++;
   *vol++ = '\0';  
}

int
numberofdirs(char *path)
/* path is 'vol:a/b/c' */
/* Return number of dirs in it (3 here), actually = number of '/' + 1 */
{
   int result = 0;

   while (*path) {
      while (*path && *path != '/') path++;
      if (*path == '/') {
         result++;
         path++;
      }
   }
   return(++result);
}

void
nthdir(char *dir, char *path, int n)
/* Path is 'volume:a/b/c' */
/* Return in dir the nth dir in it (e.g. with n=2, it's b) */
{
   char *dirhead = dir;
   while (*path && *path != ':') path++;
   if (*path == ':') {
     path++;
     while (*path) {
         while (*path && *path != '/') {
            *dir++ = *path++;
         }
         if (*(dir - 1) == '\n') dir--;
         *dir++ = '\0';
         if (*path == '/') path++;
         if (n == 1) return;
         else {
            n--; dir = dirhead;
         }
      }
   }
   else {
      printf("nthdir: problem\n");
      exit(0);
   }
}

struct Tree_t *
new_node(char *s, int line, int x, char *fullname)
{
   struct Tree_t *newnode;

   NEW(newnode, struct Tree_t);
   if (s[strlen(s) - 1] == '\n')
      s[strlen(s) - 1] = '\0';
   strcpy(newnode -> name, s);
   strcpy(newnode -> fullname, fullname);
   newnode -> brother = NULL;
   newnode -> son = NULL;
   newnode -> line = line;
   newnode -> x = x;
   return(newnode);
}

struct Tree_t *
get_line(struct Tree_t *root, int line)
/* Return the struct for line line, searching from root */
{
/*
   printf("get_line with %s, %d\n", root->name, line);
*/
   if (root) {
      if (root -> line == line) return root;
      else if (root -> brother) {
         if (root -> brother -> line <= line)
            return get_line(root -> brother, line);
         else
            return get_line(root -> son, line);
      }
      else
         return get_line(root -> son, line);
   }
   return NULL;
}

void
add_node(char *s, struct Tree_t *root, int line)
/* Add the appropriate node to the tree */
/* Assert root != NULL */
/*
      dh0:   ->    dh1:    ->   dh2:
                    | 
                  Anews
                    |
                   New
*/
{
   char vol[30];
   char dir[30];
   struct Tree_t *newnode;
   int i;
   int x = UPPERX - DELTAX;

   get_vol(s, vol);
   assert(root);
   while (root -> brother && stricmp(root -> name, vol))
      root = root -> brother;

   if (strcmp(root -> name, vol)) {   /* new vol, must create a new node */
      root -> brother = new_node(vol, line, x + DELTAX, s);
      newnode = root -> brother;
   }
   else {
      struct Tree_t *t = root;
      newnode = root;
   }
   assert(newnode);
   assert(stricmp(newnode -> name, vol) == 0);

/* Now, newnode is a node that contains the volume. Create the path */

   for (i = 1; i <= numberofdirs(s); i++) {             
      x = newnode -> x;
      nthdir(dir, s, i);
      if (newnode -> son) {
         root = newnode -> son;     /* get down one level */

/* Search amid the brothers if we have a matching name */
         while (root -> brother && stricmp(dir, root -> name)) {
            root = root -> brother;
         }
         if (stricmp(root -> name, dir)) {    /* new dir, create new node */
            newnode = root -> brother = new_node(dir, line, x + DELTAX, s);
         }
         else {
            newnode = root;
         }
      }
      else {    /* no son yet, have to create one */
         newnode -> son = new_node(dir, line, x + DELTAX, s);
         newnode = newnode -> son;
      }

/* Now, newnode is a node that contains how far we've got in the path */
/* Let's go on */
      root = newnode;
   }
}

struct Tree_t *
Build_Tree()
{
   FILE *f;
   char string[128];
   char currentvol[30];
   struct Tree_t *result;

   f = Open_Config_File("r");
   NEW(result, struct Tree_t);
   strcpy(result -> name, "Root");
   result -> brother = NULL;
   result -> son = NULL;
   result -> line = -1;

   while (! feof(f)) {
      configlines++;
      fgets(string, 128, f);
      if (string[strlen(string) - 1] == '\n')
         string[strlen(string) - 1] = '\0';
      add_node(string, result, configlines);
   }
   return result;
}

void
Find_Dir_Coo(int x, int y, int line)
{
   struct Tree_t *root;

   root = get_line(the_tree, line + (y / DELTAY) - 2);
   Call_CurrentDir(root -> fullname);
}

int
Display_Tree(struct Tree_t *root, int line, int x, int y)
/* Graphically display the tree for the volume 'vol', from the line */
/* line in the ccdconfig file, at coordinates x and y */
/* Return the y coordinate at the end */
{
   int len, i;
   struct RastPort *rp = mainwindow -> RPort;
   int xinit = x;

   root = get_line(the_tree, line);
   while (root && y < HEIGHT - DELTAY) {
      y += DELTAY;
#ifdef GRAPHICS
      assert(rp);
      Move(rp, root -> x, y);
      Text(rp, root -> name, strlen(root -> name));
#endif
      y = Display_Tree(root -> son, line + 1, x, y);
      root = root -> brother;
   }
   return y;
}

int
Create_Gadgets()
/* Create the Volumes gadgets. Return the id of the first one */
{
   struct Tree_t *t;
   struct Gadget *gad = mainwindow -> FirstGadget, *lastgadget = NULL;
   struct IntuiText *tex;
   int id = 1, result;
   int gady = w1Gadget2.TopEdge;

/* First, transfer the gadget imageries into chip ram */
   chippointer = (USHORT *)
       AllocMem(sizeof(w1ImageData1), MEMF_PUBLIC | MEMF_CHIP);
   if (chippointer == 0) {
      printf("*** Couldn't allocate in chip mem...\n");
      exit(0);
   }
   memcpy(chippointer, w1ImageData1, sizeof(w1ImageData1));
   w1Image1.ImageData = chippointer;

   chippointer2 = (USHORT *)
       AllocMem(sizeof(w1ImageData2), MEMF_PUBLIC | MEMF_CHIP);
   if (chippointer2 == 0) {
      printf("*** Couldn't allocate in chip mem...\n");
      exit(0);
   }
   memcpy(chippointer2, w1ImageData2, sizeof(w1ImageData2));
   w1Image2.ImageData = chippointer2;

   while (gad -> NextGadget) {
      gad = gad -> NextGadget;
   }
   lastgadget = gad;
   result = lastgadget -> GadgetID + 1;
   id = result;
   t = the_tree -> brother;
   while (t) {

/* Dynamically create the gadget, using w1Gadget2 as template */
      gad = (struct Gadget *) malloc(sizeof(*gad));
      memcpy(gad, & w1Gadget2, sizeof(*gad));
      gad -> TopEdge = gady;
      gady += BETWEEN_GADGETS + w1Gadget2.Height;
      gad -> GadgetID = id++;

/* Link the previous gadget to the one we're building up */
      if (lastgadget) lastgadget -> NextGadget = gad;
      lastgadget = gad;
      gad -> NextGadget = NULL;

/* And now create its IntuiText structure, using w1IText1 */
      tex = (struct IntuiText *) malloc(sizeof(*tex));
      memcpy(tex, & w1IText1, sizeof(*tex));
      tex -> IText = (char *) malloc(sizeof(t -> name) + 1);
      strcpy(tex -> IText, t -> name);

/* Link the text to the gadget */
      gad -> GadgetText = tex;

      nbvolumes++;
      t = t -> brother;
   }
   return result;
}

void
Display_Dir(int line)
/* Display the directory of the volume in the window, starting at the */
/* line in the config file */
{
   struct Tree_t *t;
   char dir[128];
   struct Window *w = mainwindow;
   struct Gadget *gad = NULL, *lastgadget;
   struct IntuiText *tex = NULL;
   int gady = w1Gadget2.TopEdge;
   int id = 2;

   assert(mainwindow);

/* Warning!!! Here, I assume the 1st gadget is the scrollbar, so I */
/* start from the second... */
   lastgadget = w -> FirstGadget;
   if (! the_tree) the_tree = Build_Tree();
   dir[0] = '\0';

   actualline = 1;
   SetAPen(w -> RPort, 0);
   RectFill(w -> RPort, UPPERX, UPPERY + 2, WIDTH - 4, HEIGHT - 2);
   SetAPen(w -> RPort, 2);
   (void) Display_Tree(the_tree -> brother, line, UPPERX + DELTAX, UPPERY);
}

int
Locate_Vol(char *vol)
/* Return first line where vol (dh0:, dh1:, ...) appears */
{
   struct Tree_t *t = the_tree -> brother;

   while (t && stricmp(vol, t -> name)) {
      t = t -> brother;
   }
   if (t) return t -> line;
   else assert(0);
}

void
Display_Window()
/* Main routine for the graphic display */
{
   struct IntuiMessage *message;
   long class, code;
   int i, fin = 0;
   int firstline = 1, previousline = 1, prev2;
   struct Gadget *g;
   struct PropInfo *info;
   struct Tree_t *highlight;

   IntuitionBase = OpenLibrary("intuition.library", 0);
   GfxBase = (struct GfxBase *) OpenLibrary("graphics.library", 0);

#ifdef GRAPHICS
   if (GfxBase -> DisplayFlags & PAL) {  /* we're in PAL, open a big one */
      linesperwindow = LINESPAL;
      NewScreenStructure.Height += 55;
      w1NewWindowStructure1.Height += 55;
      HEIGHT += 55;
      w1Gadget1.Height += 55;
   }
   if (! workbenchscreen) {
      mainscreen = OpenScreen(& NewScreenStructure);
      Palette[0] = color0;
      Palette[1] = color1;
      Palette[2] = color2;
      Palette[3] = color3;
      LoadRGB4(&mainscreen -> ViewPort, Palette, PaletteColorCount);
      w1NewWindowStructure1.Screen = mainscreen;
   }
   else {
      w1NewWindowStructure1.Type = WBENCHSCREEN;
   }
   w1NewWindowStructure1.Title = (char *) malloc(sizeof(WINDOWTITLE));
   strcpy(w1NewWindowStructure1.Title, WINDOWTITLE);
   w1Gadget1.NextGadget = NULL;
   mainwindow = OpenWindow(& w1NewWindowStructure1);
#endif
   Display_Dir(1);
#ifdef GRAPHICS
   volumesid = Create_Gadgets();
   info = (struct PropInfo *) w1Gadget1.SpecialInfo;
   info -> VertBody = (0xffff * (linesperwindow - 3)) / (configlines + 1);
   RefreshGadgets(mainwindow -> FirstGadget, mainwindow, NULL);

   while (! fin) {
      while ((message = (struct IntuiMessage *)
                           GetMsg(mainwindow -> UserPort))) {
         class = message -> Class;
         code = message -> Code;
         ReplyMsg(message);

         switch(class) {
            case CLOSEWINDOW:
               fin = 1;
               break;
            case GADGETUP:
               g = (struct Gadget *) message -> IAddress;
               if (g -> GadgetID >= volumesid) {
                  firstline = Locate_Vol(g -> GadgetText -> IText);
                  w1w1Gadget1SInfo.VertPot = (firstline * 0xffff) / configlines;
               }
               else
                  firstline = ((configlines - 1)* w1w1Gadget1SInfo.VertPot) / 0xffff;
                  prev2 = firstline;

/* Take care of trunc problems */
/*
               printf("pot: %d, previous: %d, lpw: %d, first: %d\n",
                  w1w1Gadget1SInfo.VertPot,
                  previousline, linesperwindow, firstline);
*/
               if (previousline + linesperwindow - 2 <= firstline &&
                   firstline <= previousline + linesperwindow + 2) {
                      firstline = previousline + linesperwindow;
                      previousline = prev2;
                   }
               else
               if (previousline - linesperwindow - 2 <= firstline &&
                     firstline <= previousline - linesperwindow + 2) {
                        firstline = previousline - linesperwindow;
                        previousline = prev2;
                     }
               if (firstline == 0) firstline = 1;
               if (firstline > configlines - linesperwindow - 1)
                  firstline = configlines - linesperwindow - 1;
/*
               printf("after, first: %d\n", firstline);
*/
               if (previousline != prev2) previousline = firstline;
               Display_Dir(firstline);
               RefreshGadgets(mainwindow -> FirstGadget, mainwindow, NULL);
               break;

            case GADGETDOWN:
/*
               printf("Gadgetdown, class=%d, code=%d\n", class, code);
               g = (struct Gadget *) message -> IAddress;
               printf("Gadgetdown, gadget id: %d\n", g -> GadgetID);
*/
               break;

            case MOUSEBUTTONS:
               if (message -> MouseX >= UPPERX && message -> MouseY>=UPPERY
                   && code == SELECTDOWN) {
                  Find_Dir_Coo(message -> MouseX, message -> MouseY, firstline);
                  fin = 1;
               }
               break;

            case MOUSEMOVE:
               printf("Mousemove\n");
               highlight = get_line(the_tree, firstline + (message -> MouseY / DELTAY) - 1);
               SetAPen(mainwindow -> RPort, 1);
               Move(mainwindow -> RPort, highlight -> x, message -> MouseY);
               Text(mainwindow -> RPort, highlight -> name, strlen(highlight -> name));
               break;

            default:
/*
               printf("Class=%d, Code = %d\n", class, code);
*/
               break;

         } /* switch class */
      } /* while message */
   }
   if (mainwindow) CloseWindow(mainwindow);
   if (mainscreen) CloseScreen(mainscreen);
#endif
}

int
main(int argc, char **argv)
{
  int i = 1;
  int change_dir = 1;

  while (argv[i][0] == '-') {
    switch(argv[i][1]) {
      case 'c' :         /* create config file */
        if (argc == i + 1) Usage();
        if (argv[i][2] == 'v') verbose = 1;
        update_list = 1;
        break;
      case 'a' :         /* show ambiguities */
        if (argc == i) Usage();
        show_ambiguities = 1;
        break;
      case 'n' :
         if (i + 2 != argc && i + 3 != argc) Usage();
         no_expand = 1;
         break;
      case 'e' :
         if (i + 2 != argc && i + 3 != argc) Usage();
         exact_cd = 1;
         break;
      case 'u' :
         if (i + 1 != argc) Usage();
         change_dir = 0;
         no_graphics = 1;
         Add_Current_Dir();
         break;
      case 'w' :
         workbenchscreen = 1;
         break;
      case '0' :
         color0 = atow(&argv[i][2]);
         break;
      case '1' :
         color1 = atow(&argv[i][2]);
         break;
      case '2' :
         color2 = atow(&argv[i][2]);
         break;
      case '3' :
         color3 = atow(&argv[i][2]);
         break;
      default :
         Usage();
    } /* switch */
    i++;
  } /* while */

  if (! no_graphics && i == argc) {
      Display_Window();
      exit(0);
  }
  else if (update_list)
     Update(&argv[i], argc - 2);
  else if (show_ambiguities) {
     printf("Ambiguities for %s\n", argv[i]);
     Show_Ambiguities(argv[i]);
  }
  else if (change_dir)
     Change_Dir(argv[i], (argc == i + 2 ? atoi(argv[i+1]) : 1));

/*
  free_all_locks();
*/
  if (chippointer) FreeMem(chippointer, sizeof(w1ImageData1));
  if (chippointer2) FreeMem(chippointer2, sizeof(w1ImageData2));
  exit(0);
}

