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

            T H E    P I N E    M A I L   S Y S T E M

   Laurence Lundblade and Mike Seibel
   Networks and Distributed Computing
   Computing and Communications
   University of Washington
   Administration Building, AG-44
   Seattle, Washington, 98195, USA
   Internet: lgl@CAC.Washington.EDU
             mikes@CAC.Washington.EDU

   Please address all bugs and comments to "pine-bugs@cac.washington.edu"

   Copyright 1989, 1990, 1991, 1992  University of Washington

    Permission to use, copy, modify, and distribute this software and its
   documentation for any purpose and without fee is hereby granted, provided
   that the above copyright notice appears in all copies and that both the
   above copyright notice and this permission notice appear in supporting
   documentation, and that the name of the University of Washington not be
   used in advertising or publicity pertaining to distribution of the software
   without specific, written prior permission.  This software is made
   available "as is", and
   THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
   WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
   NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
   INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
   LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
   (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
   WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  

   Pine is in part based on The Elm Mail System:
    ***********************************************************************
    *  The Elm Mail System  -  $Revision: 2.13 $   $State: Exp $          *
    *                                                                     *
    * 			Copyright (c) 1986, 1987 Dave Taylor              *
    * 			Copyright (c) 1988, 1989 USENET Community Trust   *
    ***********************************************************************
 

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

/*======================================================================
     folder.c

  Screen to display and manage all the users folders

This puts up a list of all the folders in the users mail directory on
the screen spacing it nicely. The arrow keys move from one to another
and the user can delete the folder or select it to change to or copy a
message to. The dispay lets the user scroll up or down a screen full,
or search for a folder name.
 ====*/


#include "headers.h"

/*
  HEADER LINES is the number of lines in the header/title
  BOTTOM is the number in the menu and such at the bottom.
  These include all blank lines and such. 
 */

#define FOLDER_LIST_HEADER_LINES 2
#define FOLDER_LIST_BOTTOM_LINES 2


/*----------------------------------------------------------------------
    This structure  holds the list of names when it is being displayed
   and operated on. The num_columns, and num_rows are calculated in
   create_folder_display() based on the sizes of the screen and the
   lengths of the file names
  ---*/
struct folder_display
{
    int num_columns;
    int col_width;
    int width;
    int num_rows;
    char **names; 
};

#define name(xx, yy, zz) ((xx)->names[(xx)->num_columns * (yy) + (zz)])


#ifdef ANSI
static char   *add_new_folder(int, char **);
static char   *rename_folder(char **, int, char *, int *);
static         delete_folder(char *, int *);
static void    print_folders(char **,unsigned char *, struct folder_display *);
static void    free_folder_display(struct folder_display *);
static char  **get_file_list(char *);
static char  **add_to_list(char **, char *);
static void    remove_from_list(char **, char *);
static void    free_file_list(char **);
static int     find_folder(char *, int *, int *, struct folder_display *);
static void    redraw_folder_screen();
static int     search_folders(struct folder_display *, int *, int*, int);
static void    display_folder(struct folder_display *, int, int, int, int,
                                                                    int, int);
static unsigned char 
              *get_list_lengths(char **);
static struct  folder_display
              *create_folder_display(char **, int, unsigned char *);

#else  /* ANSI */
static char   *add_new_folder();
static char   *rename_folder();
static         delete_folder();
static void    print_folders();
static void    free_folder_display();
static char  **get_file_list();
static char  **add_to_list();
static void    remove_from_list();
static void    free_file_list();
static int     find_folder();
static void    redraw_folder_screen();
static int     search_folders();
static void    display_folder();
static unsigned char 
              *get_list_lengths();
static struct  folder_display
              *create_folder_display();

#endif  /* ANSI */


static struct key_menu folder_keymenu0 =
  {0,{
     {"?","Help",0},      {"S","Save",0},          {"C","Cancel",0},
     {NULL,NULL,0},       {NULL,NULL,0},           {"L","Print",0},
     {"-","Prev Page",0}, {"SPACE","Next Page",0}, {"A","Add",0},
     {"R","Rename",0},    {"D","Delete",0},        {"W","Where is",0}}};

static struct key_menu folder_keymenu1 =
  {0,{
     {"?","Help",0},      {"O","Open",0},          {"C","Cancel", 0},
     {NULL,NULL,0},       {"G","Go to Fldr",0},    {"L","Print",0},
     {"-","Prev Page",0}, {"SPACE","Next Page",0}, {"A","Add",0},
     {"R","Rename",0},    {"D","Delete",0},        {"W","Where is",0}}};

static struct key_menu folder_keymenu2 =
  {0,{
     {"?","Help",0},      {"O","Open",0},          {"M","Main Menu", 0},
     {NULL,NULL,0},       {"G","Go to Fldr",0},    {"L","Print",0},
     {"-","Prev Page",0}, {"SPACE","Next Page",0}, {"A","Add",0},
     {"R","Rename",0},    {"D","Delete",0},        {"W","Where is",0}}};

static struct key_menu folder_keymenu3 =
  {0,{
     {"?","Help",0},      {"S","Select Fcc",0},    {"C","Cancel", 0},
     {NULL,NULL,0},       {NULL, NULL, 0},         {"L","Print",0},
     {"-","Prev Page",0}, {"SPACE","Next Page",0}, {"A","Add",0},
     {"R","Rename",0},    {"D","Delete",0},        {"W","Where is",0}}};
    


static struct folder_screen_state
{
    char                  **file_list;
    unsigned char          *file_lens;
    struct folder_display  *display_list;
    int                     col, row, top_row, lines;
} fs;



/*----------------------------------------------------------------------
      Front end to folder lister when it's called from the main menu

 Args: pine_state -- The general pine_state data structure

 Result: runs folder_lister

  ----*/
void
folder_screen(pine_state)
    struct pine *pine_state;
{
    dprint(1, (debugfile, "=== folder_screen called ====\n"));
    pine_state->next_screen = main_menu_screen;
    folder_lister(pine_state, FolderMaint, NULL);
    pine_state->prev_screen = folder_screen;
}



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

 ----*/     

folders_for_fcc(return_string)
     char *return_string;
{
    /* Coming back from composer */
    get_windsize(ps_global->ttyo);
    init_signals();
    clear_cursor_pos();
    mark_status_dirty();

    return(folder_lister(ps_global, GetFcc, return_string));
}


    

/*----------------------------------------------------------------------
      Main routine for the list of folders screen, displays and execute cmds.

  Args:  ps            -- The pine_state data structure
         do_what       -- What function we're called as -- select, maint...
         return_string -- Buffer to return selected folder name in


  Result: return 0 for abort, 1 for open folder, and 2 for save folder
          The name selected is copied into the given buffer
  ----*/
int 
folder_lister(ps, do_what, return_string)
     struct pine *ps;
     FolderFun    do_what;
     char        *return_string;
{
    int    old_col, did_delete, n_col, ch, orig_ch, mangled_footer, old_row,
           new_col,mangled_header, rv, quest_line, rc, really_add,
           new_row, cur_is_open;
    char  *new_file, **l, *cur_name, *new_fold, tmp_folder[MAXFOLDER+1];

#ifdef	DOS
/* DOS need local format first!! */
    return(0);
#endif
    dprint(1, (debugfile, "\n\n    ---- FOLDER SCREEN ----\n"));


    quest_line      = -3;
    fs.col	    = 0;
    fs.row	    = 0;
    old_col	    = -1;
    old_row	    = -1;
    fs.lines	    = ps->ttyo->screen_rows - FOLDER_LIST_BOTTOM_LINES - 1
                           - FOLDER_LIST_HEADER_LINES ;
    mangled_footer  = 1;
    mangled_header  = 1;

    ClearScreen();


    switch(do_what) {
       case FolderMaint:
         set_titlebar("FOLDER MAINTENANCE", 1, FolderName, 0, 0, 0);
         break;

       case OpenFolder:
         set_titlebar("OPEN FOLDER", 1, FolderName, 0, 0, 0);
         break;

       case SaveMessage:
         set_titlebar("SAVE MESSAGE", 1, MessageNumber,
                                       ps->current_sorted_msgno, 0, 0);
         break;
    }


    fs.file_list = get_file_list(ps->folders_dir);
    fs.file_lens = get_list_lengths(fs.file_list);
    fs.display_list = create_folder_display(fs.file_list,
                                            ps->ttyo->screen_cols,
                                            fs.file_lens);
    find_folder(ps_global->cur_folder, &fs.row, &fs.col, fs.display_list);

    fs.top_row = fs.lines >= 0 ? (fs.row/fs.lines) * fs.lines : 0;
    fs.row    -= fs.top_row;

    MoveCursor(2,0);
    CleartoEOS();

    ps_global->redrawer = redraw_folder_screen;


    for(ch = 'x' /* For display_message first time through */;;) {

        /*------------ New mail check ----------*/
        if(ps_global->ttyo->screen_rows > 1 &&
           new_mail(NULL, 0, ch==NO_OP_IDLE ?0 :ch==NO_OP_COMMAND ?1 :2) >=0 ){
            if(ps_global->new_current_sorted_msgno > 0) {
                ps_global->current_sorted_msgno =
                  ps_global->new_current_sorted_msgno;
                ps_global->new_current_sorted_msgno = -1L;
            }
            mangled_header = 1;
        }
        if(streams_died())
          ps_global->mangled_header = 1;

        
        /*----------  screen painting -------------*/
	if(mangled_header && ps_global->ttyo->screen_rows > 0) {
            switch(do_what) {
              case FolderMaint:
	        set_titlebar("FOLDER MAINTENANCE", 1, FolderName, 0, 0, 0);
                break;

              case OpenFolder:
	        set_titlebar("OPEN FOLDER", 1, FolderName, 0, 0, 0);
                break;

              case SaveMessage:
	        set_titlebar("SAVE MESSAGE", 1, MessageNumber,
                                           ps->current_sorted_msgno, 0, 0);
                break;

              case GetFcc:
                set_titlebar("SELECT FCC", 1, FolderName, 0, 0, 0);
                break;
            }
	    mangled_header = 0;
	}
        if(fs.lines >= 0 && (fs.col != old_col || fs.row != old_row))
	  display_folder(fs.display_list,fs.top_row,fs.col, fs.row, old_col,
                         old_row, fs.lines);
	if(mangled_footer && ps_global->ttyo->screen_rows > 3) {
            format_keymenu(do_what == SaveMessage ? &folder_keymenu0 :
                    do_what == OpenFolder ? &folder_keymenu1:
                    do_what == GetFcc ? &folder_keymenu3: &folder_keymenu2,
                      ps_global->ttyo->screen_cols);
            output_keymenu(do_what == SaveMessage ? &folder_keymenu0 :
                    do_what == OpenFolder ? &folder_keymenu1:
                    do_what == GetFcc ? &folder_keymenu3: &folder_keymenu2,
                           -2, 0);
	    mangled_footer = 0;
	}
	old_col = fs.col;
        old_row = fs.row;
	n_col = -1;

        cur_name = name(fs.display_list, fs.top_row + fs.row, fs.col);


        /*------- display any status messages -----*/
	display_message(ch);


        /*----- Read and validate the next command ------*/
	MoveCursor(min(0, ps_global->ttyo->screen_rows - 3), 0);

	ch = read_command();
        orig_ch = ch;
        
	if(ch < 'z' && isupper(ch))
	  ch = tolower(ch);
	ch = validatekeys(ch);

        dprint(5, (debugfile, "folder command: %c (%d)\n",ch,ch));

        /*----------- Execute command --------------*/
	switch(ch) {

            /*---------------------- Key left --------------*/
	  case ctrl('B'):
	  case KEY_LEFT:
      move_left:
            new_col = fs.col - 1;
            while(1) {
	        for( ; new_col > 0; new_col--) {
		    if(name(fs.display_list,fs.top_row+fs.row, new_col)!= NULL)
		      break;
	        }
	        if(new_col >= 0 &&
                   name(fs.display_list, fs.top_row+fs.row, new_col) != NULL){
                    fs.col = new_col;
                    break;
                }
                if(fs.top_row == 0 && fs.row == 0) {
                    /*--- top of whole list --*/
                    putchar('\007');
                    break;
                }
                if(fs.row == 0) {
                    /*------ Back up a whole page ----*/
                    fs.top_row -= fs.lines;
                    old_col = -1;
                    old_row = -1;
                    fs.row = fs.lines -1;
                } else {
                    /*---- back up one row ----*/
                    fs.row--;
                }
                new_col = fs.display_list->num_columns - 1;
            }
	    break;


            /*--------------------- Key right -------------------*/
          case ctrl('F'): 
          case KEY_RIGHT:
          case '\t': 
	    for(new_col = fs.col + 1; new_col < fs.display_list->num_columns;
                                                                   new_col++) {
		if(name(fs.display_list, fs.top_row + fs.row, new_col) != NULL)
		  break;
	    }
	    if(new_col >= fs.display_list->num_columns ||
                   name(fs.display_list, fs.top_row + fs.row, new_col)== NULL){
		n_col = 0;
	        goto next_row;
            } else {
		fs.col = new_col;
	    }
	    break;


            /*--------------- Key up ---------------------*/
	  case ctrl('P'):
          case KEY_UP:
	    if(fs.top_row == 0 && fs.row == 0) {
		putchar('\007');
		break;
	    }
	    if(fs.row == 0) {
		fs.top_row -= fs.lines;
		old_col  = -1;
		old_row  = -1;
		fs.row  = fs.lines - 1;
		/*---- previous page ----*/
	    } else {
		fs.row--;
		if(name(fs.display_list, fs.top_row + fs.row, fs.col) == NULL){
		    goto move_left;
		}
	    }
	    break;


            /*----------------- Key Down --------------------*/
	  case ctrl('N'):
          case KEY_DOWN:
       next_row:  
	    if(fs.top_row + fs.row + 1 >= fs.display_list->num_rows) {
		putchar('\007');
		break;
	    }

	    if(n_col != -1)
	      fs.col = n_col;

	    if(fs.row + 1 >= fs.lines) {
		/*---- next page ---- */
		fs.top_row += fs.lines;
		old_row  = -1;
		old_col  = -1;
		fs.row  = 0;
		mangled_footer++;
	    } else {
		fs.row++;
		if(name(fs.display_list, fs.top_row + fs.row, fs.col) == NULL)
		  /* No entry below us, so move left till we find one */
		  goto move_left;
	    }
	    break;


            /*--------------Scroll Up ----------------------*/
          case PF7: 
          case ctrl('Y'): 
	  case '-':
	    if(fs.top_row != 0) {
		fs.top_row -= fs.lines;
		old_col = -1;
		old_row = -1;
		fs.row = 0;
	    } else {
		putchar('\007');
	    }
            break;


            /*---------- Scroll screenful ------------*/
	  case PF8:
	  case ' ': 
          case ctrl('V'): 
	  case '+':
	    if(fs.top_row + fs.lines < fs.display_list->num_rows){
		fs.top_row += fs.lines;
		old_col = -1;
		old_row = -1;
		fs.row = 0;
	    } else {
		putchar('\007');
	    }
	    break;



            /*------------------ Help ----------------------*/
	  case PF1:
	  case '?':
	  case ctrl('G'):
            ps_global->next_screen = SCREEN_FUN_NULL;
            ps_global->redrawer = (void (*)())NULL;
            switch(do_what) {
              case FolderMaint:
                helper(h_folder_maint, "HELP FOR FOLDER MAINTENANCE", 0);
                break;
              case OpenFolder:
                helper(h_folder_open, "HELP FOR OPENING FOLDERS", 0);
                break;
              case SaveMessage:
                helper(h_folder_save,"HELP FOR SAVING MESSAGES TO FOLDERS", 0);
                break;
              case GetFcc:
                helper(h_folder_fcc, "HELP FOR SELECTING THE FCC", 1);
                break;
            }
            if(ps_global->next_screen != SCREEN_FUN_NULL) {
                /* So "m" to go back to main menu works */
	        free_folder_display(fs.display_list);
                free_file_list(fs.file_list);
                fs_give((void **)&fs.file_lens);
	        return(0);
            }
            ps_global->redrawer = redraw_folder_screen;
	    mangled_header++;
	    mangled_footer++;
	    old_row = -1;
	    break;


            /*---------- Save message/Open Folder ----------*/
          case ctrl('M') :
          case ctrl('J') :
          case PF2:
          case 's':
	  case 'o':
	    if((ch == 's' && (do_what==OpenFolder || do_what==FolderMaint)) ||
               (ch == 'o' && (do_what == SaveMessage || do_what == GetFcc))){
		putchar('\007');
	    } else if(do_what == OpenFolder || do_what == FolderMaint) {
                /*--- Open folder ---*/
                strcpy(tmp_folder, ps_global->cur_folder);
                rv = do_broach_folder(cur_name);
                if(rv == 1) {
                    strcpy(ps_global->last_folder, tmp_folder);
                    ps_global->redrawer = (void(*)())NULL;
                    ps_global->next_screen = mail_index_screen;
                    free_folder_display(fs.display_list);
                    free_file_list(fs.file_list);
                    fs_give((void **)&fs.file_lens);
                    return(1); 
                }
                /* Open Failed. Message will be issued by do_broach_folder. */
                /* Stay here in folder lister and let the user try again. */
                break;
            } else {
                /*-- save message --- */
	        if(cur_name == NULL){
		    rv = 0;
	        } else {
		    strcpy(return_string, cur_name);
		    rv = 1;
                    dprint(5, (debugfile, "returing \"%s\"\n", return_string));
	        }
                ps_global->redrawer = (void (*)())NULL;
	        free_folder_display(fs.display_list);
                free_file_list(fs.file_list);
                fs_give((void **)&fs.file_lens);
	        return(rv);
            }
	    break;


            /*--------- QUIT -----------*/
	  case PF3:
	  case 'm':
	  case 'c':
            if(ch != PF3 && ((do_what == FolderMaint && ch != 'm') ||
                             (do_what != FolderMaint && ch != 'c'))){
                putchar('\007');
            } else {
                ps_global->redrawer = (void (*)())NULL;
   	        free_folder_display(fs.display_list);
                free_file_list(fs.file_list);
                fs_give((void **)&fs.file_lens);
	        return(0);
            }
            break;
	    
            /*----------------- Add a new folder name -----------*/
	  case PF9:
	  case 'a':
            /*--------------- Rename folder ----------------*/
	  case PF10:
	  case 'r':
            cur_is_open = !strcmp(cur_name, ps_global->cur_folder);
            if(ch == 'a' || ch == PF9)
              new_file = add_new_folder(quest_line, fs.file_list);
            else
              new_file = rename_folder(fs.file_list, quest_line, cur_name, &really_add);
            if(new_file != NULL) {
                
                if(ch == 'a' || ch == PF9 || really_add)
                  fs.file_list = add_to_list(fs.file_list, new_file);
                if((ch == 'r' || ch == PF10) && cur_is_open)
                  mangled_header = 1;
                free_folder_display(fs.display_list);
                fs.display_list = create_folder_display(fs.file_list,
                                            ps->ttyo->screen_cols, fs.file_lens);
                for(l = fs.display_list->names;
                                  *l == NULL || strcmp(*l, new_file); l++);
                new_row = (l -fs.display_list->names) / fs.display_list->num_columns;
                fs.col = (l -fs.display_list->names) % fs.display_list->num_columns;
                if(new_row >= fs.top_row + fs.lines || new_row < fs.top_row) {
                    fs.top_row = (new_row/fs.lines) * fs.lines;
                }
                fs.row = new_row - fs.top_row ;
                old_col = -1;
                old_row = -1;
                fs_give((void **)&new_file);
            }
            mangled_footer++;
            break;
		     

            /*-------------- Delete --------------------*/
          case PF11:
	  case 'd':
            did_delete = delete_folder(cur_name, &mangled_header);
            mangled_footer++;
            if(did_delete){
                /* remove from file list */
                remove_from_list(fs.file_list,cur_name);
                free_folder_display(fs.display_list);
                fs.display_list = create_folder_display(fs.file_list,
                                             ps->ttyo->screen_cols, fs.file_lens);
                old_row = -1; /* force redraw */
                goto move_left;
                   /* there's always an entry at row 0 and col 0 so 
                      something will be found. Also list is never empty */
            }
	    break;


             /*------------- Print list of folders ---------*/
          case PF6:
	  case 'l':
            print_folders(fs.file_list, fs.file_lens, fs.display_list);
	    break;


            /*---------- Look (Search) ----------*/
	  case PF12:
	  case 'w':
            new_col = fs.col; new_row = fs.row + fs.top_row;
            mangled_footer++;
            rc = search_folders(fs.display_list, &new_row, &new_col, quest_line);
            if(rc == -2) {
                q_status_message(0, 0,2,"\007Folder name search aborted");
                break;
            }
            if(rc == 1) {
                if(new_row < fs.top_row + fs.row ||
                   new_row == fs.top_row + fs.row && new_col < fs.col)
                  q_status_message(0, 0,2, "Search wrapped to beginning");
                if(new_row > fs.top_row + fs.lines || new_row < fs.top_row) {
                    /*---- not on screen --*/
                    fs.top_row = (new_row/fs.lines) * fs.lines;
                    old_col = -1;
                    old_row = -1;
                } 
                fs.row = new_row - fs.top_row;
                fs.col = new_col;
            }else {
	        q_status_message(0, 0,2, "\007Word not found");
            }
	    break;


            /*---------- Go to Folder, ----------*/
          case PF5:
          case 'g':
            new_fold = broach_folder(-3, 0);
            mangled_footer = 1;;
            if(new_fold != NULL){
                if(do_broach_folder(new_fold) > 0) {
                    ps_global->redrawer = (void (*)())NULL;
                    ps_global->next_screen = mail_index_screen;
                    free_folder_display(fs.display_list);
                    free_file_list(fs.file_list);
                    fs_give((void **)&fs.file_lens);
                    return(1); 
                }
            }
            break;


            /*----------no op to check for new mail -------*/
          case NO_OP_IDLE:
	  case NO_OP_COMMAND :
	    break;


            /*----------- Suspend  -- ^Z ---------------*/
          case ctrl('Z'):
            if(!have_job_control())
              goto bleep;
            if(!ps->can_suspend) {
                q_status_message(1, 1,3,
                                 "\007Pine suspension not enabled - see help text");
                break;
            } else {
                do_suspend(ps);
            }
            /* Fall through to redraw-*/


            /*------------ Redraw command -------------*/
          case KEY_RESIZE:
          case ctrl('L'):
            ClearScreen();
            mangled_footer++;
            mangled_header++;
            redraw_folder_screen();
	    break;


            /*--------------- Invalid Command --------------*/
	  default: 
          bleep:
              q_status_message1(0, 0,2, "\007Unknown Command: \"%s\"",
                                (void *)pretty_command(orig_ch));
	       break;
	} /* End of switch */
    }
}



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

  ----*/
static void
redraw_folder_screen()
{
    char folder[MAXFOLDER+1];

#ifdef	DOS
    return;
#endif

    strncpy(folder,name(fs.display_list, fs.top_row + fs.row, fs.col),
            MAXFOLDER);
    folder[MAXFOLDER] = '\0';
    free_folder_display(fs.display_list);
    fs.display_list = create_folder_display(fs.file_list,
                                         ps_global->ttyo->screen_cols,
                                         fs.file_lens);
    find_folder(folder, &fs.row, &fs.col,  fs.display_list);

    fs.lines = ps_global->ttyo->screen_rows - FOLDER_LIST_BOTTOM_LINES - 1 -
                                             FOLDER_LIST_HEADER_LINES;
    if(fs.lines <= 0)
      return;

    fs.top_row = (fs.row/fs.lines) * fs.lines;
    fs.row -= fs.top_row;
    display_folder(fs.display_list, fs.top_row, fs.col, fs.row, -1, -1,
                   fs.lines); 
}
                


/*----------------------------------------------------------------------
      Paint the lines of the folders directory on the screen

   Args: fl      -- The folder display structure
         top_row -- Line number in display structure of top of current screen
         cur_col -- The current column number on the screen
         cur_row -- Line number from first line of folders displayed
         old_col -- Old or previous column number
         old_row -- Old or previous line number
         lines   -- The number of folder lines to display on the screen
  
 Result: the lines are painted or repainted on the screen

    Paint folder list, or part of it.

Called to either paint repaint the whole list, or just move the
cursor. If old_row is -1 then we are repainting. In this case have
to watch out for names that are blank as the data sometimes has blanks in
it. Go through the data row by row, column by column. Paint the item that's 
currently "it" in reverse.

When the changing the current one, just repaint the old one normal and the
new one reverse.

Different pages are handled by different parameters being passed as
the top of the page (top_row).
  ----*/
static void
display_folder(fl, top_row, cur_col, cur_row, old_col, old_row, lines)
     struct folder_display *fl;
     int top_row, cur_col, cur_row, old_row, old_col, lines;
{
    register int j_col, i_scr, i_ent;
    int          col_width;

    if(lines <= 0)
      return;

    col_width = fl->col_width;

    if(old_row == -1) {
	/*------------ repaint entire screen _-----------*/
	for(i_ent = top_row, i_scr = 0; i_scr < lines; i_scr++, i_ent++){
            MoveCursor(FOLDER_LIST_HEADER_LINES + i_scr, 0);
            CleartoEOLN();
            if(i_ent < fl->num_rows)
              for(j_col = 0; j_col < fl->num_columns; j_col++) {
                  if(i_scr == cur_row && j_col == cur_col)
                    StartInverse();
                  if(name(fl, i_ent, j_col) != NULL)
                    PutLine0(FOLDER_LIST_HEADER_LINES + i_scr,
                             j_col * col_width,name(fl, i_ent, j_col));
                  if(i_scr == cur_row && j_col == cur_col)
                    EndInverse();
                }
	}
    } else {
	/*------- Changing which is the current ------*/
	if(name(fl, top_row + old_row, old_col) != NULL)
	  PutLine0(FOLDER_LIST_HEADER_LINES + old_row, old_col * col_width,
		                       name(fl, top_row + old_row, old_col));

	if(name(fl, top_row + cur_row, cur_col) != NULL) {
            StartInverse();
	    PutLine0(FOLDER_LIST_HEADER_LINES + cur_row, cur_col * col_width,
		                       name(fl, top_row + cur_row, cur_col));
	    EndInverse();
	}
    }
}



/*----------------------------------------------------------------------
      Create a new folder

   Args: quest_line  -- Screen line to prompt on
         folder_list -- The current list of folders

 Result: returns the name of the folder created
  ----*/

static char *
add_new_folder(quest_line, folder_list)
     int quest_line;
     char **folder_list;
{
    register char **l;
    char            new_folder[MAXFOLDER+1],prompt[100],folder_path[MAXPATH+1];
    char          **help;
    int             rc;

    dprint(4, (debugfile, "\n - add_new_folder - \n"));

    new_folder[0] = '\0';
    help = NULL;
    sprintf(prompt, "Name of folder to add : ");
    while(1){
        rc = optionally_enter(new_folder, quest_line, 0, MAXFOLDER, 1,
                               0, prompt, NULL, help, 0);

        if(rc == 0 && *new_folder) {
            for(l = folder_list; *l != NULL; l++) {
                if(!strucmp(*l, new_folder))
                  break;
            }
            if(*l) {
                q_status_message1(1, 1,3, "Folder \"%s\" already exits",
                                                           pretty_fn(*l));
                display_message(NO_OP_COMMAND);
                sleep(1);
                continue;
            }
        }

        if(rc == 3) {
            help = help == NULL ? h_oe_foldadd : (char **)NULL;
            continue;
        }

        if(rc != 4) /* redraw */
          break; /* no redraw */
    }
    if(rc ==1 || new_folder[0] == '\0') {
        q_status_message(0, 0,2, "\007Addition of new folder aborted");
        return(NULL);
    }

    for(l = folder_list; *l != NULL; l++)
      if(strucmp(*l, new_folder) == 0)
        break;
    if(*l != NULL) {
        q_status_message1(1, 1, 3, "\007Folder \"%s\" already exists",
                          pretty_fn(new_folder));
        return(NULL);
    }

    build_path(folder_path, ps_global->folders_dir, new_folder);
    if(creat(folder_path, 0600) < 0) {
        q_status_message2(1, 2, 4, "\007Error creating folder \"%s\": %s",
                          pretty_fn(new_folder), error_description(errno));
        dprint(1, (debugfile, "Error creating \"%s\": %s\n",
                   folder_path, error_description(errno)));
        return(NULL);
    }

    q_status_message1(0, 1,3, "Folder \"%s\" created", pretty_fn(new_folder));

    return(cpystr(new_folder));
}



/*----------------------------------------------------------------------
      Rename folder
  
   Args: list       -- The list of current folders
         q_line     -- Screen line to prompt on
         folder     -- Name of folder to rename
         really_add -- Pointer to flag to set if our rename really turns
                       out to be an addition because we renamed one of the
                       standard folders like sent-mail.

 Result: returns the new name of the folder, or NULL if nothing happened. The
         folder name returned is malloced and should be freed by the caller.

 When either the sent-mail or saved-message folders are renamed, immediately 
create a new one in their place so they always exist. The main loop above also
detects this and makes the rename look like an add of the sent-mail or
saved-messages folder. (This behavior may not be optimal, but it keeps things
consistent.
  ----*/
static char *
rename_folder(list, q_line, folder, really_add)
     char **list;
     int    q_line, *really_add;
     char  *folder;
{
    register char **l;
    char            new_foldername[MAXFOLDER+1], orig_path[MAXPATH+1],
                                   new_path[MAXPATH+1];
    char          **help, *prompt;
    int             rc, ren_cur;

    dprint(4, (debugfile, "\n - rename folder -\n"));

    if(strcmp(folder, "inbox") == 0) {
        q_status_message(1, 2,4,
"\007Changing the name of \"inbox\" is not allowed -- it's a special folder.");
        return(NULL);
    }

    ren_cur = strcmp(folder, ps_global->cur_folder) == 0;

    *really_add = 0;

    prompt = "Change name of folder: ";
    help   = (char **)NULL;
    strcpy(new_foldername, folder);
    while(1) {
        rc = optionally_enter(new_foldername, q_line, 0, MAXFOLDER, 1, 0,
                              prompt, NULL, help, 0);
        if(rc == 3) {
            help = help == NULL ? h_oe_foldrename : (char **)NULL;
            continue;
        }

        if(rc == 0 && *new_foldername) {
            for(l = list; *l != NULL; l++) {
                if(!strucmp(*l, new_foldername))
                  break;
            }
            if(*l) {
                q_status_message1(1, 1,3, "Folder \"%s\" already exits",
                                  pretty_fn(*l));
                display_message(NO_OP_COMMAND);
                sleep(1);
                continue;
            }
        }

        if(rc != 4) /* redraw */
          break;  /* no redraw */

    }
    if(rc==1 || new_foldername[0]=='\0' || strcmp(new_foldername, folder)==0){
        q_status_message(0, 0,2, "\007Folder rename aborted");
        return(0);
    }

    if(ren_cur && ps_global->mail_stream != NULL) {
        mail_close(ps_global->mail_stream);
        ps_global->mail_stream = NULL;
    }

    build_path(orig_path, ps_global->folders_dir, folder);
    build_path(new_path, ps_global->folders_dir, new_foldername);
    if(rename_file(orig_path, new_path) < 0) {
        q_status_message2(1, 2, 4, "\007Error renaming folder \"%s\" : %s",
                         pretty_fn(folder), error_description(errno));
        dprint(1, (debugfile, "Error renaming \"%s\" to \"%s\" %s\n",
                   folder, new_foldername, error_description(errno)));
        if(ren_cur) {
            /* Deep yogurt here; rename failed for some reason */
            /* Fake it and reopen inbox */
            do_broach_folder("inbox");
        }
        return(0);
    }

    if(strcmp(ps_global->VAR_DEFAULT_FCC, folder) == 0) {
        /* renaming sent-mail */
        build_path(new_path, ps_global->folders_dir,
                   ps_global->VAR_DEFAULT_FCC);
        q_status_message3(0, 1,3,
                         "Folder \"%s\" renamed to \"%s\". New \"%s\" created",
                          pretty_fn(folder), pretty_fn(new_foldername),
                          pretty_fn(ps_global->VAR_DEFAULT_FCC));
        if(creat(new_path, 0600) < 0){
            q_status_message2(1, 2, 4, "\007Error creating new \"%s\": %s",
                            pretty_fn(ps_global->VAR_DEFAULT_FCC),
                              error_description(errno));
            dprint(2, (debugfile, "Error creating \"%s\": %s\n",
                        new_path, error_description(errno)));
        }
        /* This turns into a fake add and list gets fixed up above */
        *really_add = 1;

    } else if(strcmp(DEFAULT_SAVE, folder) == 0) {
        /* renaming saved-messages */
        build_path(new_path, ps_global->folders_dir,
                pretty_fn(DEFAULT_SAVE));
        q_status_message3(0, 1,3,
                         "Folder \"%s\" renamed to \"%s\". New \"%s\" created",
                          pretty_fn(folder), pretty_fn(new_foldername),
                          pretty_fn(DEFAULT_SAVE));
        if(creat(new_path, 0600) < 0){
            q_status_message2(1, 2, 4,"\007Error creating new \"%s\": %s",
                            pretty_fn(DEFAULT_SAVE), error_description(errno));
            dprint(2, (debugfile, "Error creating \"%s\": %s\n",
                        new_path, error_description(errno)));
        }
        /* This turns into a fake add and list gets fixed up above */
        *really_add = 1;
    } else {
        q_status_message2(0, 1,3, "Folder \"%s\" renamed to \"%s\"",
                     pretty_fn(folder), pretty_fn(new_foldername));
    
        /*-------- fix up incore list of folders ------*/
        for(l = list; *l != NULL && strcmp(*l, folder); l++);
        fs_give((void **)l);
        *l = cpystr(new_foldername);
    }

    if(ren_cur) {
        /* No reopen the folder we just had open */
        do_broach_folder(new_foldername);
    }
        
    return(cpystr(new_foldername));
}



/*----------------------------------------------------------------------
   Confirm and delete a folder

   Args: f_name         -- Name of folder to delete
         mangled_header -- Pointer to flag to set if the the anchor line
                           needs updating (deleted the open folder)

 Result: return 0 if not delete, 1 if deleted.
  ----*/
static
delete_folder(f_name, mangled_header)
     char *f_name;
     int  *mangled_header;
{
     char         namebuf[MAXPATH+1], ques_buf[100];
     int          zap_cur, rv;
     long         count;
     MAILSTREAM  *ms;

     dprint(4, (debugfile, "===== delete_folder(%s) ====\n", f_name));

     zap_cur = !strcmp(f_name, ps_global->cur_folder);

     if(strcmp(f_name, "inbox") == 0)  {
         q_status_message(1, 2,4, "\007 Can't delete your \"inbox\".");
         return(0);
     }

     build_path(namebuf, ps_global->folders_dir, f_name);

     ms  = zap_cur ? ps_global->mail_stream : mail_open(NULL, namebuf,
#ifdef DEBUG                                                        
                                                       (long) debug);
#else
       0L);
#endif
     
     count = ms != NULL ? ms->nmsgs : -1;
     if(!zap_cur && ms != NULL)
         mail_close(ms);
         
     if(count < 0) 
       sprintf(ques_buf, "Really delete%s \"%s\"", zap_cur ?
               " the currently open folder" : "",pretty_fn(f_name));
     else
       sprintf(ques_buf,"Really delete%s \"%s\" with %ld message%s",
               zap_cur ? " current folder" : "" , pretty_fn(f_name),
               count, count == 1 ? "" : "s");
     if(want_to(ques_buf, 'n', (char **)NULL, 0) == 'n'){
	 return(0);
     }

     dprint(2, (debugfile, "Unlinking folder \"%s\"\n", namebuf));

     if(zap_cur && ps_global->mail_stream != NULL) {
         dprint(2, (debugfile, "Closed mail stream \"%s\"\n",
                        ps_global->mail_stream->mailbox));
         mail_close(ps_global->mail_stream);
         /* Now we're somewhat commited and in a jam if we fail */
     }

     rv = unlink(namebuf);

     if(zap_cur) {
         /* This code same as in do_broach_folder */
         /* BUG, bad news if inbox isn't open */
         ps_global->mail_stream          = ps_global->inbox_stream;
         ps_global->current_sorted_msgno = ps_global->inbox_sorted_msgno;
         ps_global->max_msgno            = ps_global->inbox_max_msgno;
         dprint(7, (debugfile, "%ld %ld %x\n", ps_global->current_sorted_msgno,
                    ps_global->max_msgno, ps_global->mail_stream));
         strcpy(ps_global->cur_folder, ps_global->inbox_name);
         clear_index_cache();
         *mangled_header = 1; /* repaint header line */

         if(rv) {
             /* delete of open folder failed. this is messy. just reopen
                inbox and fake it */
             q_status_message2(1, 2,4,
                            "\007Delete of \"%s\" failed: %s. Inbox reopend",
                               pretty_fn(f_name),error_description(errno));
             dprint(1, (debugfile, "Error deleting %s: %s\n",
                    namebuf, error_description(errno)));
             return(0);
         } else {
           q_status_message1(1, 1,3,
                    "Folder \"%s\" closed and deleted! Reopened inbox.",
                               pretty_fn(f_name));
           return(1);
         }
     } else {
         if(rv) {
             q_status_message2(1, 2, 4, "\007Delete of \"%s\" failed: %s",
                               pretty_fn(f_name),
                           error_description(errno));
             dprint(1, (debugfile, "Error deleting %s: %s\n",
                    namebuf, error_description(errno)));
             return(0);
         } else {
             q_status_message1(0, 1,3,"Folder \"%s\" deleted!",pretty_fn(f_name));
             return(1);
         }
     }
}         



/*----------------------------------------------------------------------
      Print the list of folders on paper

   Args: list    --  The current list of folders
         lens    --  The list of lengths of the current folders
         display --  The current folder display structure

 Result: list printed on paper

If the display list was created for 80 columns it is used, otherwise
a new list is created for 80 columns
  ----*/

static void
print_folders(list, lens, display)
     char                 **list;
     unsigned char         *lens;
     struct folder_display *display;
{
    register int i, j, col_width, used_screen_display;

    used_screen_display = 0;
    if(display->width != 80) 
        display = create_folder_display(list, 80, lens);
    else
        used_screen_display = 1;

    if(open_printer("folder list ") != 0)
      return;

    col_width = display->col_width;
    for(i = 0; i < display->num_rows; i++){
        for(j = 0 ; j < display->num_columns; j++){
            char *n = name(display, i, j);
            if(n != NULL) {
                print_text(n);
                print_text(repeat_char(col_width - (strlen(n)%col_width),' '));
            }
        }
        print_text("\n");
    }
    close_printer();

    if(!used_screen_display)
      free_folder_display(display);
}

                     

/*----------------------------------------------------------------------
  Search folder list

   Args: display  -- The folder display structure
         row      -- pointer to current row, and to row entry was found on
         col      -- pointer to current col, and to col entry was found on
         ask_line -- Screen line to prompt on

 Result: returns 1 if found, 0 if not
         returns the row and column for entry found
  ----------------------------------------------------------------------*/
static int
search_folders(display, row, col, ask_line)
     struct folder_display *display;
     int                    *row, *col, ask_line;
{
    register char **i, **i_end, **i_next_end;
    char            prompt[MAX_SEARCH+50], nsearch_string[MAX_SEARCH+1];
    static char     search_string[MAX_SEARCH+1];
    char          **help;
    int             rc, cur_row, cur_col;

    sprintf(prompt, "Word to search folder names for [%s] : ", search_string);
    help = NULL;
    nsearch_string[0] = '\0';
    while(1) {
        rc = optionally_enter(nsearch_string, ask_line, 0, MAX_SEARCH, 1,
                               0, prompt, NULL, help,0);
        if(rc == 3) {
            help = help == NULL ? h_oe_foldsearch : (char **)NULL;
            continue;
        }
        if(rc != 4)
          break;
    }
    if(rc == 1 || (search_string[0] == '\0' && nsearch_string[0] == '\0'))
      return(-2); /* abort */

    if(nsearch_string[0] != '\0')
      strcpy(search_string, nsearch_string);

    /*----- Search the bottom half of list ------*/
    cur_row = *row;
    cur_col = *col;
    i     = &(display->names[cur_row * display->num_columns + cur_col]);
    i_end = &(display->names[display->num_rows * display->num_columns]);
    i_next_end = i;
    i++; /* start one past where we are now */
    while(i < i_end) {
	if(*i != NULL && srchstr(*i, search_string)){
	    int tmp  =  i - display->names;
            *col = tmp % display->num_columns;
	    *row = tmp / display->num_columns;
	    return(1);
	}
	i++;
    }

    /*------- Search the top half of list (wrap seach) -------*/
    i = display->names;
    i_end = i_next_end;
    while(i < i_end) {
	if(*i != NULL && srchstr(*i, search_string)){
	    int tmp  =  i - display->names;
            *col = tmp % display->num_columns;
	    *row = tmp / display->num_columns;
	    return(1);
	}
	i++;
    }
    return(0);
}

    


/*----------------------------------------------------------------------
      compare two names for qsort, case independent

   Args: pointers to strings to compare

 Result: integer result of strcmp of the names
  ----------------------------------------------------------------------*/
static
compare_names(a, b)
     void **a, **b;
{
    return(strucmp((char *)*a, (char *)*b));
}


/*----------------------------------------------------------------------
      Create the folder display function by reading the directory and such

     Args: list  -- The current list of folders
           width -- The width of the screen for which to create display struct
           lens  -- List of the lengths of the folder names

   Result: Produces a structure to represent the display
     
   This is where the display is formatted
The resulting structure is an array of strings, with some NULL, 
that is really a 2d array with dimensions defined in the same structure.
  ----*/

static struct folder_display *
create_folder_display(list, width, lens)
     char         **list;
     int            width;
     unsigned char *lens;
{
    int                    col_width, cols_taken, formatted_list_size, i;
    register int           current_col, num_cols, current_row, c;
    struct folder_display *fold_disp;
    int                    list_size, len_list_size, num_at_top;
    register char         **l;

    /*----- How many files are we dealing with? ----*/
    for(l = list; *l != NULL; l++);
    list_size = l - list;
    col_width = 0; /* Avoid ref before set messages */

    /*-------
        Calculate the column width, by making it as long as 95% of
        all folder names. Bump it up if it's less than 8
     ------*/
    for(len_list_size = 0; lens[len_list_size] != 0; len_list_size++);
    len_list_size--;
    for(i = (len_list_size * 19)/20; i >= 0 ; i--){
        /*-- Find a width greater than 0. There will be one */
        if((col_width = lens[i]) > 0)
          break;
    }
    num_cols  = width/ col_width;
    num_cols = num_cols > 1 ? num_cols : 1;
    col_width = width/ num_cols;
    if(col_width <= 8 ) {
	col_width = 13;
	num_cols = width/col_width;
        if(num_cols == 0) {
            num_cols = 1;
            col_width = width;
        }
    }

    /* don't sort the first three, "inbox" and "sent-mail", and "saved-.." */
    num_at_top = 3;
    if(strlen(ps_global->VAR_DEFAULT_FCC) == 0)
      num_at_top--;
      
    qsort(list + num_at_top, list_size - num_at_top, sizeof(char *),
          compare_names);

    /*---
       Now copy the previously created list into the acutal list used
       for display.
       Allocate the list to twice as big as the previous one, or at least 100.
      --*/
    fold_disp =(struct folder_display *)fs_get(sizeof(struct folder_display));
    formatted_list_size  = max(list_size * 2, 100);
    fold_disp->names     = (char **)fs_get(formatted_list_size*sizeof(char *));
    fold_disp->col_width = col_width;
    fold_disp->width     = width; 

    /*--
      Copy the names from original array to formatted structure.
      Make sure name fits in at the current column..
      --*/
    current_col  = 0;
    current_row  = 0;

    for(l = list ;*l != NULL; l++) {
        cols_taken = 1 + ((strlen(*l) + 1)/col_width);
        if(cols_taken > num_cols - current_col) {
	    for(c = current_col; c < num_cols; c++)
		(fold_disp->names)[current_row * num_cols + c] = NULL;
	    current_row++;
	    current_col = 0;
        }
        (fold_disp->names)[current_row* num_cols+ current_col] = *l;
        c  = current_col + cols_taken;
	while(++current_col < c)
	  (fold_disp->names)[current_row * num_cols + current_col] = NULL;
    }

    /*--- Fill in last line with NULLS ---*/
    while( current_col < num_cols ) {
        (fold_disp->names)[current_row * num_cols+ current_col] = NULL;
	current_col++;
    }

    if(current_row * num_cols + current_col > formatted_list_size)
      panic("Confusion formatting folders list for display");

    fold_disp->num_columns = num_cols;
    fold_disp->num_rows    = current_row + 1;

    return(fold_disp);
}



#ifdef DEBUG
/*----------------------------------------------------------------------
           Splat out the file display structure to a stream for debuggin

 Input:  folder display structure
         stream to write on

 Result: structure written to stream
  ----------------------------------------------------------------------*/
splat_fd(fd, stream)
    struct folder_display *fd;
    FILE *stream;
{
    int i, j;
    
    fprintf(stream, "%d rows, %d columns\n", fd->num_rows, fd->num_columns);

    for(i = 0; i < fd->num_rows; i++) {
	fprintf(stream, "\n---- row %d ----\n", i);
	for(j = 0; j < fd->num_columns; j++) 
	    fprintf(stream, "%-10.10s ", fd->names[i * fd->num_columns + j]);
    }
    fprintf(stream, "\n");
}

#endif



/*----------------------------------------------------------------------
       free all the data structures associated with the folder display

   Args: fd -- The folder display structure to free

 Result: memory is freed. Does not free the actual strings which are
         part of the folders list structure.
  ----------------------------------------------------------------------*/
static void
free_folder_display(fd)
     struct folder_display *fd;
{
    fs_give((void **)&(fd->names));
    fs_give((void **)&fd);
}




/*----------------------------------------------------------------------
    Get a list of files in folders directory

   Args: dir -- The directory containg the files to list

 Result: Returns list of file names in no particular order

The three standard folders are always inserted into the top of the list,
presuming they always exist.
  ----*/
static char **
get_file_list(dir)
     char *dir;
{
    char **list, *file_name;
    int    list_allocated, list_used;

    list_used = 0;
    list_allocated = 100; /* 100, is a nice starting place */
    list = (char **)fs_get(sizeof(char *) * list_allocated);

    list[list_used++] = cpystr(INBOX_NAME);
    if(strlen(ps_global->VAR_DEFAULT_FCC) > 0)
      list[list_used++] = cpystr(ps_global->VAR_DEFAULT_FCC);
    list[list_used++] = cpystr(DEFAULT_SAVE);
    
    if(simple_opendir(dir) < 0) {
        q_status_message1(1, 4, 6, "\007Error getting list of mail folders: %s",
                          error_description(errno));
    } else {
        for(file_name = simple_readdir(); file_name != NULL;
                                              file_name = simple_readdir()) {
            if(file_name[0] == '.')
              continue;
            if(strcmp(file_name, DEFAULT_SAVE) == 0)
              continue;
            if(strcmp(file_name, ps_global->VAR_DEFAULT_FCC) == 0)
              continue;
            if(strcmp(file_name, "inbox") == 0)
              continue;
            list[list_used] = cpystr(file_name);
            list_used++;
            if(list_used >= list_allocated - 2) {
                list_allocated *= 2;
                fs_resize((void **)&list, list_allocated * sizeof(char *));
            }
        }
        simple_closedir();
    }

    list[list_used] = NULL;
    return(list);
}



/*----------------------------------------------------------------------
    Add a new folder name to the incore list

  Args:  list       -- The current list of folders to be added to
         new_folder -- The name of the new folder to add to list

 Result: Returns a new list with the new folder at the end.
  ----*/

static char **
add_to_list(list, new_folder)
     char **list, *new_folder;
{
    register char **l;
    int             list_size;

    for(l = list; *l != NULL; l++);
    list_size         = l - list;
    fs_resize((void **)&list, (list_size + 2) * sizeof(char *));
    list[list_size++] = cpystr(new_folder);
    list[list_size]   = NULL;
    return(list);
}



/*----------------------------------------------------------------------
      Remove folder name from incore list

  Args:  list   -- The list of folder from which entry is to be removed
         f_name -- The folder name to remove from the list

   If folder isn't in the list nothing happens.
  -----*/

static void
remove_from_list(list, f_name)
     char **list, *f_name;
{
    register char **l, **m;
    
    for(l = list; *l != NULL ; l++)
      if(strcmp(*l, f_name) == 0)
        break;

    if(*l == NULL)
      return;

    fs_give((void **)l);

    /*--- shuffle the entries up --*/
    for(m = l + 1, *l++ = *m++; *l != NULL; *l++ = *m++);
}



/*----------------------------------------------------------------------
      Deallocate the file list

      Args: list -- The file list to be deallocated
  ----*/
static void
free_file_list(list)
     char **list;
{
    register char **l;

    for(l = list; *l != NULL; )
      fs_give((void **)l++);
    fs_give((void **)&list);
}

        

/*----------------------------------------------------------------------
     Little compare of to chars for list lengths below
  ----------------------------------------------------------------------*/
static compare_lengths(a, b)
     unsigned char *a, *b;
{
    return(*a - *b);
}


/*----------------------------------------------------------------------
     List of lengths of folder names

   Args:  list -- List of folders

 Result: array of lengths of file names

     Create a list of the lengths of the folder names. We can do this
indepedent of the order of the original list because all we're
interested in is the distribution of lengths so we can make it look
pretty; the lengths don't have ever to be matched to their folder
names. It's also much more efficient to do this way. Future additions
and deletions of folders don't affect this list, so if the lots of
folders are added or deleted the this list will be off and the
presentation won't be as pretty.
  ----*/

static unsigned char *
get_list_lengths(list)
     char **list;
{
    register char         **l;
    register unsigned char *u, *lengths;
    int                     l_size;

    for(l = list; *l != NULL; l++);
    l_size = l - list;

    lengths = (unsigned char *)fs_get((l_size + 1) * sizeof(unsigned char));

    for(u = lengths, l = list; *l != NULL; l++, u++) 
      *u = strlen(*l);

    *u = 0;

    qsort(lengths, l_size, sizeof(unsigned char), compare_lengths);

    return(lengths);
}


/*----------------------------------------------------------------------
   Find folder name
   Args:  name -- the name of the folder to look for
          row  -- pointer to place to put row of found folder
          col  -- pointer to place to put col of found folder

  If nothing is found row and col are not modified. The name match
must be exact except for case.
 ----*/
static int
find_folder(fname, row, col, display)
     char *fname;
     int *row, *col;
     struct folder_display *display;
{
    register char **i, **i_end;

    i     = display->names;
    i_end = &(display->names[display->num_rows * display->num_columns]);
    while(i < i_end) {
	if(*i != NULL && !strucmp(*i, fname)){
	    int tmp  =  i - display->names;
            *col = tmp % display->num_columns;
	    *row = tmp / display->num_columns;
	    return(1);
	}
	i++;
    }
    return(0);
}
 


/*----------------------------------------------------------------------
       Add up the disk space used under a directory

   Args: path      -- The path to directory to add up
         filecount -- Pointer to int to return number of files in
         bytecount -- Pointer to long to return number of bytes in

 Result: returns number of files and bytes in variables passed to it.
 
This does not recurse through a directory tree. In fact it may get confused
a bit if the things in the directory are other than plain files.
  ----*/
void
add_up_disk_usage(path, filecount, bytecount)
     char *path;
     long *bytecount;
     int  *filecount;
{
    char   filename[MAXPATH+1];
    char **l;
    char **list = get_file_list(path);
    long   size;

    *filecount = 0;
    *bytecount = 0;

    for(l = list; *l != NULL; l++) {
        (*filecount)++;
        build_path(filename, path, *l);
        size = file_size(filename);
        if(size > 0)
          *bytecount += size;
    }
}


/*----------------------------------------------------------------------
      Format the given folder name for display for the user

   Args: folder -- The folder name to fix up

Not sure this always makes it prettier. It could do nice truncation if we
passed in a length. Right now it adds the path name of the mail 
subdirectory if appropriate.
 ----*/
      
char *
pretty_fn(folder)
     char *folder;
{
    static char  pfn[MAXFOLDER * 2 + 1];
    char        *p;

#ifdef	DOS
    return(folder);
#else
    if(!ps_global->show_folders_dir || *folder == '/' || *folder == '~' ||
       *folder == '{' || *folder == '\0' || !strcmp(folder, "inbox"))  {
        if(ps_global->nr_mode){ 
	    if((p = strrindex(folder, '/')) != NULL)
	      return(p+1);
	    else if((p = strindex(folder, '}')) != NULL)
	      return(p+1);
	}

	return(folder);
    } else {
        build_path(pfn, ps_global->VAR_MAIL_DIRECTORY, folder);
        return(pfn);
    }
#endif
}



/*----------------------------------------------------------------------
       Check to see if folder exists in given directory

  Args: dir -- directory to check for folder in
        file -- name of folder to check
 
 Result: returns 0 if the folder exists
                 1 if not
    ----*/
folder_exists(dir, file)
     char *file, *dir;
{
    char  fn[MAXPATH+1];

    if(strlen(dir) + strlen(file) + 1 > MAXPATH)
      return(1);   /* Pathname too long, which means we can't access it! */

    build_path(fn, dir, file);

    return(can_access(fn, 06));
}
