/****************************************************************
*
* a new FSEL placed in public domain by D N Korte 26 Dec 86.
* Do with it whatever you want...
*
* modified for undelete program & GNU c by T Blight, Dec 1992
*
*****************************************************************/

#include <aesbind.h>
#include <gemfast.h>
#include <osbind.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include "undel.h"

#define QUIT     0
#define CONTINUE 1

#define MAXFILES 144	  /* abs max 327 (overflow FS_calc_curfirst) */
#define WINDSIZE   9      /* size of file window */
#define DIAMOND_THINGY 7

/*** indexes into rs_object ***/
#define CANCBUT 2
#define OKBUT   3
#define NEWNAME 4
#define WINDOW  5
#define DRVA    6
#define DRVB    7
#define DRVC    8
#define DRVD    9
#define DRVE   10
#define DRVF   11
#define SLEXTN0 12
#define SLEXTN1 13
#define SLEXTN2 14
#define SLEXTN3 15
#define SLEXTN4 16
#define EXTN3   17
#define EXTN4   18
#define FNAM1   19
#define FNAM2   20
#define FNAM3   21
#define FNAM4   22
#define FNAM5   23
#define FNAM6   24
#define FNAM7   25
#define FNAM8   26
#define FNAM9   27
#define FILEBOX 28
#define TOPBAR  29
#define UARROW  30
#define SLIDBOX 31
#define MVSLIDE 32
#define DARROW  33
#define TOPTEXT 34

struct finfo {
    char name[9];	/* name of file */
    char extn[4];	/* extension of it */
    BOOL  dirflag;	/* FALSE if not a directory, TRUE if is directory */
};

PRIVATE void FS_dir_dis( struct finfo[], int[], int, int);
PRIVATE void FS_dir_select_good( int, const char *, const char *, const char *,
                                 char *, char *, struct finfo[], int, int[],
                                 int *, int *);
PRIVATE int FS_dir_read(char *drivs, char *path, struct finfo files[],
                        int *numfiles, char *dispath);
PRIVATE void FS_shift_curfirst(int, int, int *);
PRIVATE void FS_calc_curfirst(int, int, int *);
PRIVATE void FS_calc_slid_box(int, int, BOOL);
PRIVATE void FS_setname( int, char *);
PRIVATE void FS_path_lengthen( char *, int, struct finfo[], char *);
PRIVATE void FS_path_shorten( char *, char *);
PRIVATE void FS_pathtrunc( char *, const char *);
PRIVATE BOOL FS_wildmatch( const char *, const char *);
PRIVATE void FS_exchinfo( struct finfo *, struct finfo *);
PRIVATE int FS_find_last_delim(char, char *);
PRIVATE void FS_strxcpy(char *, const char*);



PRIVATE char file_ids[WINDSIZE][17]; /* spot for (10) 16-char formatted filenames in menu */

PRIVATE TEDINFO rs_tedinfo[] = {
    NULL,"________.___","FFFFFFFFFFF",3,0,0,0x1180,0, 0,0,13, /* 0 for newname */
    NULL,".___",        "",           3,0,2,0x1180,0,-2,0,5,  /* 1 for extn0   */
    NULL,".___",        "",           3,0,2,0x1180,0,-2,0,5,  /* 2 for extn1   */
    NULL,".___",        "",           3,0,2,0x1180,0,-2,0,5,  /* 3 for extn2   */
    NULL,".___",        "FFF",        3,0,2,0x1180,0,-2,0,5,  /* 4 for extn3   */
    NULL,".___",        "FFF",        3,0,2,0x1180,0,-2,0,5,  /* 5 for extn4   */
    NULL,"",            "",           3,0,2,0x1180,0, 0,0,0   /* 6 for pathname in TOPBAR */
};
/*  ^        ^              ^         ^ ^ ^   ^    ^  ^ ^ ^
    |        |              |         | | |   |    |  | | +-- te_tmplen length of template (+1 for null)
    |        |              |         | | |   |    |  | ----- te_txtlen length of text     (+1 for null)
    |        |              |         | | |   |    |  +------ te_thickness  border thickness (-) outside
    |        |              |         | | |   |    +--------- te_junk2
    |        |              |         | | |   +-------------- te_color packed color information
    |        |              |         | | +------------------ te_just 0=left 1=right 2=centered
    |        |              |         | +-------------------- te_junk1
    |        |              |         +---------------------- te_font 3=system 5=small
    |        |              +------------------------------- *te_pvalid ptr to validation chars
    |        +---------------------------------------------- *te_ptmplt ptr to template
    +------------------------------------------------------- *te_ptext  ptr to text
*/

PRIVATE OBJECT rs_object[] = {
    -1,1,34,G_BOX,      NONE,                        OUTLINED,    0x21100L, 0, 0,38,15, /* 0 dialog box */
    2,-1,-1,G_STRING,   NONE,                        NORMAL,            0L, 2, 1,34,1,  /* 1 TITLE */
    3,-1,-1,G_BUTTON,   SELECTABLE|EXIT,             NORMAL,(unsigned long)"CANCEL",22, 3, 6,1,  /* 2 CANCBUT */
    4,-1,-1,G_BUTTON,   SELECTABLE|EXIT|DEFAULT,     NORMAL,(unsigned long)"OK",30, 3, 6,1,  /* 3 OKBUT */
    5,-1,-1,G_FTEXT,    EDITABLE,                    NORMAL,(unsigned long)&rs_tedinfo[0],22, 5,12,1,  /* 4 NEWNAME */
    6,-1,-1,G_IBOX,     NONE,                        NORMAL,     0xFF1100L, 2, 3,18,11, /* 5 WINDOW (outline of pretend window) */
    7,-1,-1,G_BUTTON,   SELECTABLE|EXIT|RBUTTON,     NORMAL,(unsigned long)"A:",22, 7, 4,1,  /* 6 DRVA */
    8,-1,-1,G_BUTTON,   SELECTABLE|EXIT|RBUTTON,     NORMAL,(unsigned long)"B:",27, 7, 4,1,  /* 7 DRVB */
    9,-1,-1,G_BUTTON,   SELECTABLE|EXIT|RBUTTON,     NORMAL,(unsigned long)"C:",32, 7, 4,1,  /* 8 DRVC */
    10,-1,-1,G_BUTTON,  SELECTABLE|EXIT|RBUTTON,     NORMAL,(unsigned long)"D:",22, 9, 4,1,  /* 9 DRVD */
    11,-1,-1,G_BUTTON,  SELECTABLE|EXIT|RBUTTON,     NORMAL,(unsigned long)"E:",27, 9, 4,1,  /* 10 DRVE */
    12,-1,-1,G_BUTTON,  SELECTABLE|EXIT|RBUTTON,     NORMAL,(unsigned long)"F:",32, 9, 4,1,  /* 11 DRVF */
    13,-1,-1,G_FBOXTEXT,SELECTABLE|TOUCHEXIT,        NORMAL,(unsigned long)&rs_tedinfo[1],22,11, 4,1,  /* 12 SLEXTN0 */
    14,-1,-1,G_FBOXTEXT,SELECTABLE|TOUCHEXIT,        NORMAL,(unsigned long)&rs_tedinfo[2],27,11, 4,1,  /* 13 SLEXTN1 */
    15,-1,-1,G_FBOXTEXT,SELECTABLE|TOUCHEXIT,        NORMAL,(unsigned long)&rs_tedinfo[3],32,11, 4,1,  /* 14 SLEXTN2 */
    16,-1,-1,G_BOX,     SELECTABLE|TOUCHEXIT,        NORMAL,     0xFE1100L,22,13, 2,1,  /* 15 SLEXTN3 */
    17,-1,-1,G_BOX,     SELECTABLE|TOUCHEXIT,        NORMAL,     0xFE1100L,30,13, 2,1,  /* 16 SLEXTN4 */
    18,-1,-1,G_FBOXTEXT,EDITABLE,                    NORMAL,(unsigned long)&rs_tedinfo[4],24,13, 4,1,  /* 17 EXTN3 */
    28,-1,-1,G_FBOXTEXT,EDITABLE,                    NORMAL,(unsigned long)&rs_tedinfo[5],32,13, 4,1,  /* 18 EXTN4 */
    20,-1,-1,G_STRING,  SELECTABLE|TOUCHEXIT|RBUTTON,NORMAL,(unsigned long)&file_ids[0][0], 0, 0,15,1,  /* 19 FNAM1 (note that for doubleclick */
    21,-1,-1,G_STRING,  SELECTABLE|TOUCHEXIT|RBUTTON,NORMAL,(unsigned long)&file_ids[1][0], 0, 1,15,1,  /* 20 FNAM2 (to work, these objects must */
    22,-1,-1,G_STRING,  SELECTABLE|TOUCHEXIT|RBUTTON,NORMAL,(unsigned long)&file_ids[2][0], 0, 2,15,1,  /* 21 FNAM3 (be G_STRING and must have */
    23,-1,-1,G_STRING,  SELECTABLE|TOUCHEXIT|RBUTTON,NORMAL,(unsigned long)&file_ids[3][0], 0, 3,15,1,  /* 22 FNAM4 (ob_flags exactly as shown. */
    24,-1,-1,G_STRING,  SELECTABLE|TOUCHEXIT|RBUTTON,NORMAL,(unsigned long)&file_ids[4][0], 0, 4,15,1,  /* 23 FNAM5 (When selected, bit 15 of */
    25,-1,-1,G_STRING,  SELECTABLE|TOUCHEXIT|RBUTTON,NORMAL,(unsigned long)&file_ids[5][0], 0, 5,15,1,  /* 24 FNAM6 (form_do return will be */
    26,-1,-1,G_STRING,  SELECTABLE|TOUCHEXIT|RBUTTON,NORMAL,(unsigned long)&file_ids[6][0], 0, 6,15,1,  /* 25 FNAM7 (set if gotten by a */
    27,-1,-1,G_STRING,  SELECTABLE|TOUCHEXIT|RBUTTON,NORMAL,(unsigned long)&file_ids[7][0], 0, 7,15,1,  /* 26 FNAM8 (doubleclick. */
    28,-1,-1,G_STRING,  SELECTABLE|TOUCHEXIT|RBUTTON,NORMAL,(unsigned long)&file_ids[8][0], 0, 8,15,1,  /* 27 FNAM9 */
    29,19,27,G_BOX,     NONE,                        NORMAL,      0x011100L, 2, 4,15,10, /* 28 FILEBOX (box is parent for FNAMs) */
    30,-1,-1,G_BOX,     NONE,                        NORMAL,      0xFF1121L, 2, 3,18,1,  /* 29 TOPBAR */
    31,-1,-1,G_BOXCHAR, TOUCHEXIT,                   NORMAL,    0x01011180L,17, 4, 3,1,  /* 30 UARROW (adjust y later) */
    33,32,32,G_BOX,     TOUCHEXIT,                   NORMAL,      0x011111L,17, 5, 3,7,  /* 31 SLIDBOX (adjust y later) */
    31,-1,-1,G_BOX,     TOUCHEXIT,                   NORMAL,      0x011100L, 0, 0, 3,1,  /* 32 MVSLIDE */
    34,-1,-1,G_BOXCHAR, TOUCHEXIT,                   NORMAL,    0x02011100L,17,12, 3,1,  /* 33 DARROW (adjust y and h later) */
     0,-1,-1,G_TEXT,    LASTOB,                      NORMAL, (unsigned long)&rs_tedinfo[6], 3, 3,16,1   /* 34 TOPTEXT */
};
/*   ^  ^ ^    ^           ^                            ^        ^           ^  ^  ^ ^
     |  | |    |           |                            |        |           |  |  | +-- ob_height height of obj (now chars, but
     |  | |    |           |                            |        |           |  |  +---- ob_width  width  of obj (must cnvt to pixels
     |  | |    |           |                            |        |           |  +------- ob_y      y coord relative to parent
     |  | |    |           |                            |        |           +---------- ob_x      x coord relative to parent
     |  | |    |           |                            |        +---------------------  ob_spec   obj determines meaning (p11-55,60)
     |  | |    |           |                            +------------------------------  ob_state  current state of obj (p.11-61 MM)
     |  | |    |           +-----------------------------------------------------------  ob_flags  attributes for obj (p.11-60 MM)
     |  | |    +-----------------------------------------------------------------------  ob_type   object type (p. 11-59 MM)
     |  | +----------------------------------------------------------------------------  ob_tail   ptr to last child of obj or -1
     |  +------------------------------------------------------------------------------  ob_head   ptr to first child of obj or -1
     +---------------------------------------------------------------------------------  ob_next   ptr to next sibling or -1

*/

PRIVATE int xdial,ydial,wdial,hdial;         /* co-ord for dialog box */

PRIVATE int firstcall = TRUE;                /* for first-run initialization */

/*************************************************************************
 *
 * Function fsel ()
 * This is what we're here for !!!
 * Can't say much more 'cuz MM editor is almost full of characters.
 *
 *   The parameters are as follows:
 *
 *      stat        an integer returned by routine; 0=canc 1=ok
 *      title       a string of up to 34 characters which will
 *                  be displayed as the title or instructions line
 *      path        a string representing the current path.
 *                  This must both start and end with a '\' and
 *                  may include any number of subdirectories
 *                  within it.  The selector will return the
 *                  user's path selection in this variable too
 *                  so it should be a real variable of reasonable
 *                  length (don't make it up as text in the
 *                  parameter list itself!).
 *      driv        An integer indicating which drive to use.
 *                  code is as follows;
 *                      -1 use current default drive
 *                       0 A:
 *                       1 B:
 *                       ...
 *                       5 F:
 *                  The selector will return the user's drive
 *                  selection in this variable too, so you should
 *                  pass the address instead of the value.
 *      sextn       An integer which indicates which of the five
 *                  extension boxes should be used when matching
 *                  filenames.  This is bit-mapped such that:
 *                     0x0001 selects extn0
 *                     0x0002 selects extn1
 *                     0x0004 selects extn2
 *                     0x0008 selects extn3
 *                     0x0010 selects extn4
 *                  More than one may be selected.  As above, the
 *                  selector returns the user's suggestions, so
 *                  this should be passed as an address too.
 *      extn0 ...
 *      extn4       There are 5 extension text variables.  These
 *                  should all be 3 characters long (plus the
 *                  terminator of course) and should not include
 *                  the '.' (dot) which normally preceeds an
 *                  extension.  The first three will not be
 *                  changed by the selector and so may be defined
 *                  as constants in the calling line if desired.
 *                  The last two will be modified by the selector
 *                  and thus must be defined as real variables.
 *                  In all cases, the string must have all 3
 *                  characters defined (use trailing blanks or
 *                  underscores for short extensions).  A '*'
 *                  will match anything; a '?' in any position
 *                  will match any character in that position.
 *      fullname    This is where the selector returns the user's
 *                  selected name and path.  The string should be
 *                  long enough to hold the largest anticipated
 *                  string.  The returned string will include
 *                  the drive specifier (i.e. A:), the full path
 *                  specified, and the file name itself.
 */
int
fsel(const char *title,    /* I  display title for box */
     char *path,     /* IO initial path spec \ ... \ no drive; returns selected path */
     int  *driv,     /* IO selects drive -1=default 0=A: .. 5=F: */
     int  *sextn,    /* IO bitmap selected extn boxes :
                      *    0x0001 = extn0    0x0002 = extn1    0x0004 = extn2
                      *    0x0008 = extn3    0x0010 = extn4
                      */
     char *extn0,
     char *extn1,    /* I  extension text not including "." for each of 1st 3 extn boxes  */
     char *extn2,    /*    NOTE for all 5 boxes this should be 3 chars long, even if spaces   */
     char *extn3,
     char *extn4,    /* IO extension text for last 2 extn boxes (editable) */
     char *fullname) /* O  returns complete path and file */

{

struct finfo files[MAXFILES];           /* holds name, extn, dir for all files */

char newname[12],                       /* holds user-typed name */
     fnam[13],                           /* will build return filename here */
     c,
     drivs[3],                           /* current dir as string i.e. "B:" */
     dispath[17];                       /* displayable pathname (trunc to 16 chars max) */

int  i,fp,ret,x,y,w,h;
int  action,                           /* flag */
     button,                           /* button code from dialog box */
     slidpsn,                          /* slider position 0-1000 */
     goodfiles[MAXFILES],               /* has indices into 'files[]' of files matching extn specifications */
     numfiles,                         /* total number of files in 'files[]' */
     numgood,                           /* number of files noted in 'goodfiles[]' */
     curfirst,                           /* index into 'goodfiles[]' of first displayable file */
     hchar,                               /* height of character in pixels */
     my,                               /* mouse position y coordinate */
     doubleclick,                       /* TRUE if double-clicked on item, else FALSE */
     editfield;                           /* index of field to edit */
int  t;                    /* holds drivemap */

    /*
     * On the very first call to this subroutine we must do some
     * setup stuff...  Primarily adjustments to the resource table
     * to adjust for screen resolution, and to adjust element locations
     * which do not live on even character boundaries.
     */
    if (firstcall) {
        firstcall = FALSE;

        for(i=0; i<=HIGH(rs_object); i++)
            rsrc_obfix(rs_object,i);        /* chg coord from char to pixel */

        /*
         * also adjust size of SLIDBOX and xxARROWs into half-char sizes
         * because 1 char tall is too short to allow boxchar to display the arrows,
         * but 2 chars tall looks silly.
         */
        i=graf_handle(&ret,&hchar,&ret,&ret);
        rs_object[UARROW].ob_height += hchar/2;
        rs_object[DARROW].ob_y      += hchar/2;
        rs_object[DARROW].ob_height += hchar/2;
        rs_object[SLIDBOX].ob_y     += hchar/2;

        /*
         * and similarly, adjust position of filenames in menu box
         * so that 9 fit nicely into a box that tightly fits 10
         */
         for (i=0; i<WINDSIZE; i++) {
             rs_object[FNAM1+i].ob_y += hchar/2;
         } /* for */
    } /* if (firstcall) ... */

    strcpy(newname,"___________");
    rs_tedinfo[0].te_ptext = newname;
    rs_tedinfo[1].te_ptext = extn0 ;
    rs_tedinfo[2].te_ptext = extn1 ;
    rs_tedinfo[3].te_ptext = extn2 ;
    rs_tedinfo[4].te_ptext = extn3 ;
    rs_tedinfo[5].te_ptext = extn4 ;
    rs_tedinfo[6].te_ptext = dispath  ;


    for (i=0; i<=HIGH(rs_tedinfo); i++) {
        rs_tedinfo[i].te_txtlen = strlen(rs_tedinfo[i].te_ptext) + 1 ;
    }

    rs_object[1].ob_spec  = (unsigned long)title;
    FS_pathtrunc(dispath,path);
    for (i=0; i<WINDSIZE; i++) strcpy(&file_ids[i][0],"                ");
    editfield = NEWNAME;

    /*
     * If user passed blanks for editable extensions, this is good
     * but it will look funny -- put in initial underscores instead,
     * also if he just passed unitialized (empty) string do it too.
     * We must assume that string will be long enough...
     */
    if((extn3[0] == ' ') OR (strlen(extn3) == 0)) strcpy(extn3,"___");
    if((extn4[0] == ' ') OR (strlen(extn4) == 0)) strcpy(extn4,"___");

    if (*sextn & 0x0001) rs_object[SLEXTN0].ob_state |= SELECTED;
    if (*sextn & 0x0002) rs_object[SLEXTN1].ob_state |= SELECTED;
    if (*sextn & 0x0004) rs_object[SLEXTN2].ob_state |= SELECTED;
    if (*sextn & 0x0008) rs_object[SLEXTN3].ob_state |= SELECTED;
    if (*sextn & 0x0010) rs_object[SLEXTN4].ob_state |= SELECTED;

    if (*driv  == -1)     *driv = Dgetdrv();            /* get cur logged drive if needed */
    rs_object[(*driv)+DRVA].ob_state |= SELECTED;
    drivs[0] = (*driv) +'A' ;    drivs[1] = ':' ;   drivs[2] = '\0' ;

    t = Drvmap();
    if ((t & 0x01) == 0) rs_object[DRVA].ob_state = DISABLED;
    if ((t & 0x02) == 0) rs_object[DRVB].ob_state = DISABLED;
    if ((t & 0x04) == 0) rs_object[DRVC].ob_state = DISABLED;
    if ((t & 0x08) == 0) rs_object[DRVD].ob_state = DISABLED;
    if ((t & 0x10) == 0) rs_object[DRVE].ob_state = DISABLED;
    if ((t & 0x20) == 0) rs_object[DRVF].ob_state = DISABLED;
    
    form_center (&rs_object,&xdial,&ydial,&wdial,&hdial);
    x=y=w=h=0;
    form_dial (0,x,y,w,h,xdial,ydial,wdial,hdial);      /* reserve room */
    form_dial (1,x,y,w,h,xdial,ydial,wdial,hdial);      /* grow box */
    objc_draw (&rs_object,0,2,xdial,ydial,wdial,hdial); /* draw the dialog box */




    FS_dir_read(drivs, path, files, &numfiles, dispath);
    FS_dir_select_good(*sextn,extn0,extn1,extn2,extn3,extn4,
                    files, numfiles, goodfiles, &numgood, &curfirst);
    FS_calc_slid_box(numgood,curfirst,FALSE);             /* calculate initial slider position */

    FS_dir_dis(files,goodfiles,numgood,curfirst);

    action = CONTINUE;                                       /* look for new button press */
    while (action == CONTINUE) {                           /* loop while operator plays */

        if (rs_object[SLEXTN3].ob_state & SELECTED)     /* setup status of (SL)EXTN3 click-ability */
            rs_object[EXTN3].ob_flags = TOUCHEXIT;
        else
            rs_object[EXTN3].ob_flags = EDITABLE;

        if (rs_object[SLEXTN4].ob_state & SELECTED)     /* setup status of (SL)EXTN4 click-ability */
            rs_object[EXTN4].ob_flags = TOUCHEXIT;
        else
            rs_object[EXTN4].ob_flags = EDITABLE;

        button = form_do(&rs_object,editfield);            /* manage user's actions until a button press */
        if (button & 0x8000) {                            /* if double-click ... */
            doubleclick  = TRUE;
            button      &= ~0x8000;                        /* (remove double-click bit from botton code) */
        }
        else
            doubleclick  = FALSE;

        switch (button) {                                /* now prepare to act upon user's request */
            case CANCBUT: action = QUIT; rs_object[CANCBUT].ob_state &= ~SELECTED; break;
            case OKBUT:   action = QUIT; rs_object[OKBUT].ob_state   &= ~SELECTED; break;

            case DRVA:
            case DRVB:
            case DRVC:
            case DRVD:
            case DRVE:
            case DRVF:    *driv = button - DRVA;
                          drivs[0] = (*driv) + 'A'; drivs[1] = ':';  drivs[2] = '\0';
                          strcpy(path,"\\");
                          FS_pathtrunc(dispath,path);
                          FS_dir_read(drivs,path,files,&numfiles,dispath);
                          FS_dir_select_good(*sextn,extn0,extn1,extn2,extn3,extn4,
                                files,numfiles,goodfiles,&numgood,&curfirst);
                          FS_dir_dis(files,goodfiles,numgood,curfirst);
                          FS_setname(-1,newname);
                          break;

            case SLEXTN0:
            case SLEXTN1:
            case SLEXTN2:
            case SLEXTN3:
            case SLEXTN4: i = 0x01 << (button - SLEXTN0);
                          *sextn ^= i ;
                          FS_dir_select_good(*sextn,extn0,extn1,extn2,extn3,extn4,
                                files,numfiles,goodfiles,&numgood,&curfirst);
                          FS_dir_dis(files,goodfiles,numgood,curfirst);
                          FS_setname(-1,newname);
                          break;

            case FNAM1:
            case FNAM2:
            case FNAM3:
            case FNAM4:
            case FNAM5:
            case FNAM6:
            case FNAM7:
            case FNAM8:
            case FNAM9: i = button - FNAM1;
                        fp= goodfiles[curfirst+i] ;                            /* (fp points into files[]) */
                        if (files[fp].dirflag) {                            /* if a directory of any flavor */
                            if (strcmp(files[fp].name,".") == 0)             /* current directory */
                                  /* do nothing special */ ;
                              else if (strcmp(files[fp].name,"..") == 0) {    /* parent directory */
                                  FS_path_shorten(path,dispath);
                                  FS_dir_read(drivs,path,files,&numfiles,dispath);
                                  FS_dir_select_good(*sextn,extn0,extn1,extn2,extn3,extn4,
                                    files,numfiles,goodfiles,&numgood,&curfirst);
                            }
                            else {                                            /* another directory */
                                FS_path_lengthen(path,fp,files,dispath);
                                FS_dir_read(drivs,path,files,&numfiles,dispath);
                                  FS_dir_select_good(*sextn,extn0,extn1,extn2,extn3,extn4,
                                    files,numfiles,goodfiles,&numgood,&curfirst);
                              }
                              FS_dir_dis(files,goodfiles,numgood,curfirst);    /* then always display the selected directory */
                              FS_setname(-1,newname);
                          }                                                    /* end of 'if a directory' stuff */
                          else {                                            /* here it must be just a file */
                             FS_setname(i,newname);
                             if (doubleclick) {                                /* a doubleclick causes default OKBUT too */
                                 action   = QUIT;
                                 button   = OKBUT;
                             }
                          }
                          break;
            case MVSLIDE: if (rs_object[SLIDBOX].ob_height==rs_object[MVSLIDE].ob_height) break;     /* do nothing if full size */
                          slidpsn = graf_slidebox(&rs_object,SLIDBOX,MVSLIDE,1);
                          FS_calc_curfirst(slidpsn,numgood,&curfirst);
                          FS_calc_slid_box(numgood,curfirst,TRUE);
                          FS_dir_dis(files,goodfiles,numgood,curfirst);
                          FS_setname(-1,newname);
                          break;
            case SLIDBOX: graf_mkstate(&ret,&my,&ret,&ret);
                          if (my > (ydial+rs_object[SLIDBOX].ob_y+rs_object[MVSLIDE].ob_y))
                             FS_shift_curfirst(WINDSIZE,numgood,&curfirst);
                          else
                               FS_shift_curfirst(-WINDSIZE,numgood,&curfirst);
                          FS_calc_slid_box(numgood,curfirst,TRUE);
                          FS_dir_dis(files,goodfiles,numgood,curfirst);
                          FS_setname(-1,newname);
                          break;
            case UARROW:  FS_shift_curfirst(-1,numgood,&curfirst);
                          FS_calc_slid_box(numgood,curfirst,TRUE);
                          FS_dir_dis(files,goodfiles,numgood,curfirst);
                          FS_setname(-1,newname);
                          break;
            case DARROW:  FS_shift_curfirst(1,numgood,&curfirst);
                          FS_calc_slid_box(numgood,curfirst,TRUE);
                          FS_dir_dis(files,goodfiles,numgood,curfirst);
                          FS_setname(-1,newname);
                          break;

                          /*
                           * to get to EXTN3 then EXTN3 must have been TOUCHEXIT
                           * which means that SLEXTN3 must have been SELECTED.
                           * When we touched on the EXTN3 field we signify that we
                           * want to edit it -- therefore we unSELECT the box so
                           * that we can force a directory read when it next gets selected.
                           */
            case EXTN3:      rs_object[EXTN3].ob_flags = EDITABLE;
                          objc_change(&rs_object,SLEXTN3,0,xdial,ydial,wdial,hdial,NORMAL,1);
                          *sextn &= ~0x0008;
                          editfield = EXTN3;
                          break;

                          /* similar logic here... */
            case EXTN4:      rs_object[EXTN4].ob_flags = EDITABLE;
                          objc_change(&rs_object,SLEXTN4,0,xdial,ydial,wdial,hdial,NORMAL,1);
                          *sextn &= ~0x0010;
                          editfield = EXTN4;
                          break;
        }    /* end of switch(button)    */
    }        /* end of while(action) */

    form_dial (2,x,y,w,h,xdial,ydial,wdial,hdial);  /* shrink dialog box */
    form_dial (3,x,y,w,h,xdial,ydial,wdial,hdial);  /* release its room */

    for (i=0; i<8; i++) {                            /* build formatted filename from newname */
        if ((c=newname[i]) == ' ') break ;            /* (scan name part until ends at space */
        if ( c == '_' ) break;                        /* (also stops at _ in case no name was ever selected! */
        fnam[i]   = c;
        fnam[i+1] = '\0';
    }
    if((newname[8] != ' ') AND (newname[8] != '_')) {    /* (if there is an extension) */
        fnam[i++] = '.';
        fnam[i]   = '\0';
        strcat(&fnam[i],&newname[8]);
    }

    strcpy(fullname,drivs);                            /* finally, build a returnable name with full path info too */
    strcat(fullname,path);
    strcat(fullname,fnam);

    if (button == CANCBUT) return (0);                /* then return */
    return(1);
} /* fsel() */


/************************************************************
 * function FS_shift_curfirst() is called when the slider
 * is moved by an arrow key or a pageup/pagedn request.
 * it returns a new value for curfirst (index into goodfiles)
 */
PRIVATE void
FS_shift_curfirst(int  inc,      /* typically -1,1 (arrow), -9,+9 pagedn,pageup */
                  int  numgood,  /* total number of good files */
                  int *curfirst) /* returns new value for curfirst */
{
    *curfirst += inc;        /* change it as requested */

    /* then fix it if necessary */
    if (*curfirst > (numgood-WINDSIZE)) *curfirst = numgood-WINDSIZE;
    if (*curfirst < 0) *curfirst = 0;
} /* FS_shift_curfirst() */


/*********************************************************
 * function FS_calc_curfirst() is called after
 * a boxslide operation to find new value for curfirst.
 */
PRIVATE void
FS_calc_curfirst(int  slidpsn,        /* slider psn as returned from graf_slidbox 0-1000 */
                 int  numgood,        /* total number of good files */
                 int *curfirst)       /* returned index into goodfiles */
{
    *curfirst = (numgood * (slidpsn/10)) / 100 ;   /* funny scaling to avoid arith overflow */
    if (*curfirst > (numgood - WINDSIZE)) *curfirst = numgood - WINDSIZE;
} /* FS_calc_curfirst() */


/*****************************************************
 * function FS_calc_slid_box() is called to calculate
 * new dimensions for the moving box, and draw it if necessary.
 */
PRIVATE void
FS_calc_slid_box(int numgood,            /* number of good files */
                 int curfirst,           /* index into goodfiles[] */
                 BOOL    drawit)         /* FALSE = don't draw, TRUE = please draw */
{
    if (numgood <= WINDSIZE) {               /* then sliding box is full size */
        rs_object[MVSLIDE].ob_y      = 0;    /* position at top (rel to parent) */
        rs_object[MVSLIDE].ob_height = rs_object[SLIDBOX].ob_height;
    }
    else {
        rs_object[MVSLIDE].ob_height = (rs_object[SLIDBOX].ob_height * WINDSIZE) / numgood ;
        rs_object[MVSLIDE].ob_y      = (rs_object[SLIDBOX].ob_height * curfirst) / numgood ;
    }

    if (drawit) objc_draw(&rs_object,SLIDBOX,2,xdial,ydial,wdial,hdial);
} /* FS_calc_slid_box() */


/***************************************************************
 * function FS_setname() sets a filename or blanks into the NEWNAME field
 * if i== -1 then blanks are set, otherwise file_ids[i] is set
 */
PRIVATE void FS_setname( int i, char *newname)
{
    if (i == -1)
        strcpy(newname,"___________");
    else {
        strncpy(newname,&file_ids[i][2],8);
        strncpy(&newname[8],&file_ids[i][11],3);
    }
    objc_draw(&rs_object,NEWNAME,1,xdial,ydial,wdial,hdial);
} /* FS_setname() */


/***************************************
 * function FS_dir_read() reads specified directory
 * and stores names in 'files' array.  Also adjusts numfiles.
 * function returns 0 on error 1 on success
 */
PRIVATE int FS_dir_read(char *drivs, char *path, struct finfo files[],
                        int *numfiles, char *dispath)
{
char fullname[14];                            /* full name of file from dta */
int   dotat,error,i,numdirs,exch;
char  fullpath[80];                            /* full path spec with driv, pathlist, and *.*  */
struct _dta *dtap;

    *numfiles = 0;                            /* no files found yet */
    dtap = (struct _dta *)Fgetdta();

    do {
        strcpy(fullpath, drivs);
        strcat(fullpath, path);
        strcat(fullpath, "*.*");
        error = d_First(drivs, path, FA_DIR);
        if (error == -33) {
            i = form_alert(2,"[1][You have specified either|an empty disk or a nonexistant path...][CONTINUE|RETRY|BACKUP PATH]");
            switch(i) {
                case 1: return(1);            /* quit indicating success (?) */
                case 2: break;                /* just do it again */
                case 3: FS_path_shorten(path,dispath);
            }
        }
        else if (error != 0) {
            char  emsg[80];
            sprintf( emsg, "[1][%s|%s][QUIT]", sys_errlist[-error], fullpath);
            form_alert(1,emsg);
            exit(1);              /* quit  */
        } /* if */
    } while (error == -33);

    while (error == 0) {
        strcpy(fullname,dtap->dta_name);
        if(fullname[0] == '.') {               /* then a directory . or .. */
            strcpy(files[*numfiles].name,fullname);
            strcpy(files[*numfiles].extn,"");
        }
        else {                                  /* a real file */

            dotat = FS_find_last_delim('.',fullname);
            if (dotat == -1) {
                strcpy(files[*numfiles].extn,"");
                strcpy(files[*numfiles].name,fullname);
            }
            else {
                strcpy(files[*numfiles].extn,&fullname[dotat+1]);
                strncpy(files[*numfiles].name,fullname,dotat);
                files[*numfiles].name[dotat] = '\0';
            }
        }
        files[*numfiles].dirflag = (dtap->dta_attribute & FA_DIR) != 0;
        (*numfiles)++;                                /* count the discovered file */
        if (*numfiles > MAXFILES-1) {                /* is this possible??? */
            *numfiles = MAXFILES-1;                    /* if so, just ignore later files */
            return(1);                                /* kinda sloppy huh ??? */
        }
        error = d_Next();                            /* then look for another */
} /* while */

    /* now find all directories and put at beginning of list */
    numdirs = 0;
    for (i=0; i< *numfiles; i++) {
        if(files[i].dirflag) {
            FS_exchinfo(&files[numdirs],&files[i]); /* if dir, swap to beginning of list */
            numdirs++;                                /* and count it */
        }
    }

    /* now sort the directories */
    if ((numdirs) > 1) {
        do {
            exch = FALSE;
            for(i=0; i<numdirs-1; i++) {            /* for all directories */
                if(strcmp(files[i].name,files[i+1].name) > 0) {
                    FS_exchinfo(&files[i],&files[i+1]);
                }
            }
        } while (exch == TRUE);
    }

    /* and now sort real files */
    if ((*numfiles-numdirs) > 1) {
        do {
            exch = FALSE;
            for(i=numdirs; i< *numfiles-1; i++) {    /* for all files */
                if(strcmp(files[i].name,files[i+1].name) > 0) {
                    FS_exchinfo(&files[i],&files[i+1]);
                    exch = TRUE;
                }
            }
        } while (exch == TRUE);
    }

    /* all done so return status */
    if (error == -ENMFILES) return (1) ;                /* success if error = "no more files" */
     return(0);                                         /* else a problem */
} /* FS_dir_read() */


/**************************************************
 * function FS_dir_select_good() scans files[] for files matching at least one of
 * specified extensions.  Counts matches in numgood, stores index (into files[])
 * in goodfiles[]. Also sets curfirst = 0 so later display will start at beginning.
 */
PRIVATE void
FS_dir_select_good( int sextn,       /* the mask specifying checkable extensions */
                    const char *extn0,     /* the extensions to try matching */
                    const char *extn1,
                    const char *extn2,
                    char *extn3,
                    char *extn4,
                    struct finfo files[],
                    int numfiles,
                    int goodfiles[],
                    int *numgood,
                    int *curfirst)
{
int i;

    *curfirst = 0;
    *numgood  = 0;

    for (i=0; i<numfiles; i++) {                    /* always match directories */
        if(files[i].dirflag) {
            goodfiles[(*numgood)++] = i;
            continue;
        }
        if((sextn & 0x01) AND FS_wildmatch(extn0,files[i].extn)) {
            goodfiles[(*numgood)++] = i;
            continue;
        }
        if((sextn & 0x02) AND FS_wildmatch(extn1,files[i].extn)) {
            goodfiles[(*numgood)++] = i;
            continue;
        }
        if((sextn & 0x04) AND FS_wildmatch(extn2,files[i].extn)) {
            goodfiles[(*numgood)++] = i;
            continue;
        }
        if((sextn & 0x08) AND FS_wildmatch(extn3,files[i].extn)) {
            goodfiles[(*numgood)++] = i;
            continue;
        }
        if((sextn & 0x10) AND FS_wildmatch(extn4,files[i].extn)) {
            goodfiles[(*numgood)++] = i;
            continue;
        }
    }
}

/*
 * function FS_dir_dis() displays current directory in window.  It displays
 * at max WINDSIZE names, starting at 'curfirst' in files[].
 */
PRIVATE void
FS_dir_dis( struct finfo files[],
            int          goodfiles[],
            int          numgood,
            int          curfirst)
{
int i,j;

    /* first, clear out display strings */
    for (i=0; i<WINDSIZE; i++) {
        for(j=0; j<16; j++)
            file_ids[i][j] = ' ';
        file_ids[i][16]    = '\0';
    }

    /* now build  at most WINDSIZE filenames */
    for (i=0,j=curfirst; i<WINDSIZE; i++,j++) {
        if (j >= numgood) break;                                /* in case not enough good files to fill window */
        FS_strxcpy(&file_ids[i][2],files[goodfiles[j]].name);    /* put in basic filename */
        if (strlen(files[goodfiles[j]].extn) > 0) {                /* if there is an extension... */
            file_ids[i][10] = '.';
            FS_strxcpy(&file_ids[i][11],files[goodfiles[j]].extn);
        }
        if (files[goodfiles[j]].dirflag)
            file_ids[i][0]  = DIAMOND_THINGY;                    /* put in funny box if its a directory */
    }

    /* and finally 'print' all to screen */
    for (i=0; i< WINDSIZE; i++)
        rs_object[FNAM1+i].ob_state = NORMAL;                    /* unselect all filenames */
    objc_draw(&rs_object,FILEBOX,2,xdial,ydial,wdial,hdial);    /* blank the FILEBOX then write in all filenames */
    FS_calc_slid_box(numgood,curfirst,1);                        /* redraw sliding box too */
    objc_draw(&rs_object,TOPBAR, 0,xdial,ydial,wdial,hdial);    /* redraw TOPBAR to erase old path name */
    objc_draw(&rs_object,TOPTEXT,0,xdial,ydial,wdial,hdial);    /* then redraw TOPTEXT  so that new path is shown */
}


/*******************************************************
 * function FS_path_lengthen() is called to add a newly
 * selected name to the current pathlist.  It is assumed that
 * the newly selected name is a directory...
 */
PRIVATE void
FS_path_lengthen( char *path,    /* the current path name (ends with '\') */
                  int   index,   /* index into FS_files[] of name to add  */
                  struct finfo files[],
                  char * dispath)       /* the truncated displayable version of same */
{
    strcat(path,files[index].name);                /* add the name itself */
    if (strlen(files[index].extn) > 0) {          /* (if there is an extension */
        strcat(path,".");                        /* then append it */
        strcat(path,files[index].extn);
    }
    strcat(path,"\\");                            /* and put on trailing backslash */
    FS_pathtrunc(dispath,path);                    /* make displayable version */
} /* FS_path_lengthen() */

/* 
 * function FS_path_shorten() shortens the current path. 
 * It is called after click on ".."
 * Note that the path must always start and end with a '\' 
 * character (will always have a length of at least 1).
 */
PRIVATE void FS_path_shorten( char *path, char *dispath)
{
int len,i;
	
	len=strlen(path);
	if (len == 1) return;				/* already minimum path */
	i=len-2;							/* points at char before trailing slash */
	while(path[i] != '\\') i-- ;		/* keep looking for a slash */
	path[i+1] = '\0' ;					/* and put a terminator after it */
	FS_pathtrunc(dispath,path);			/* make displayable version */
}
 
/* 
 * function pathtrunc() truncates real pathname into shorter displayable version
 */
PRIVATE void FS_pathtrunc( char *dpath,	/* the output (destination) string */
                           const char *spath)	/* the (maybe too long) source str */
{
int slen,i,is,id;

	if((slen=strlen(spath)) <= 16) strcpy(dpath,spath);	/* the trivial case */
	else {
		is = slen - 1 ;					/* index of last char in string */
		id = 15 ; 						/* last position in destination string */
		for(i=0; i<14; i++)				/* copy 14 chars into 16 char field */
			dpath[id--] = spath[is--];
		dpath[0] = '<';					/* and put little flags at beginning */
		dpath[1] = '<';
	}
}

/* 
 * function FS_wildmatch() checks a specified extension
 * against a mask value for match, and returns TRUE if matched, else FALSE.
 * Cases causing match are as follows:
 *     exact match
 *     mask value is "*"
 *     match on all existing characters, with any '?' in mask matching anything
 * Note that the mask value may have trailing spaces (" ") or underbars ("_") as
 * a result of the object template process.  These are considered junk and are
 * removed from the comparison process.
 */
PRIVATE BOOL FS_wildmatch( const char *mask, const char *candidate)
{
int masklen,i;

   if(mask[0]=='*') return(TRUE);         /*  a '*' in mask matches anything */

   masklen = strlen(mask);                /* will elim trailing spaces and underbars */
   for(i=masklen-1; i>0; i--) 							/* check all but first char to see if it is ignorable */
      if((mask[i]==' ') OR (mask[i]=='_')) masklen=i;	/* adjust length word to effect ignorance */
   if(strlen(candidate) > masklen) return(FALSE);  	/* candidate .OLD should not match mask .O */
		
   for(i=0; i<masklen; i++) {             /* now look at all valid chars to check for match */
      if(mask[i] == '?') continue;        /* a '?' in mask matches anything in that position */
      if(toupper(mask[i]) != toupper(candidate[i])) return(FALSE);	/* oops -- no match */
   }
	
   return(TRUE);										/* must have matched ! */
}
	
	

/*
 * function FS_exchinfo() exchanges contents of two 'finfo' style structures 
 */		
PRIVATE void FS_exchinfo( struct finfo *x, struct finfo *y)
{
struct finfo t;

	 t = *x;	/* caution -- */
	*x = *y;	/* this all-in-one assignment is an extension that works in MM C */
	*y =  t;	/* other C's take care ... */
}
	

PRIVATE int FS_find_last_delim(char delim, char *string)
{
int i,spot;

	spot = -1;
	for (i=0; i<strlen(string); i++) 
		if (string[i] == delim) spot = i;
	return(spot);
}

/*
 * function FS_strxcpy() is just like strcpy except it does not copy 
 * the terminating \0
 */
PRIVATE void FS_strxcpy(char *dest, const char *sour)
{
char c;
	while ((c=*sour++) != '\0') *dest++ = c ;
}

/************************* end of fsel.c ************************/
