static char rcsid[] = "@(#)$Id: savefolder.c,v 1.11 2006/08/09 16:12:28 hurtta Exp $";

/******************************************************************************
 *  The Elm (ME+) Mail System  -  $Revision: 1.11 $   $State: Exp $
 *
 *  Author: Kari Hurtta <hurtta+elm@posti.FMI.FI> (was hurtta+elm@ozone.FMI.FI)
 *      or  Kari Hurtta <elm@elmme-mailer.org>
 *****************************************************************************
 * Some local mailbox code based on ../src/file.c.
 * That code was following copyright:
 *
 *  The Elm Mail System 
 *
 *			Copyright (c) 1988-1992 USENET Community Trust
 *			Copyright (c) 1986,1987 Dave Taylor
 *
 *
 *****************************************************************************/

#include "def_mbox.h"
#include "s_me.h"

DEBUG_VAR(Debug,__FILE__,"mbox");

#if ANSI_C
#define S_(x) static x;
#else
#define S_(x)
#endif

static unsigned char * s2us P_((char *str));
static unsigned char * s2us(str)
     char *str;
{
    return (unsigned char *)str;
}

static char *us2s P_((unsigned char *str));
static char *us2s(str) 
     unsigned char *str;
{
    return (char *)str;
}


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

static int change_type P_((struct folder_browser *dir,
			   struct browser_type *new_type));

/* Dummy browser */

#include <errno.h>
#ifndef ANSI_C
extern int errno;
#endif

static  void browser_zero_dummy P_((struct folder_browser *dir));
static  void browser_zero_dummy(dir) 
     struct folder_browser *dir;
{
    DPRINT(Debug,11,(&Debug,"browser_zero_local: dir=%p\n", dir));

    dir->a.dummy_browser.remote = NULL;
}

static void browser_free_dummy P_((struct folder_browser *dir));
static void browser_free_dummy(dir)
     struct folder_browser *dir;
{
    DPRINT(Debug,11,(&Debug,"browser_free_local: dir=%p\n", dir));

    if (dir->a.dummy_browser.remote) {
	free(dir->a.dummy_browser.remote);
	dir->a.dummy_browser.remote = NULL;
    }
}

#ifdef REMOTE_MBX
static int dummy_to_imap P_((struct folder_browser *dir));
static int dummy_to_imap(dir)
     struct folder_browser *dir;
{
    int ret = 0;
    
    struct remote_account X;
    struct service_entry *se = NULL;
    char *rest = NULL;
    int code;
    
    zero_remote_account(&X);
    
    DPRINT(Debug,15,(&Debug,
		     "dummy_to_imap: Trying %s as remote address...\n",
		     dir->a.dummy_browser.remote));
    
    /* -1 == name not found or bad syntax
       0 == not a remote address
       1 == name found 
    */
    if (0 <= (code = split_remote_name(dir->a.dummy_browser.remote,
				       &X,&se,&rest,
				       STFLAG_is_imap /* Need IMAP */
				       ))) {
	struct connection_cache *CX;
	PORTS ports_imaponly[] = { PORT_imap4, PORT_end };
	int got;
	
	if (code == 0 || rest)
	    panic("BROWSER PANIC",__FILE__,__LINE__,"dummy_to_imap",
		  "Bad return from split_remote_name",0);
	
	DPRINT(Debug,15,(&Debug,
			 "dummy_to_imap: Using user=%s host=%s\n",
			 X.username,X.host));
	
	/* Only IMAP connections are cached */
	CX = locate_from_cache(X.username,X.host,&IMAP_connection,
			       0  /* Default port */);
	if (CX) {
	    DPRINT(Debug,10,(&Debug,
			     "dummy_to_imap: Changing browser to IMAP browser\n"));
	    
	    change_type(dir,&imap_browser);
	    clear_dir_vector(dir);
	    browser_from_connection(CX,dir);
	    
	    ret = 1;	  
	} else if (connect_remote_account(&X,&got,se,ports_imaponly,
					  PORT_end)) {
	    DPRINT(Debug,10,(&Debug,
			     "dummy_to_imap: Changing browser to IMAP browser\n"));
		
	    change_type(dir,&imap_browser);
	    clear_dir_vector(dir);
		
	    ret = join_connection(dir->a.imap_browser.Ch,&X,
				  CON_greeting);
	}
    }
    free_remote_account(&X);
    free_temporary_service_entry(&se);

    DPRINT(Debug,15,(&Debug,"dummy_to_imap=%d\n",ret));

    return ret;
}
#endif /* REMOTE_MBX */


/* Returns name relative to directory of browser */
static struct string * browser_descend_dummy P_((struct folder_browser *dir,
						 const struct string *rel_name));
static struct string * browser_descend_dummy(dir,rel_name)
     struct folder_browser *dir;
     CONST struct string *rel_name;
{
    int L, idx;
    static struct string * ret = NULL;

    int last_idx_ok = -1;

    DPRINT(Debug,12,(&Debug,"browser_descend_dummy: dir=%p, ", dir));

    if (!rel_name) {
	DPRINT(Debug,12,(&Debug,"rel_name=NULL\n"));
	DPRINT(Debug,12,(&Debug,"browser_descend_dummy=NULL\n"));
	return NULL;
    } 
    DPRINT(Debug,12,(&Debug,"rel_name=%S\n",rel_name));    

    L = string_len(rel_name);

    for (idx = 0; idx < L ; idx++) {
	uint16 code = give_unicode_from_string(rel_name,idx);

	if (0x002F /* '/' */ == code) {
	    int X1 = 0;
		
	    /* 1) First check is current path have same prefix ... */
	    struct string * A1 = clip_from_string(rel_name,&X1,idx+1);	    
	    if (dir->dirname) {
		int X2 = 0;
		struct string * A2 = clip_from_string(dir->dirname,&X2,idx+1); 

		if (X1 == X2 &&
		    0 == string_cmp(A1,A2,-99 /* Unknown indicator */ )) {

		    DPRINT(Debug,12,(&Debug,
				     "browser_descend_dummy: sep idx=%d -- so far ok\n",
				     idx));

		    last_idx_ok = idx;
		    free_string(&A1);
		    free_string(&A2);
		    continue;
		}
		
		free_string(&A2);
	    }

	    /* 2) Then check if current path have prefix of wanted */
	    if (!dir->dirname || 
		0 != string_cmp(A1,dir->dirname,
				-99 /* unknown indicator */)) {
		struct string * Lstr =  
		    convert_string(local_fs_charset,A1,0);
		unsigned char * str  = 
		    stream_from_string(Lstr,0,NULL);
		struct stat buf;

		DPRINT(Debug,12,(&Debug,
				 "browser_descend_dummy: sep idx=%d -- need change of directory\n",
				 idx));
		
		if (0 != stat(us2s(str),&buf) 
#ifdef S_ISDIR
		    || !S_ISDIR(buf.st_mode)
#endif
		    ) {
		    DPRINT(Debug,12,(&Debug,
				     "browser_descend_dummy: Failed -- bailing out\n"));

		    
		    free_string(&A1);
		    free_string(&Lstr);
		    free(str);
		    break;
		}
		 
		/* Make empty selection folder */
		clear_dir_vector(dir);

		if (dir->dirname)
		    free_string(&dir->dirname);
		dir->dirname = Lstr;
		Lstr = NULL;
		
		if (dir->sys_dir) {
		    free(dir->sys_dir);
		    dir->sys_dir = NULL;
		}
		dir->sys_dir = us2s(str);
		str = NULL;
	    }

	    DPRINT(Debug,12,(&Debug,
			     "browser_descend_dummy: sep idx=%d -- ok\n",
			     idx));
	    last_idx_ok = idx;		
	    free_string(&A1);
	}
    }

    DPRINT(Debug,12,(&Debug,
		     "browser_descend_dummy: Up to %d OK\n",last_idx_ok));

    last_idx_ok++;

    ret = clip_from_string(rel_name,&last_idx_ok,string_len(rel_name));
    
    DPRINT(Debug,10,(&Debug,
		     "*** %S as relative to %S is %S\n",
		     rel_name,dir->dirname,ret));

    DPRINT(Debug,12,(&Debug,
		     "browser_descend_dummy=%S   -- consumed to %d\n",
		     ret,last_idx_ok));
    return ret;
}

/* rel_dirname is relative to type -- not include user@hostname 

   rel_dirname is NULL is indicating that selection is on
   dir->a.dummy_browser.remote
 */

/* This assumes that NO default directory -- 
 * because there is no directory listing
 * that is NO-OP
 */

static int browser_change_v_dummy P_((struct folder_browser *dir,
				      struct name_vector *X,
				      struct string **dispname));
static int browser_change_v_dummy(dir,X,dispname)
     struct folder_browser *dir;
     struct name_vector *X;
     struct string **dispname;
{
    DPRINT(Debug,11,(&Debug,"browser_change_v_dummy=0: dir=%p\n", dir));

    return 0;   /* UNSUPPORTED */
}

static void browser_gen_default_menu P_((struct folder_browser *dir));
static void browser_gen_default_menu(dir)
     struct folder_browser *dir;
{
    DPRINT(Debug,11,(&Debug,"browser_gen_default_menu: dir=%p\n", dir));
 
    /* Make top level selection folder */
    clear_dir_vector(dir);
    
    if (dir->dirname)
	free_string(&dir->dirname);
    if (dir->sys_dir) {
	free(dir->sys_dir);
	dir->sys_dir = NULL;
    }
    
    /* Local default directory (also the last folder used on src/file.c) */
    if (0 == access(".",READ_ACCESS)) {
	/* WARNING: add_dir_vector does not allocate strings -- 
	 *          it just assign pointers!
	 */
	add_dir_vector(dir,
		       safe_strdup("."),new_string2(local_fs_charset,
						    s2us(".")),
		       BROWSER_NOFOLDER);
    }
    
    switch(dir->sel_type) {
    case selection_folder:

	{
	    /* give_dt_estr_as_str adds / to end */
	    char * folders_val = give_dt_estr_as_str(&folders_e,"maildir");

	    /* Local folders directory */
	    if (folders_val && 0 == access(folders_val,READ_ACCESS)) {
		char * str = safe_strdup(folders_val);

		/* WARNING: add_dir_vector does not allocate strings -- 
		 *          it just assign pointers!
		 */
		add_dir_vector(dir,str,new_string2(system_charset,s2us("=")),
			       BROWSER_NOFOLDER);
	    }
	}
	
	{
	    char * default_val = give_dt_estr_as_str(&defaultfile_e,"incoming-mailbox");

	    /* Incoming mailbox (may also be IMAP mailbox) */
	    if (default_val) {
		/* WARNING: add_dir_vector does not allocate strings -- 
		 *          it just assign pointers!
		 */
		add_dir_vector(dir,
			       safe_strdup(default_val),
			       new_string2(system_charset,s2us("!")),
			       BROWSER_NODIR);
	    }	
	}

	{
	    char * recvd_val = give_dt_estr_as_str(&recvd_mail_e,"receivedmail");

	    /*  ">" -- usually =received folder */
	    if (recvd_val) {
		/* WARNING: add_dir_vector does not allocate strings -- 
		 *          it just assign pointers!
		 */
		add_dir_vector(dir,
			       safe_strdup(recvd_val),
			       new_string2(system_charset,s2us(">")),
			       BROWSER_NODIR);
	    }
	}

	{
	    char * sent_val = give_dt_estr_as_str(&sent_mail_e,"sentmail");

	    /*  "<" -- usually =sent folder */
	    if (sent_val) {
		/* WARNING: add_dir_vector does not allocate strings -- 
		 *          it just assign pointers!
		 */
		add_dir_vector(dir,
			       safe_strdup(sent_val),
			       new_string2(system_charset,s2us("<")),
			       BROWSER_NODIR);
	    }
	}
	break;
	
    case selection_file:
	/* Local root */
	if (0 == access("/",READ_ACCESS)) {
	    /* WARNING: add_dir_vector does not allocate strings -- 
	     *          it just assign pointers!
	     */

	    add_dir_vector(dir,
			   safe_strdup("/"),
			   new_string2(system_charset,s2us("/")),
			   BROWSER_NOFOLDER);
	}

	{
	    char * attachment_dir_val = 
		give_dt_estr_as_str(&attachment_dir_e,
				    "attachment-dir");

	    /* Attachment directory for {doc}/ */
	    if (attachment_dir_val &&
		0 == access(attachment_dir_val,READ_ACCESS)) {
		/* WARNING: add_dir_vector does not allocate strings -- 
		 *          it just assign pointers!
		 */
		
		add_dir_vector(dir,
			       elm_message(FRM("%s/"),attachment_dir_val),
			       new_string2(system_charset,s2us("{doc}/")),
			       BROWSER_NOFOLDER);
	    }
	}
	break;
    }
	
    /* User's local home directory */
    if (home[0] && 0 == access(home,READ_ACCESS)) {
	/* WARNING: add_dir_vector does not allocate strings -- 
	 *          it just assign pointers!
	 */

	char * str = safe_strdup(home);
	int l = strlen(str);
	if (str[l-1] != '/')
	    str = strmcat(str,"/");
	add_dir_vector(dir,str,new_string2(system_charset,s2us("~")),
		       BROWSER_NOFOLDER);
    }

}

static int browser_change_up_dummy P_((struct folder_browser *dir,
				      struct string **dispname));
static int browser_change_up_dummy(dir,dispname)
     struct folder_browser *dir;
     struct string **dispname;
{
    int ret = 0;

    DPRINT(Debug,11,(&Debug,"browser_change_up_dummy: dir=%p\n", dir));

    if (dir->a.dummy_browser.remote) {
	/* Deselect remote mode ... */

	DPRINT(Debug,11,(&Debug,
			 "browser_change_up_dummy: Deselecting remote mode...\n"));
	free(dir->a.dummy_browser.remote);
	dir->a.dummy_browser.remote = NULL;

	ret = -1;
    } else {
	int L   = string_len(dir->dirname);
	int L1  = -1;
	int idx;

	for (idx = 0; idx < L ; idx++) {
	    uint16 code = give_unicode_from_string(dir->dirname,idx);
	    
	    if (0x002F /* '/' */ == code) 
		L1 = idx;
	}

	if (L1 < L-1)
	    L1++;

	if (0 == L1) {
	    ret = -1;
	} else {
	    int X = 0;
	    struct string * A1 = clip_from_string(dir->dirname,&X,L1);

	    if (*dispname)
		free_string(dispname);	    
	    *dispname = A1;

	    ret = 1;
	}

    }

    DPRINT(Debug,11,(&Debug,"browser_change_up_dummy=%d\n", ret));
    return ret;   
}


static int browser_change_dummy P_((struct folder_browser *dir,
				    struct string *rel_dirname,
				    struct string **dispname));
static int browser_change_dummy(dir,rel_dirname,dispname)
     struct folder_browser *dir;
     struct string *rel_dirname;
     struct string **dispname;
{
    int ret = 0;

    DPRINT(Debug,11,(&Debug,"browser_change_dummy: dir=%p\n", dir));
    
    if (rel_dirname) {
	DPRINT(Debug,11,(&Debug,"browser_change_dummy: rel_dirname=%S\n", 
			 rel_dirname));
    } else {
	DPRINT(Debug,11,(&Debug,"browser_change_dummy: rel_dirname=NULL\n"));
    }

    if (dir->a.dummy_browser.remote) {     /* Do not change menu */
	switch(dir->sel_type) {

	case selection_file:
	    DPRINT(Debug,11,(&Debug,
			     "browser_change_dummy: File browser does not support remote directories\n"));

	    break;
	case selection_folder:
#ifdef REMOTE_MBX
	    /* On that point dir->a.dummy_browser.remote is user@host
	       indicating of POP or IMAP mailbox.
	       
	       But if user requires directory content from that it
	       must be IMAP server directory then.
	    */
	
	    if (dummy_to_imap(dir)) {	
		ret = dir->type->browser_change_it(dir,rel_dirname,dispname);
		goto clean;
	    }
#endif /* REMOTE_MBX */
	    break;
	}
	lib_error(CATGETS(elm_msg_cat, MeSet, MeRemoteDirNotAvail,
			  "Remote directries are not available in %s"),
		  dir->a.dummy_browser.remote);

    } else if (rel_dirname &&    
	       string_len(rel_dirname) > 0) {   
	/* Menu on case where there is no DIROPS ... */

	/* Dummy operation ... but for consistency ... */
	struct string * relative = browser_descend_dummy(dir,rel_dirname);

	struct string * Lstr =  convert_string(local_fs_charset,rel_dirname,0);
	unsigned char * str  = stream_from_string(Lstr,0,NULL);
	struct stat buf;

	if (relative)
	    free_string(&relative);

	DPRINT(Debug,12,(&Debug,
			 "browser_change_dummy: Trying stat %s\n",
			 str));

	if (0 != stat(us2s(str),&buf)) {
	    int err = errno;
	    lib_error(CATGETS(elm_msg_cat, MeSet, MeDirError,
			      "Directory %S: %s"),
		      Lstr, error_description(err));
#ifdef S_ISDIR
	} else if (!S_ISDIR(buf.st_mode)) {
	    lib_error(CATGETS(elm_msg_cat, MeSet, MeNoDirError,
			      "Not a directory: %S"),
		      Lstr);	    
#endif
	} else {
	    ret = 1;

	    /* Make empty selection folder */
	    clear_dir_vector(dir);

	    if (dir->dirname)
		free_string(&dir->dirname);
	    /* Copy just from edit buffer */
	    dir->dirname = dup_string(*dispname);

	    if (dir->sys_dir) {
		free(dir->sys_dir);
		dir->sys_dir = NULL;
	    }
 	    dir->sys_dir = us2s(str);
	    str = NULL;
	}

	if (Lstr)
	    free_string(&Lstr);
	if (str)
	    free(str);
    } else {       /* Default menu */
	ret = 1;
	browser_gen_default_menu(dir);
    }

#ifdef REMOTE_MBX
 clean:
#endif

    DPRINT(Debug,11,(&Debug,"browser_change_dummy=%d\n", ret));

    return ret;
}

int browser_select_generic(dir,relative_path,
			   rel_dirname,relative,Lstr,str,sep,default_charset)
     struct folder_browser *dir;
     struct string * relative_path;
     CONST struct string * rel_dirname;
     struct string * relative;
     struct string ** Lstr;
     char ** str;
     int sep;
     charset_t default_charset;
{
    int ret = -1;

    int i;
    int l1 = dir->sys_dir ? strlen(dir->sys_dir) : 0;
    int add_sep = 0;
    
    DPRINT(Debug,12,(&Debug,"browser_select_generic: dir=%p\n", 
		dir));
    
    if (!relative) {
	DPRINT(Debug,12,(&Debug,
			 "browser_select_generic: 'relative' (to directory) name is not given, using 'rel_dirname' (to service)\n"));
	goto recover;
    }

    if (sep == '\0' && l1 > 0) {
	DPRINT(Debug,12,(&Debug,
			 "browser_select_generic: Separator not given!\n"));
	goto recover;
    }
    
    if (l1 > 0 && sep != dir->sys_dir[l1-1]) {	
	add_sep = 1;
    }
    
    if (0 == string_len(relative)) {
	DPRINT(Debug,12,(&Debug,
			 "browser_select_generic: 'relative' is empty -- directory prefix is given\n"));
	ret = -2;

	if (! dir->sys_dir)
	    goto recover;
	
	
	*Lstr = dup_string(relative_path);
	if (add_sep)
	    fill_ascii_to_string(*Lstr,1,sep);
	
	*str = safe_strdup(dir->sys_dir);
	if (add_sep) {
	    char buf[2];
	    buf[0] = sep;
	    buf[1] = '\0';
	    *str = strmcat(*str,buf);
	}

	DPRINT(Debug,12,(&Debug,
			 "browser_select_generic: Using name %s (%S) from prefix, separator %c\n",
			 *str,*Lstr,sep));
	
	goto done;
    }


    /* Update cache ... */
    browser_vector_len(dir);

    for (i = 0; i < dir->vector_len; i++)
	if (0 == (string_cmp(relative,
			     dir->vector[i].disp_name,
			     -99 /* Unknown indicator */)))
	    break;

    if (i < dir->vector_len) {

	if (dir->sys_dir) {
	    struct string * XX;

	    *Lstr = dup_string(relative_path);
	    if (add_sep)
		fill_ascii_to_string(*Lstr,1,sep);
	    
	    XX = *Lstr;
	    *Lstr = cat_strings(XX,dir->vector[i].disp_name,0);
	    free_string(&XX);
		
	    *str = safe_strdup(dir->sys_dir);
	    if (add_sep) {
		char buf[2];
		buf[0] = sep;
		buf[1] = '\0';
		*str = strmcat(*str,buf);
	    }
	    *str = strmcat(*str,dir->vector[i].sys_name);
	} else {
	    *Lstr = dup_string(dir->vector[i].disp_name);
	    *str  = safe_strdup(dir->vector[i].sys_name);
	}

	ret = i;
	
	DPRINT(Debug,12,(&Debug,
			 "browser_select_generic: Using name %s (%S) from listing, separator %c\n",
			 *str,*Lstr,sep));
    } else {
    recover:
	*Lstr =  convert_string(default_charset,rel_dirname,0);
	*str  = us2s(stream_from_string(*Lstr,0,NULL));
	
	DPRINT(Debug,12,(&Debug,
			 "browser_select_generic: Using system name %s (%S)\n",
			 *str,*Lstr));
	if (rel_dirname)
	    DPRINT(Debug,11,(&Debug,"         ... rel_dirname was %S\n",
			     rel_dirname));

    }
    
    if (relative_path && relative)
	DPRINT(Debug,12,(&Debug,
			 "         ... relative path was %S and item %S\n",
			 relative_path,relative));
    
 done:
    DPRINT(Debug,12,(&Debug,"browser_select_generic=%d %s\n",
		     ret,
		     ret < 0 ? "(no index)" : "(index of listing)"));
    return ret;
}

/* rel_itemname is relative to type -- not include user@hostname 

   rel_itemname is NULL is idicating that selection is on
   dir->a.dummy_browser.remote
*/

int real_select_local(dir,rel_itemname,relative)
     struct folder_browser *dir;
     CONST struct string *rel_itemname;
     struct string * relative;
{
    struct string * Lstr =  NULL;
    char          * str  = NULL;
    int F = 0;
    struct stat    bufX, *buf = NULL;	 /* stat command  */
    int idx;
    
    /* give_dt_estr_as_str adds / to end */
    char * folders_val = give_dt_estr_as_str(&folders_e,"maildir");

    char * mbx_dir = give_dt_estr_as_str(&extra_mailbox_dir_e,
					 "extra-mailbox-dir");



    idx = browser_select_generic(dir,dir->dirname,
				 rel_itemname,relative,&Lstr,&str,'/',
				 local_fs_charset);
		
    if (0 == stat(str,&bufX)) {
	DPRINT(Debug,11,(&Debug,
		    "real_select_local: %s exists\n",
		    str));
	F = BROWSER_EXIST;
	buf = &bufX;
    } else {
	DPRINT(Debug,11,(&Debug,
		    "real_select_local: %s : stat fail\n",
		    str));
    }
	
    if (in_directory(buf,str,mailhome))
	F |= BROWSER_MAILFILE;
    else if (mbx_dir &&
	     in_directory(buf,str,mbx_dir))
	F |= BROWSER_MAILFILE;
    else if (folders_val &&
	     in_directory(buf,str,folders_val))
	F |= BROWSER_MAILFILE;

    if (idx >= 0) {
 	DPRINT(Debug,11,(&Debug,
			 "real_select_local: Using index %d: %s\n",
			 idx,dir->vector[idx].sys_name));

	if (dir->vector[idx].flags & BROWSER_NEEDSTAT) {
	    DPRINT(Debug,11,(&Debug,
			     "real_select_local: Updating flags -- doing stat\n"));
	    dir->type->browser_stat_routine(dir,idx);
	}  

	/* WARNING: set_dir_selection does not allocate strings -- 
	 *          it just assign pointers!
	 */
	set_dir_selection(dir,
			  safe_strdup(dir->vector[idx].sys_name),
			  dup_string(dir->vector[idx].disp_name),
			  dir->vector[idx].flags|F);
    } else {
	struct string * ConvRelative = convert_string(local_fs_charset,
						      relative,0);
	char * rel_str = us2s(stream_from_string(ConvRelative,0,NULL));

	DPRINT(Debug,11,(&Debug,
			 "real_select_local: Using given name: %s\n",
			 rel_str));

	if (-2 == idx) {
	    DPRINT(Debug,11,(&Debug,
			     "real_select_local: Directory prefix was given.\n"));
	    F |= BROWSER_DIRPREFIX;
	}


	/* WARNING: set_dir_selection does not allocate strings -- 
	 *          it just assign pointers!
	 */
	set_dir_selection(dir,rel_str,dup_string(relative),0|F);	    
	free_string(&ConvRelative);
    }

    if (Lstr)
	free_string(&Lstr);
    if (str)
	free(str);

    DPRINT(Debug,12,(&Debug,
		     "real_select_local=1 dir=%p\n",
		     dir));
    return 1;
}

static int browser_select_dummy P_((struct folder_browser *dir,
				    const struct string *rel_itemname,
				    struct string **dispname));
static int browser_select_dummy(dir,rel_itemname,dispname)
     struct folder_browser *dir;
     CONST struct string *rel_itemname;
     struct string **dispname;
{
    int ret = 0;

    DPRINT(Debug,11,(&Debug,"browser_select_dummy: dir=%p\n", dir));

    if (rel_itemname) {
	DPRINT(Debug,11,(&Debug,"browser_select_dummy: rel_itemname=%S\n", 
			 rel_itemname));
    } else {
	DPRINT(Debug,11,(&Debug,"browser_select_dummy: rel_itemname=NULL\n"));
    }

    if (dir->a.dummy_browser.remote) {     /* Do not change menu */
	
	switch(dir->sel_type) {
	case selection_file:
	    DPRINT(Debug,11,(&Debug,
			     "browser_change_dummy: File browser does not support remote directories\n"));
	    
	    lib_error(CATGETS(elm_msg_cat, MeSet, MeRemoteFileNotAvail,
			      "Remote file is not available in %s"),
		      dir->a.dummy_browser.remote);
	    break;
	case selection_folder:
#ifdef REMOTE_MBX
	    /* On that point dir->a.dummy_browser.remote is user@host
	       indicating of POP or IMAP mailbox.
	       
	       But if rel_itemname is non-NULL, then it must be on
	       IMAP server directory...
	    */
	
	    if (rel_itemname) {
		/* IMAP directory then ... */
		
		if (dummy_to_imap(dir)) {
		    ret = dir->type->browser_select_it(dir,rel_itemname,
						       dispname);
		    goto clean;
		}	   
	    } else {
		/* Assume OK */
		
		/* Null selection when remote mode ... */
		set_dir_selection(dir,NULL,NULL,
				  BROWSER_EXIST       /* Assume existance */
				  |BROWSER_MAILFILE   /* Assume folder    */
				  );
		ret = 1;
		goto clean;
	    }	
#endif /* REMOTE_MBX */	    
	    lib_error(CATGETS(elm_msg_cat, MeSet, MeRemoteFolderNotAvail,
			      "Remote folder is not available in %s"),
		      dir->a.dummy_browser.remote);
	    break;
	}    


    } else if (rel_itemname) {
	
	/* Make selection relative ... */
	struct string * relative = browser_descend_dummy(dir,rel_itemname);
	
	if (relative) {
	    ret = real_select_local(dir,rel_itemname,relative);
	    free_string(&relative);
	}       
    }

#ifdef REMOTE_MBX
 clean:
#endif
    
    DPRINT(Debug,11,(&Debug,"browser_select_dummy=%d\n", ret));
    
    return ret;
}


static struct string * browser_give_title_dummy P_((struct folder_browser *dir));
static struct string * browser_give_title_dummy(dir)
     struct folder_browser *dir;
{
    static struct string *ret;

    DPRINT(Debug,11,(&Debug,"browser_give_title_dummy: dir=%p\n", dir));

    if (dir->dirname)
	ret = format_string(CATGETS(elm_msg_cat, MeSet,MeNoDirListing,
				    "Directory listing for %S not available"),
				    dir->dirname);
    else 
	ret = format_string(CATGETS(elm_msg_cat, MeSet,MeDefaultDir,
				    "Default directory"));

    DPRINT(Debug,11,(&Debug,"browser_give_title_dummy=%S\n", ret));
    
    return ret;
}

static char browser_separator_dummy P_((struct folder_browser *dir));
static char browser_separator_dummy(dir)
     struct folder_browser *dir;
{
    if (dir->a.dummy_browser.remote)
	return '\0';
    else
	return '/';
}

static struct string * browser_name_dummy P_((struct folder_browser *dir));
static struct string * browser_name_dummy(dir)
     struct folder_browser *dir;
{
    static struct string *ret;

    DPRINT(Debug,11,(&Debug,"browser_name_dummy: dir=%p\n", 
		     dir));

    if (dir->a.dummy_browser.remote) {
	ret = format_string(CATGETS(elm_msg_cat, MeSet,MeRemoteServer,
				    "Remote server %s"),
			    dir->a.dummy_browser.remote);
    } else if (dir->dirname) {
	ret = format_string(CATGETS(elm_msg_cat, MeSet,MeLocalDir,
				    "Local directory %S"),
			    dir->dirname);
    } else {
	ret = format_string(CATGETS(elm_msg_cat, MeSet,MeDefaultDir,
				    "Default directory"));
    }

    DPRINT(Debug,11,(&Debug,"browser_name_dummy=%S\n", ret));
    return ret;
}

static struct string * browser_cat_dummy P_((struct folder_browser *dir,
					     struct string * item));
static struct string * browser_cat_dummy(dir,item)
     struct folder_browser *dir;
     struct string * item;
{
    struct string * ret = NULL;
    int L;

    DPRINT(Debug,11,(&Debug,"browser_cat_dummy: dir=%p\n", 
		     dir));

    
    if (dir->a.dummy_browser.remote) {
	switch(dir->sel_type) {
	case selection_file:
	    DPRINT(Debug,11,(&Debug,
			     "browser_cat_dummy: File browser does not support remote directories\n"));
	    
	    lib_error(CATGETS(elm_msg_cat, MeSet, MeRemoteFileNotAvail,
			      "Remote file is not available in %s"),
		      dir->a.dummy_browser.remote);
	    break;
	case selection_folder:
#ifdef REMOTE_MBX
	    /* Must have IMAP folder !! */
	    if (dummy_to_imap(dir)) {
		ret = dir->type->browser_cat_it(dir,item);
		goto clean;
	    }
#endif
	    lib_error(CATGETS(elm_msg_cat, MeSet, MeRemoteFolderNotAvail,
			      "Remote folder is not available in %s"),
		      dir->a.dummy_browser.remote);
	    break;
	}
	goto clean;
    }

    if (dir->dirname &&
	(L = string_len(dir->dirname)) > 0) {
	
	ret = dup_string(dir->dirname);
	if (L > 0 && 
	    0x002F /* '/' */  !=  give_unicode_from_string(dir->dirname,
							   L-1))
	    fill_ascii_to_string(ret,1,'/');

	/* Some local directory! */
	if (item) {
	    struct string * XX = ret;
	    ret = cat_strings(XX,item,0);
	    free_string(&XX);
	}
	
    } else {
	/* Default MENU ! */

	if (item)
	    ret = dup_string(item);
	else
	    ret = new_string(system_charset);
    }
    
 clean:
    if (ret) {
	DPRINT(Debug,11,(&Debug,"browser_cat_dummy=%S\n",ret));
    } else {
	DPRINT(Debug,11,(&Debug,"browser_cat_dummy=NULL\n"));
    }
    return ret;
}

struct folder_info * real_folder_from_local(dir)
     struct folder_browser *dir;
{
    struct folder_info *res = NULL;

    DPRINT(Debug,12,(&Debug,"real_folder_from_local: dir=%p\n", dir));

    if (dir->selection->sys_name) {  /* Must have local file */
	enum folder_place place;

	res = mbx_new_folder();

	if (dir->sys_dir && dir->sys_dir[0]) {
	    res -> cur_folder_sys = elm_message(FRM("%s/%s"),
						dir->sys_dir,
						dir->selection->sys_name);
	    res -> cur_folder_disp = format_string(FRM("%S/%S"),
						   dir->dirname,
						   dir->selection->disp_name);
	} else {
	    res -> cur_folder_sys  = safe_strdup(dir->selection->sys_name);
	    res -> cur_folder_disp = dup_string(dir->selection->disp_name);
	}
	res-> folder_type = get_folder_type(res -> cur_folder_sys,&place);

	switch (place) {
	case in_home:
	    free_string(&(res -> cur_folder_disp));
	    res -> cur_folder_disp = format_string(FRM("~/%S"),
						   dir->selection->disp_name);
	    break;
	case in_folders:
	    free_string(&(res -> cur_folder_disp));
	    res -> cur_folder_disp = format_string(FRM("=%S"),
						   dir->selection->disp_name);
	    break;
	}

	res-> folder_type->init_it(res);
    }

    if (res) {
	DPRINT(Debug,12,(&Debug,"real_folder_from_local=%p\n",res));
    } else {
	DPRINT(Debug,12,(&Debug,"real_folder_from_local=NULL\n"));
    }

    return res;
}

static struct folder_info * 
browser_folder_from_dummy P_((struct folder_browser *dir));

static struct folder_info * browser_folder_from_dummy(dir)
     struct folder_browser *dir;
{
    struct folder_info * res = NULL;

    DPRINT(Debug,11,(&Debug,"browser_folder_from_dummy: dir=%p\n", 
		     dir));

    if (dir->sel_type != selection_folder)
	panic("BROWSER PANIC",__FILE__,__LINE__,
	      "browser_folder_from_dummy",
	      "Bad selection type",0);

    if (dir->a.dummy_browser.remote) {
#ifdef REMOTE_MBX

	if (dir->selection->sys_name) {
	    /* Must have IMAP folder !! */
	    if (dummy_to_imap(dir)) {
		res = dir->type->browser_folder_from_it(dir);
		goto clean;
	    }
	} else {
	    struct remote_account X;
	    struct service_entry *se = NULL;
	    char *rest = NULL;
	    int code;
    
	    zero_remote_account(&X);

	    /* -1 == name not found or bad syntax
	       0 == not a remote address
	       1 == name found 
	    */
	    if (0 <= (code = split_remote_name(dir->a.dummy_browser.remote,
					       &X,&se,&rest,
					       0 /* Both POP and IMAP are OK */
					       ))) {

		if (code == 0 || rest)
		    panic("BROWSER PANIC",__FILE__,__LINE__,
			  "browser_folder_from_dummy",
			  "Bad return from split_remote_name",0);

		res = mbx_new_folder();
		
		res -> cur_folder_sys = 
		    safe_strdup(dir->a.dummy_browser.remote);
		res -> cur_folder_disp =
		    new_string2(display_charset,
				s2us(dir->a.dummy_browser.remote));

		/* May free X (if succeed) or copy
		   If make copy of X, then X is zeroed 
		*/
		if (!make_remote_mbox(res,&X,se,NULL,1)) {
		    
		    res -> folder_type = NO_NAME;
		    res->folder_type->init_it(res);
		}	           
		free_temporary_service_entry(&se);
		free_remote_account(&X);

		goto clean;		
	    }
	    free_temporary_service_entry(&se);
	    free_remote_account(&X);
	}
#endif
	lib_error(CATGETS(elm_msg_cat, MeSet, MeRemoteFolderNotAvail,
			  "Remote folder is not available in %s"),
		  dir->a.dummy_browser.remote);

    } else 
	res = real_folder_from_local(dir);

#ifdef REMOTE_MBX
 clean:
#endif

    if (res) {
	DPRINT(Debug,11,(&Debug,"browser_folder_from_dummy=%p\n",res));
    } else {
	DPRINT(Debug,11,(&Debug,"browser_folder_from_dummy=NULL\n"));
    }
    return res;
}

int real_selection_is_local(dir,folder)
     struct folder_browser *dir;
     struct folder_info *folder;
{
    int ret = 0;

    DPRINT(Debug,11,(&Debug,"real_selection_is_local: dir=%p\n", 
		     dir));

    if (folder->folder_type != &read_only &&
	folder->folder_type != &non_spool &&
	folder->folder_type != &spool) {
	DPRINT(Debug,11,(&Debug,
			 "real_selection_is_local: Not a local folder\n"));
	ret = 0;
    } else {
	char * name;

	if (dir->sys_dir && dir->sys_dir[0])
	    name = elm_message(FRM("%s/%s"),
					    dir->sys_dir,
					    dir->selection->sys_name);
	else
	    name = safe_strdup(dir->selection->sys_name);
	
	if (0 == strcmp(name,folder->cur_folder_sys)) {
	    DPRINT(Debug,11,(&Debug,
			"real_selection_is_local: Same filaname\n"));
	    ret = 1;
	} else {
	    struct stat XX, A;

	    if (-1 == stat(name,&A)) {
		int err = errno;
		DPRINT(Debug,11,(&Debug,
				 "real_selection_is_local: Failed to stat %s: %s\n",
				 name,
				 error_description(err)));
		err = 0;
	    } else if ((NULL != folder ->p->fh_folder &&
			0 == fstat(fileno(folder ->p->fh_folder),&XX)) 
		       ||
		       0 == stat(folder->cur_folder_sys,&XX)) {
		if (XX.st_ino == A.st_ino && XX.st_dev == A.st_dev) {
		  DPRINT(Debug,11,(&Debug,
				   "real_selection_is_local: Same ino and dev\n"));
		  ret = 1;
		} else
		    ret = 0;
	    } else {
		int err = errno;
		DPRINT(Debug,11,(&Debug,
				 "real_selection_is_local: Both fstat and stat failed: %s\n",
				 error_description(err)));
		ret = 0;
	    }
	}
	if (name)
	    free(name);
    }

    DPRINT(Debug,11,(&Debug,"real_selection_is_local=%d\n",ret));
    return ret;
}

static int browser_selection_is_dummy P_((struct folder_browser *dir,
					  struct folder_info *folder));

static int browser_selection_is_dummy(dir,folder)
     struct folder_browser *dir;
     struct folder_info *folder;
{
    int ret = 0;
    DPRINT(Debug,11,(&Debug,"browser_selection_is_dummy: dir=%p\n", 
		     dir));

    if (dir->sel_type != selection_folder)
	panic("BROWSER PANIC",__FILE__,__LINE__,
	      "browser_selection_is_dummy",
	      "Bad selection type",0);
    
    if (dir->a.dummy_browser.remote) {
#ifdef REMOTE_MBX
	if (folder->folder_type == POP_MBX) {
	    DPRINT(Debug,11,(&Debug,
			     "browser_selection_is_dummy: Maybe POP ...\n"));
	    ret = 0 == strcmp(dir->a.dummy_browser.remote,
			      folder->cur_folder_sys);

	} else if (folder->folder_type == IMAP_MBX) {
	    DPRINT(Debug,11,(&Debug,
			     "browser_selection_is_dummy: Maybe IMAP ...\n"));  
	    if (dummy_to_imap(dir)) {
		ret = dir->type->browser_selection_is_it(dir,folder);
	    } else {
		DPRINT(Debug,11,(&Debug,
				 "browser_selection_is_dummy: IMAP failed\n"));
		ret = 0;
	    }
	} else {
	   DPRINT(Debug,11,(&Debug,
			    "browser_selection_is_dummy: Folder is not remote\n"));
	    ret = 0;
	} 	    
#else
	DPRINT(Debug,11,(&Debug,
			 "browser_selection_is_dummy: Remote selection is not available\n"));
	ret = 0;
#endif
    } else {	
	ret = real_selection_is_local(dir,folder);
    }
    
    DPRINT(Debug,11,(&Debug,
		     "browser_selection_is_dummy=%d\n",ret));
    return ret;
}

int create_as_user(name)
     CONST char *name;
{
    int the_stat = 0, pid, w, sig;
    VOLATILE int ret = 0;

    S__ status;
	
    if ((pid = 
#ifdef VFORK
	 vfork()
#else
	 fork()
#endif
	 ) == 0) {
	int filedes;

	if (-1 == setgid(groupid)) {
	    int err = errno;
	    fprintf(stderr,"create_as_user: setgid(%d) FAILED: %s\n",
		    groupid,error_description(err));
	    fflush(stderr);
	    _exit(err != 0? err : 1);	/* never return zero! */
	}
	if (-1 == setuid(userid)) {		/** back to normal userid **/
	    int err = errno;
	    fprintf(stderr,"create_as_user: setuid(%d) FAILED: %s\n",
		    userid,error_description(err));
	    fflush(stderr);
	    _exit(err != 0? err : 1);	/* never return zero! */
	}
	errno = 0;
	filedes = open(name, O_WRONLY | O_CREAT | O_EXCL, 0600);
	
	if (filedes < 0)
	    _exit(errno);
	else {
	    close(filedes);
	    _exit(0);
	}
    }
    
    errno = 0;
    while ((w = my_wait(pid,&status)) != pid && 
	   (w != -1 || EINTR == errno))
	;
    
    sig = convert_status(status,&the_stat);
    if (sig)
	ret = 0;
    else if (0 == the_stat)
	ret = 1;
    else
	lib_error(CATGETS(elm_msg_cat, MeSet, MeCreatingFileFailed,
			  "Creating of file %s failed: %s"),
		  name,error_description(the_stat));
    
    return ret;
}

static int browser_create_selection_dummy P_((struct folder_browser *dir));

static int browser_create_selection_dummy(dir)
     struct folder_browser *dir;
{
    int ret = 0;

    DPRINT(Debug,11,(&Debug,
		     "browser_create_selection_dummy: dir=%p\n", 
		     dir));

    if (dir->a.dummy_browser.remote) {
	lib_error(CATGETS(elm_msg_cat, MeSet, MeCreatingRemoteFolderNotSupp,
			  "Creating of remote folder for %s not supported"),
		  dir->a.dummy_browser.remote);
	
    } else {
	char * name = NULL;

	if (dir->sys_dir && dir->sys_dir[0])
	    name = elm_message(FRM("%s/%s"),
					    dir->sys_dir,
					    dir->selection->sys_name);
	else
	    name = safe_strdup(dir->selection->sys_name);

	ret = create_as_user(name);
	
	free(name);
    }

    DPRINT(Debug,11,(&Debug,"browser_create_selection_dummy=%d\n", ret));
    
    return ret;
}



/* We use same routines zero_ws_fields_local and free_ws_fields_local
 * both dummy and local browser!
 */

void zero_ws_fields_local(ptr)
     WRITE_STATE ptr;
{
    ptr->a.local.save_fd   = -1;
    ptr->a.local.save_file = NULL;
}

void free_ws_fields_local(ptr)
     WRITE_STATE ptr;
{
    /* ptr->a.local.save_file and ptr->a.local.save_fd shares same
       file descriptor!
    */

    if (ptr->a.local.save_file) {
	fclose(ptr->a.local.save_file);
	ptr->a.local.save_file = NULL;
	ptr->a.local.save_fd   = -1;
    }
    if (-1 != ptr->a.local.save_fd) {
	close(ptr->a.local.save_fd);
	ptr->a.local.save_fd  = -1;
    }
}

int real_prepare_write_local(dir,ptr,filename)
     struct folder_browser *dir;
     WRITE_STATE ptr;
     CONST char *filename;
{
    int ret = 0;
    int err = 0;
    
    switch(dir->sel_type) {
    case selection_file:
	if (0 != (err = can_open(filename, "w"))) {
	    lib_error(CATGETS(elm_msg_cat, MeSet, MeCouldnWriteFile,
			      "Couldn't write to file %s: %s"), 
		      filename,error_description(err));
	    goto fail;
	}

	DPRINT(Debug,4,(&Debug,"Writing to file '%s'...\n", filename));

	if ((ptr->a.local.save_fd = open(filename,O_WRONLY,0600)) == -1 ||
	    (ptr->a.local.save_file = fdopen(ptr->a.local.save_fd,"w")) == NULL) {
	    int err = errno;
	    
	    DPRINT(Debug,2,(&Debug,
			    "Error: couldn't write to specified file %s\n", 
			    filename));

	    lib_error(CATGETS(elm_msg_cat, MeSet, MeCouldnWriteFile,
			      "Couldn't write to file %s: %s"), 
		      filename,error_description(err));
	    goto fail;
	}	
	ret = 1;
	
	break;
    case selection_folder:
	if (0 != (err = can_open(filename, "a"))) {
	    lib_error(CATGETS(elm_msg_cat, MeSet, MeCouldntAppendFolder,
			      "Couldn't append to folder %s: %s"), 
		      filename,error_description(err));
	    goto fail;
	}
	
	save_file_stats(filename);

	DPRINT(Debug,4,(&Debug, "Saving mail to folder '%s'...\n", filename));
	
	if ((ptr->a.local.save_fd = open(filename,O_RDWR,0600)) == -1 ||
	    (ptr->a.local.save_file = fdopen(ptr->a.local.save_fd,"r+")) == NULL) {
	    int err = errno;
	    
	    DPRINT(Debug,2,(&Debug,
			    "Error: couldn't append to specified folder %s\n", 
			    filename));
	    lib_error(CATGETS(elm_msg_cat, MeSet, MeCouldntAppendFolder,
			      "Couldn't append to folder %s: %s"), 
		      filename,error_description(err));
	    goto fail;
	}
    
	if (lock_in_copy) {
	    /*  lock the file to be modified to serialize access with other */
	    /* programs like procmail. */
	
	    if (Grab_the_file(ptr->a.local.save_fd) != FLOCKING_OK){
		int err = errno;
		fclose(ptr->a.local.save_file);
		
		ptr->a.local.save_file = NULL;
		ptr->a.local.save_fd   = -1;
		restore_file_stats(filename);
		
		DPRINT(Debug,2,(&Debug,
				"Error: couldn't lock specified folder %s (save)\n", 
				filename));
		lib_error(CATGETS(elm_msg_cat, MeSet, MeCouldntLockFolder,
				  "Couldn't lock folder %s: %s"), 		      
			  filename,
			  error_description(err));
		goto fail;
	    }	
	}
    
	/* copy_message want now seek backwards ... */
	if (0 != fseek(ptr->a.local.save_file,0,SEEK_END)) {
	    int err = errno;
	    if (lock_in_copy)
		Release_the_file(ptr->a.local.save_fd);
	    
	    ptr->a.local.save_file = NULL;
	    ptr->a.local.save_fd   = -1;
	    restore_file_stats(filename);
	    
	    DPRINT(Debug,2,(&Debug,
			    "Error: couldn't seek to end of specified folder %s (save)\n", 
			    filename));
	    lib_error(CATGETS(elm_msg_cat, MeSet, MeCouldntAppendFolder,
			      "Couldn't append to folder %s: %s"), 
		      filename,error_description(err));
	    
	    goto fail;
	}    
	ret = 1;
	break;
    }

 fail:
    
    DPRINT(Debug,12,(&Debug,
		     "real_prepare_write_local=%d dir=%p filename=%s\n", 
		     ret,dir,filename));
    return ret;
}

int real_end_write_local(dir,ptr,filename)
     struct folder_browser *dir;
     WRITE_STATE ptr;
     CONST char *filename;
{
    int ret = 0;

    if (lock_in_copy)
	Release_the_file(ptr->a.local.save_fd); /* XXX LOCKING */


    /** ptr->a.local.save_file and ptr->a.local.save_fd shares same
	file descriptor!
    */

    ret = 0 == fclose(ptr->a.local.save_file);
    ptr->a.local.save_file = NULL;
    ptr->a.local.save_fd   = -1;

    switch(dir->sel_type) {
    case selection_file:
	break;
    case selection_folder:
	restore_file_stats(filename);
	break;
    }

    DPRINT(Debug,12,(&Debug,
		     "real_end_write_local=%d dir=%p filename=%s\n", 
		     ret,dir,filename));
    return ret;
}


int real_sync_write_local(dir,ptr,filename)
     struct folder_browser *dir;
     WRITE_STATE ptr;
     CONST char *filename;
{
    int ret = 0;

    /** ptr->a.local.save_file and ptr->a.local.save_fd shares same
	file descriptor!
    */

    ret = 0 == fflush(ptr->a.local.save_file);
    if (!ret) {
	DPRINT(Debug,12,(&Debug,
			 "real_sync_write_local: fflush failed\n"));
    }

    if (-1 == fsync(ptr->a.local.save_fd)) {
	DPRINT(Debug,12,(&Debug,
			 "real_sync_write_local: fsync failed\n"));
	ret = 0;
    }

    switch(dir->sel_type) {
    case selection_file:
	break;
    case selection_folder:
	break;
    }

    DPRINT(Debug,12,(&Debug,
		     "real_sync_write_local=%d dir=%p filename=%s\n", 
		     ret,dir,filename));
    return ret;
}


long real_browser_tell_ws(dir,ptr)
     struct folder_browser *dir;
     WRITE_STATE ptr;
{
    /* Both ftell and real_browser_tell_ws returns -1 on failure */
    long ret = ftell(ptr->a.local.save_file);

    if (-1 == ret) {
	int err = errno;
	DPRINT(Debug,12,(&Debug,
			 "real_browser_tell_ws: ERROR (errno=%d): %s\n",
			 err,error_description(err)));
    }

    DPRINT(Debug,12,(&Debug,
		     "real_browser_tell_ws=%ld dir=%p\n", 
		     ret,dir));
    return ret;
}

/* Returns 0 on failure! */
int real_browser_seek_ws(dir,ptr,pos)
     struct folder_browser *dir;
     WRITE_STATE ptr;
     long pos;
{
    int ret = 0;

    if (0 == fseek(ptr->a.local.save_file,pos,SEEK_SET))
	ret = 1;
    else {
	int err = errno;
	DPRINT(Debug,12,(&Debug,
			 "real_browser_seek_ws: ERROR (errno=%d): %s\n",
			 err,error_description(err)));
    }

    DPRINT(Debug,12,(&Debug,
		     "real_browser_seek_ws=%d dir=%p\n", 
		     ret,dir));
    return ret;
}

int real_browser_write_ws(dir,ptr,l,buffer)
     struct folder_browser *dir;
     WRITE_STATE ptr;
     int l; 
     CONST char *buffer;
{
    int ret = 1;    
    int count = 0;
    
    while (ret && count < l) {
	int x = fwrite(buffer+count,1,l-count,ptr->a.local.save_file);
	
	if (x > 0) 
	    count += x;
	else if (ferror(ptr->a.local.save_file)) {
	    int err = errno;

	    if (err != EINTR) {
		DPRINT(Debug,12,(&Debug,
				 "real_browser_write_ws: ERROR (errno=%d): %s\n",
				 err,error_description(err)));
		ret = 0;
	    } else {
		clearerr(ptr->a.local.save_file);
	    }
	}
	if (0 == x && ret) {
	    DPRINT(Debug,12,(&Debug,
			     "real_browser_write_ws: Looping...\n"));
	}
    }

    DPRINT(Debug,12,(&Debug,
		     "real_browser_write_ws=%d dir=%p (%d written of %d)\n", 
		     ret,dir,count,l));
    return ret;
}


static int browser_prepare_write_dummy P_((struct folder_browser *dir,
					   WRITE_STATE ptr));
static int browser_prepare_write_dummy(dir,ptr)
     struct folder_browser *dir;
     WRITE_STATE ptr;
{
    int ret = 0;

    DPRINT(Debug,11,(&Debug,"browser_prepare_write_dummy: dir=%p\n", 
		     dir));

    if (dir->a.dummy_browser.remote) {

	switch(dir->sel_type) {
	case selection_file:
	    DPRINT(Debug,11,(&Debug,
			"browser_prepare_write_dummy: File browser does not support remote directories\n"));
	    
	    lib_error(CATGETS(elm_msg_cat, MeSet, MeWritingRemoteFileNotSupp,
			      "Writing to remote file %s not supported"),
		      dir->a.dummy_browser.remote);   
	    break;
	case selection_folder:
#ifdef REMOTE_MBX
	    /* Must have IMAP folder !! */

	    dir->type->free_ws_fields_it(ptr);       /* Need reinit of state */
	    if (dummy_to_imap(dir)) {
		dir->type->zero_ws_fields_it(ptr);   /* Hopefully ok ....... */
		ret = dir->type->browser_prepare_write_it(dir,ptr);
		goto clean;
	    }
#endif /* REMOTE_MBX */
	    lib_error(CATGETS(elm_msg_cat, MeSet, MeWritingRemoteFolderNotSupp,
			      "Writing to remote folder %s not supported"),
		      dir->a.dummy_browser.remote);
	    break;
	}

    } else {
	char * name = NULL;
	
	if (dir->sys_dir && dir->sys_dir[0])
	    name = elm_message(FRM("%s/%s"),
					    dir->sys_dir,
					    dir->selection->sys_name);
	else
	    name = safe_strdup(dir->selection->sys_name);

	ret = real_prepare_write_local(dir,ptr,name);

	free(name);

    }

#ifdef REMOTE_MBX
 clean:
#endif

    DPRINT(Debug,11,(&Debug,"browser_prepare_write_dummy=%d\n", ret));

    return ret;
}

static int browser_sync_write_dummy P_((struct folder_browser *dir,
					WRITE_STATE ptr));
static int browser_sync_write_dummy(dir,ptr)
     struct folder_browser *dir;
     WRITE_STATE ptr;
{
    int ret = 0;

    DPRINT(Debug,11,(&Debug,"browser_sync_write_dummy: dir=%p\n", 
		     dir));

    if (dir->a.dummy_browser.remote) {
	panic("BROWSER PANIC",__FILE__,__LINE__,
	      "browser_sync_write_dummy",
	      "Bad remote argument",0);
    } else {
	char * name = NULL;
	
	if (dir->sys_dir && dir->sys_dir[0])
	    name = elm_message(FRM("%s/%s"),
					    dir->sys_dir,
					    dir->selection->sys_name);
	else
	    name = safe_strdup(dir->selection->sys_name);

	ret = real_sync_write_local(dir,ptr,name);

	free(name);
    }
 
    DPRINT(Debug,11,(&Debug,"browser_sync_write_dummy=%d\n", ret));
    
    return ret;
}

static int browser_end_write_dummy P_((struct folder_browser *dir,
			      WRITE_STATE ptr));
static int browser_end_write_dummy(dir,ptr)
     struct folder_browser *dir;
     WRITE_STATE ptr;
{
    int ret = 0;

    DPRINT(Debug,11,(&Debug,"browser_end_write_dummy: dir=%p\n", 
		     dir));

    if (dir->a.dummy_browser.remote) {
	panic("BROWSER PANIC",__FILE__,__LINE__,
	      "browser_end_write_dummy",
	      "Bad remote argument",0);
    } else {
	char * name = NULL;
	
	if (dir->sys_dir && dir->sys_dir[0])
	    name = elm_message(FRM("%s/%s"),
					    dir->sys_dir,
					    dir->selection->sys_name);
	else
	    name = safe_strdup(dir->selection->sys_name);

	ret = real_end_write_local(dir,ptr,name);

	free(name);
    }
 
    DPRINT(Debug,11,(&Debug,"browser_end_write_dummy=%d\n", ret));
    
    return ret;
}

static long browser_tell_dummy_ws P_((struct folder_browser *dir,
				      WRITE_STATE write_state_ptr));
static long browser_tell_dummy_ws(dir,write_state_ptr)
     struct folder_browser *dir;
     WRITE_STATE write_state_ptr;
{
    long ret = -1L;

    DPRINT(Debug,11,(&Debug,"browser_tell_dummy_ws: dir=%p\n", 
		     dir));

    if (dir->a.dummy_browser.remote) {
	panic("BROWSER PANIC",__FILE__,__LINE__,
	      "browser_tell_dummy_ws",
	      "Bad remote argument",0);
    } 
    ret = real_browser_tell_ws(dir,write_state_ptr);
    
    DPRINT(Debug,11,(&Debug,"browser_tell_dummy_ws=%ld\n",ret));
    return ret;
}

/* Returns 0 on failure */
static int browser_seek_dummy_ws P_((struct folder_browser *dir,
				      WRITE_STATE write_state_ptr,
				      long pos));
static int browser_seek_dummy_ws(dir,write_state_ptr,pos)
     struct folder_browser *dir;
     WRITE_STATE write_state_ptr;
     long pos;
{
    int ret = 0;

    DPRINT(Debug,11,(&Debug,"browser_seek_dummy_ws: dir=%p\n", 
		     dir));

    if (dir->a.dummy_browser.remote) {
	panic("BROWSER PANIC",__FILE__,__LINE__,
	      "browser_seek_dummy_ws",
	      "Bad remote argument",0);
    } 
    ret = real_browser_seek_ws(dir,write_state_ptr,pos);
    
    DPRINT(Debug,11,(&Debug,"browser_seek_dummy_ws=%d\n",ret));
    return ret;
}

static int browser_write_dummy_ws P_((struct folder_browser *dir,
				      WRITE_STATE ptr,
				      int l, const char *buffer));
static int browser_write_dummy_ws(dir,ptr,l,buffer)
     struct folder_browser *dir;
     WRITE_STATE ptr;
     int l; 
     CONST char *buffer;
{
    int ret = 0;

    DPRINT(Debug,11,(&Debug,"browser_write_dummy_ws: dir=%p\n", 
		     dir));

    if (dir->a.dummy_browser.remote) {
	panic("BROWSER PANIC",__FILE__,__LINE__,
	      "browser_write_dummy_ws",
	      "Bad remote argument",0);
    } 
    ret = real_browser_write_ws(dir,ptr,l,buffer);
    
    DPRINT(Debug,11,(&Debug,"browser_write_dummy_ws=%d\n",ret));
    return ret;
}

int real_start_we_local(dir,ptr,current_header,env_flags)
     struct folder_browser *dir;
     WRITE_STATE ptr;
     struct header_rec *current_header;
     int *env_flags;
{
    int ret = 0;
    char * buffer1 = ctime(& current_header->received_time);

    *env_flags = 0;

#ifdef MMDF
    DPRINT(Debug,5,(&Debug,
		    "real_start_we_local: Write MMDF message separator\n"));
    if (fprintf(ptr->a.local.save_file, "%s", MSG_SEPARATOR) == EOF) {
       	DPRINT(Debug,1,(&Debug, "real_start_we_local fails\n"));
	goto fail;
    }
    *env_flags = WE_ADD_RETURN_PATH;
#endif
    
    if (fprintf(ptr->a.local.save_file,
	       "From %s %.24s\n", 
	       current_header->env_from,
	       buffer1) == EOF) {
	DPRINT(Debug,1,(&Debug,"real_start_we_local fails\n"));
	goto fail;
    }
    ret = 1;

 fail:
    DPRINT(Debug,12,(&Debug,
		     "real_start_we_local=%d dir=%p\n", 
		     ret,dir));

    return ret;
}

int real_end_we_local(dir,ptr,current_header)
     struct folder_browser *dir;
     WRITE_STATE ptr;
     struct header_rec *current_header;
{
    int ret = 0;

    long END_pos = ftell(ptr->a.local.save_file);
    char buffer[2];
    int i;
    long NEW_pos = END_pos - 2L;

    /* parse_body_routine() excludes ending empty line from
       content_length, so add empty line always to mail 

       see also mbx_mark_keep_normal() on localmbx.c 
    */

    buffer[0] = '\0';
    buffer[1] = '\0';
    if (NEW_pos < 0L)
	NEW_pos = 0L;
	  
    if (0 != fseek(ptr->a.local.save_file,NEW_pos,SEEK_SET)) {
	DPRINT(Debug,5,(&Debug,
			"real_end_we_local: Failed to seek to %ld (output)\n",
			NEW_pos)); 
    }
    
    i = fread(buffer,sizeof (char),2,ptr->a.local.save_file);
    if (i < 0) {
	DPRINT(Debug,5,(&Debug,
			"real_end_we_local: Read error! (output)\n"));
	i = 0;
    } else {
	DPRINT(Debug,5,(&Debug,
			"real_end_we_local: read %d bytes (output). \n",i));
    }
    
    for (; i < 2; i++) {
	buffer[i] = '\0';
    }

    DPRINT(Debug,5,(&Debug,
		    "real_end_we_local: last two bytes %d %d (output)\n",
		    buffer[0], buffer[1])); 
    
    if (0 != fseek(ptr->a.local.save_file,0L,SEEK_CUR)) {
	DPRINT(Debug,5,(&Debug,
			"real_end_we_local: Failed to seek 0 bytes (output)\n"));
    }

    if (buffer[1] != '\n') {
	/* No \n in end ? */ 	
	DPRINT(Debug,5,(&Debug,
			"real_end_we_local: NL missing from end of mail\n")); 
	if (fprintf(ptr->a.local.save_file, "\n") == EOF) {
	    DPRINT(Debug,5,(&Debug,
			    "real_end_we_local: Failed to add NL to end of mail\n"));
	    DPRINT(Debug,1,(&Debug,"real_end_we_local fails\n"));
	    goto fail;
	}
    }

    /* NOTE:
     * Ending empty line or MSG_SEPARATOR is not part of 
     * mail, but instead part of folder format, so it
     * should NOT be included on calculation of content-length
     */
#ifndef MMDF

    /* blank line to keep mailx happy *sigh* */      
    if (fprintf(ptr->a.local.save_file, "\n") == EOF) {
	DPRINT(Debug,5,(&Debug,
			"real_end_we_local: Failed to add second NL to end of mail\n"));
	DPRINT(Debug,1,(&Debug, "real_end_we_local fails\n"));
	goto fail;
    }

#else   
    DPRINT(Debug,5,(&Debug,
		    "real_end_we_local: Write MMDF message separator (end)\n"));
    if (fprintf(ptr->a.local.save_file, "%s", MSG_SEPARATOR) == EOF) {
	DPRINT(Debug,1,(&Debug, "real_end_we_local fails\n"));
	goto fail;
    }   
#endif
    ret = 1;

 fail:
    DPRINT(Debug,12,(&Debug,
		     "real_end_we_local=%d dir=%p\n", 
		     ret,dir));
      return ret;
}


static int browser_start_we_dummy P_((struct folder_browser *dir,
				      WRITE_STATE write_state_ptr,
				      int write_envelope,
				      struct header_rec *current_header,
				      int *env_flags));
static int browser_start_we_dummy(dir,write_state_ptr,
				  write_envelope,current_header,
				  env_flags)
     struct folder_browser *dir;
     WRITE_STATE write_state_ptr;
     int write_envelope;
     struct header_rec *current_header;
     int *env_flags;
{
    int ret = 1;

    DPRINT(Debug,11,(&Debug,"browser_start_we_dummy: dir=%p\n", 
		     dir));

    if (dir->a.dummy_browser.remote) {
	panic("BROWSER PANIC",__FILE__,__LINE__,
	      "browser_start_we_dummy",
	      "Bad remote argument",0);
    } 

    *env_flags = 0;

    if (write_envelope)
	ret = real_start_we_local(dir,write_state_ptr,
				  current_header, env_flags);
	    
    DPRINT(Debug,11,(&Debug,"browser_start_we_dummy=%d\n",ret));
    return ret;
}


static int browser_end_we_dummy P_((struct folder_browser *dir,
				  WRITE_STATE write_state_ptr,
				  int write_envelope,
				  struct header_rec *current_header));
static int browser_end_we_dummy(dir,write_state_ptr,
			      write_envelope,current_header)
     struct folder_browser *dir;
     WRITE_STATE write_state_ptr;
     int write_envelope;
     struct header_rec *current_header;
{
    int ret = 1;
    DPRINT(Debug,11,(&Debug,"browser_end_we_dummy: dir=%p\n", 
		dir));

    if (dir->a.dummy_browser.remote) {
	panic("BROWSER PANIC",__FILE__,__LINE__,
	      "browser_end_we_dummy",
	      "Bad remote argument",0);
    } 

    if (write_envelope)
	ret = real_end_we_local(dir,write_state_ptr,current_header);

    DPRINT(Debug,11,(&Debug,"browser_end_we_dummy=%d\n",ret));
    return ret;
}

int real_make_ref_local(dir,refname,iscopy,is_text)
     struct folder_browser *dir;
     char **refname; 
     int *iscopy;
     int is_text;
{
    char * name = NULL;
    int ret = 0;
    
    if (dir->sys_dir && dir->sys_dir[0])
	name = elm_message(FRM("%s/%s"),
			   dir->sys_dir,
			   dir->selection->sys_name);
    else
	name = safe_strdup(dir->selection->sys_name);

    if (access(name,READ_ACCESS) < 0) {
	int err = errno;
	lib_error(CATGETS(elm_msg_cat, MeSet,
			  MeErrorRead,
			  "Can't read %S: %s"),
		  dir->selection->disp_name,error_description(err));
	ret = 0;
	free(name);
	name = NULL;
    } else {
	*iscopy = 0;
	*refname = name;
	name = NULL;
	ret = 1;

	DPRINT(Debug,12,(&Debug,"real_make_ref_local: *refname=%s\n", 
			 *refname));
    }

    DPRINT(Debug,12,(&Debug,"real_make_ref_local=%d", ret));
    
    return ret;
}


static int browser_make_ref_dummy P_((struct folder_browser *dir,
				      char **refname, int *iscopy,
				      int is_text));
static int browser_make_ref_dummy(dir,refname,iscopy,is_text)
     struct folder_browser *dir;
     char **refname; 
     int *iscopy;
     int is_text;
{
    int ret = 0;

    DPRINT(Debug,11,(&Debug,"browser_make_ref_dummy: dir=%p\n", 
		     dir));

    if (dir->sel_type != selection_file)
	panic("BROWSER PANIC",__FILE__,__LINE__,
	      "browser_make_ref_dummy",
	      "Bad selection type",0);

    if (dir->a.dummy_browser.remote) {
       	DPRINT(Debug,11,(&Debug,
			 "browser_make_ref_dummy: File browser does not support remote directories\n"));
	
	lib_error(CATGETS(elm_msg_cat, MeSet, MeRemoteFileNotAvail,
			  "Remote file is not available in %s"),
		      dir->a.dummy_browser.remote);
    } else {
	ret = real_make_ref_local(dir,refname,iscopy,is_text); 
    }

    DPRINT(Debug,11,(&Debug,"browser_make_ref_dummy=%d\n",ret));
    
    return ret;
}

static void browser_update_dummy P_((struct folder_browser *dir));
static void browser_update_dummy(dir)
     struct folder_browser *dir;
{
    panic("BROWSER PANIC",__FILE__,__LINE__,"browser_update_dummy",
	  "browser_update_dummy() called",0);	
}

/* Call only if BROWSER_NEEDSTAT is set */
static void browser_dummy_do_stat P_((struct folder_browser *dir,
				      int idx));
static void browser_dummy_do_stat(dir,idx)
     struct folder_browser *dir;
     int idx;
{
    panic("BROWSER PANIC",__FILE__,__LINE__,"browser_dummy_do_stat",
	  "browser_update_dummy() called",0);	
}


S_(browser_folder_sort_dir browser_folder_sort_dummy)
static void browser_folder_sort_dummy P_((struct folder_browser *dir,
					  print_sort_message * print));
static void browser_folder_sort_dummy(dir,print)
     struct folder_browser *dir;
     print_sort_message * print;
{
    /* Nothing */
}

static struct browser_type dummy_browser = { browser_zero_dummy,
					     browser_free_dummy,
					     browser_change_dummy,
					     browser_give_title_dummy,
					     browser_separator_dummy,
					     browser_name_dummy,
					     browser_cat_dummy,
					     browser_select_dummy,
					     browser_folder_from_dummy,
					     browser_change_v_dummy,
					     browser_change_up_dummy,
					     browser_create_selection_dummy,
					     zero_ws_fields_local,
					     free_ws_fields_local,
					     browser_prepare_write_dummy,
					     browser_end_write_dummy,
					     browser_tell_dummy_ws,
					     browser_seek_dummy_ws,
					     browser_write_dummy_ws,
					     browser_start_we_dummy,
					     browser_end_we_dummy,
					     browser_selection_is_dummy,
					     browser_make_ref_dummy,
					     browser_update_dummy,
					     browser_dummy_do_stat,
					     browser_sync_write_dummy,
					     browser_folder_sort_dummy,
};


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


static int valid_browser P_((struct browser_type    * type));
static int valid_browser(type)
     struct browser_type    * type;
{
    if (
#ifdef REMOTE_MBX
	&imap_browser == type ||
#endif
#ifdef DIROPS
	&local_browser == type ||
#endif
	&dummy_browser == type)
	return 1;
    return 0;
}


/* Folder browser */

void clear_dir_selection(dir)
     struct folder_browser *dir;
{
    DPRINT(Debug,12,(&Debug,
		     "clear_dir_selection: dir=%p; type=%p\n",
		     dir,dir->type));

    if (dir->selection) {
	if (dir->selection->sys_name) {
	    free(dir->selection->sys_name);
	    dir->selection->sys_name = NULL;
	}
	if (dir->selection->disp_name) 
	    free_string(&(dir->selection->disp_name));

	free(dir->selection);
	dir->selection = NULL;
    }
}

/* WARNING: set_dir_selection does not allocate strings -- 
 *          it just assign pointers!
 */
void set_dir_selection(dir,sys_name,disp_name,flags)
     struct folder_browser *dir;
     char *sys_name;
     struct string * disp_name;
     int flags;
{
    DPRINT(Debug,12,(&Debug,
		     "set_dir_selection: dir=%p; type=%p%s\n",
		     dir,dir->type,
		     dir->selection ? " (clearing previous selection)" : ""));

    if (dir->selection) 
	clear_dir_selection(dir);

    DPRINT(Debug,12,(&Debug,
		     "set_dir_selection: sys_name=%s, flags=%X",
		     sys_name ? sys_name : "<NULL>",flags));
    if (disp_name) {
	DPRINT(Debug,12,(&Debug,
			 ", disp_name=%S\n",disp_name));
    } else {
	DPRINT(Debug,12,(&Debug,
			 ", disp_name=NULL\n"));
    }

    dir->selection = safe_malloc(sizeof (struct name_vector));

    /* bzero is defined hdrs/defs.h */
    bzero((void *)dir->selection,sizeof (struct name_vector));
    dir->selection->sys_name  = sys_name;
    dir->selection->disp_name = disp_name;
    dir->selection->flags     = flags;    
}

void clear_dir_vector(dir)
     struct folder_browser *dir;
{
    int i;

    DPRINT(Debug,12,(&Debug,
		"clear_dir_vector: dir=%p; type=%p\n",
		dir,dir->type));
    
    if (dir->vector) {
	if (dir->vector_len < 0) {
	    panic("BROWSER PANIC",__FILE__,__LINE__,"clear_dir_vector",
		  "Bad vector len (non-null vector)",0);	
	}

	for (i = 0; i < dir->vector_len; i++) {
	    if (dir->vector[i].sys_name) {
		free(dir->vector[i].sys_name);
		dir->vector[i].sys_name = NULL;
	    }
	    if (dir->vector[i].disp_name) 
		free_string(&(dir->vector[i].disp_name));
	}
	free(dir->vector);
	dir->vector     = NULL;
	dir->vector_len = 0;
    } else {
	if (dir->vector_len > 0) {
	    panic("BROWSER PANIC",__FILE__,__LINE__,"clear_dir_vector",
		  "Bad vector len (null vector)",0);	
	}
	dir->vector_len = 0;
    }
}

/* WARNING: add_dir_vector does not allocate strings -- 
 *          it just assign pointers!
 */
void add_dir_vector(dir, sys_name, disp_name, flags)
     struct folder_browser *dir;
     char *sys_name;
     struct string * disp_name;
     int flags;
{
    DPRINT(Debug,12,(&Debug,
		     "add_dir_vector: dir=%p; type=%p\n",
		     dir,dir->type));

    if (dir->vector_len < 0) {
	DPRINT(Debug,12,(&Debug,
			 "add_dir_vector: RESETTING vector_len=%d to 0\n",
			 dir->vector_len));

	if (dir->vector) {
	    panic("BROWSER PANIC",__FILE__,__LINE__,"add_dir_vector",
		  "Bad vector len (non-null vector)",0);
	}
	dir->vector_len = 0;
    }


    DPRINT(Debug,12,(&Debug,    
		     "add_dir_vector: [%d].sys_name=%s, [%d].disp_name=%S, [%d].flags=%X%s\n",
		     dir->vector_len,sys_name,
		     dir->vector_len,disp_name,
		     dir->vector_len,flags,
		     flags & BROWSER_NEEDSTAT ? " (need stat)" : ""));

    dir->vector = safe_realloc (dir->vector,
				(dir->vector_len+1) * sizeof (dir->vector[0]));
    dir->vector[dir->vector_len].sys_name  = sys_name;
    dir->vector[dir->vector_len].disp_name = disp_name;
    dir->vector[dir->vector_len].flags     = flags;
    dir->vector[dir->vector_len].mtime     = 0;
    dir->vector_len++;
}


static void free_browser P_((struct folder_browser **dir));
static void free_browser(dir)
     struct folder_browser **dir;
{
    (*dir)->type->browser_free_it(*dir);
    clear_dir_vector(*dir);

    if ((*dir)->selection) 
	clear_dir_selection(*dir);
    
    if ((*dir)->dirname)
	free_string(&((*dir)->dirname));

    if ((*dir)->filter)
	free_string(&((*dir)->filter));

    if ((*dir)->sys_dir) {
	free((*dir)->sys_dir);
	(*dir)->sys_dir = NULL;
    }

    free(*dir);
    *dir = NULL;
}


void free_dir(dir)
     struct folder_browser **dir;
{

    if (!valid_browser((*dir)->type))
	panic("BROWSER PANIC",__FILE__,__LINE__,"free_dir",
	      "Bad browser (type)",0);
   
    DPRINT(Debug,10,(&Debug,
		"free_dir: *dir=%p (%s); type=%p\n", 
		*dir,(*dir)->sys_dir ? (*dir)->sys_dir : "<NULL>",
		(*dir)->type));
    free_browser(dir);
}

static struct folder_browser * browser_malloc2 P_((
	 enum selection_type     sel_type,
	 struct browser_type    *type
	 ));
static struct folder_browser * browser_malloc2(sel_type,type)
     enum selection_type     sel_type;
     struct browser_type    *type;
{
    struct folder_browser * dir = safe_malloc(sizeof (struct folder_browser));    

    bzero((void *)dir,sizeof (*dir));   /* defined hdrs/defs.h */

    dir->sys_dir       = NULL;
    dir->dirname       = NULL;
    dir->filter        = NULL;

    dir->sel_type      = sel_type;
    dir->vector        = NULL;
    dir->vector_len    = 0;
    dir->selection     = NULL;

    dir->type          = type;
    dir->type->browser_zero_it(dir);

    return dir;
}


static struct folder_browser * browser_malloc 
P_((enum selection_type  sel_type));

static struct folder_browser * browser_malloc(sel_type)
     enum selection_type  sel_type;
{
    return browser_malloc2(sel_type,&dummy_browser);
}

#ifdef REMOTE_MBX

uint16 browser_URL_default_port(schema)
     CONST struct browser_url_method  *schema;
{
    uint16 port;

    if (BROWSER_URL_method_magic != schema->magic)
	panic("BROWSER PANIC",__FILE__,__LINE__,"browser_URL_default_port",
	      "Bad schema magic",0);

    port = schema->url_default_port_it(schema);

    return port;
}

/* May not be 
      uin16 port
   because this is promoted to int
*/

struct folder_browser * browser_connect_URL(schema,user,password,host,port)
     CONST struct browser_url_method  *schema;
     CONST struct string              *user   /* May be NULL */;
     CONST struct string              *password /* May be NULL */;
     CONST char                       *host   /* May be NULL */;
     int                      port   /* May be NULL */;
{
    struct folder_browser * dir;
    struct connection_cache *con;

    if (BROWSER_URL_method_magic != schema->magic)
	panic("BROWSER PANIC",__FILE__,__LINE__,"browser_connect_URL",
	      "Bad schema magic",0);
    
    con = schema->url_make_it(schema,user,password,host,port);

    if (!con) {
	DPRINT(Debug,10,(&Debug,
			 "browser_connect_URL=NULL: url_make_it failed \n"));

	return NULL;
    }

    dir = browser_malloc2(selection_url,schema->type);

    browser_from_connection(con,dir);

    DPRINT(Debug,10,(&Debug,
		     "browser_connect_URL=%p\n",
		     dir));

    return dir;
}

/* Returns 1 on succeed */
/* This is always absolute path -- from root as defined for URL */
int select_item_from_URL(schema,browser,elems_count,elems)
     const struct browser_url_method  *schema;
     struct folder_browser *browser;
     int elems_count;
     const struct string **elems;
{
    int ret;

    if (BROWSER_URL_method_magic != schema->magic)
	panic("BROWSER PANIC",__FILE__,__LINE__,
	      "select_item_from_URL_directory",
	      "Bad schema magic",0);

    /* Force type to correct */
    change_type(browser,schema->type);

    ret = schema->url_select_item_from_it_URL(schema,browser,elems_count,elems);

    return ret;
}
#endif

struct folder_browser *new_browser(sel_type)
     enum selection_type  sel_type;
{
    struct folder_browser * dir;

    DPRINT(Debug,10,(&Debug,
		"new_browser: sel_type = %d\n",sel_type));

    dir = browser_malloc(sel_type);

    DPRINT(Debug,10,(&Debug,
		     "new_browser=%p type = %p\n",dir,dir->type));
    return dir;
}

static int change_type(dir,new_type)
     struct folder_browser *dir; 
     struct browser_type *new_type;
{

    if (!valid_browser(new_type))
	panic("BROWSER PANIC",__FILE__,__LINE__,"change_type",
	      "Bad new browser type",0);
    
    if (new_type == dir->type) 
	return 0;

    DPRINT(Debug,11,(&Debug,
		     "change_type: dir=%p Changing type from %p to %p\n",
		     dir,dir->type,new_type));
    
    dir->type->browser_free_it(dir);

    /* Assurance for clean start */
    bzero((void *)&(dir->a), sizeof (dir->a));

    dir->type = new_type;
    dir->type->browser_zero_it(dir);
    
    /* Be sure that caching request does not follow
       when browser type changes...
    */
    if (dir->vector_len < 0) {
       	DPRINT(Debug,12,(&Debug,
			 "change_type: RESETTING vector_len=%d to 0\n",
			 dir->vector_len));

	if (dir->vector) {
	    panic("BROWSER PANIC",__FILE__,__LINE__,"change_type",
		  "Bad vector len (non-null vector)",0);
	}

	dir->vector_len = 0;
    }

    return 1;
}



/* NOTE:
   'spec' given on tail_expander is assumed to be mailbox.
   
   Therefore 'user@hostname' as IMAP mailbox is interpreted
   as 'user@hostname:INBOX'

*/

static int tail_expander P_((struct folder_browser *dir,
			     struct string **buffer,
			     int pos, char *spec,
			     struct string **relative_to_type));
static int tail_expander(dir,buffer,pos,spec,relative_to_type)
     struct folder_browser *dir;
     struct string **buffer;
     int pos; 
     char *spec;
     struct string **relative_to_type;
{    
    int ret = 0;
    
    int code = 0;
    struct string * tempbuf = NULL;
    
#ifdef REMOTE_MBX
    struct remote_account X;
    struct service_entry *se = NULL;
    char *rest = NULL;
    
    DPRINT(Debug,15,(&Debug,
		     "tail_expander: Trying %s as remote address...\n",
		     spec));
    
    /* 'spec' is some folder ... */
    
    zero_remote_account(&X);

    /* -1 == name not found or bad syntax
        0 == not a remote address
	1 == name found 
    */
    if (0 != (code = split_remote_name(spec,&X,&se,&rest,
				       0 /* Both POP and IMAP are OK */
				       ))) {
	if (code < 0) {     /* ERROR */ 
	    free_temporary_service_entry(&se);
	    goto fail;	    
	}
	switch(dir->sel_type) {
	case selection_file:
	    /* Use dummy browser to store information ... (not supported) */

	    DPRINT(Debug,15,(&Debug,
			     "tail_expander: user=%s host=%s -- remote unsupported file\n",
			     X.username,X.host));
	    
	    if (change_type(dir,&dummy_browser))
		clear_dir_vector(dir);
	    
	    if (dir->a.dummy_browser.remote)
		free(dir->a.dummy_browser.remote);
	    dir->a.dummy_browser.remote = 
		elm_message(FRM("%s@%s"),X.username,X.host);

	    free_remote_account(&X);

	    if (rest) {
		switch(rest[0]) {
		case ':':
		    tempbuf = new_string2(system_charset,
					  s2us(rest+1));
		    break;
		case '/':
		    tempbuf = new_string2(system_charset,
					  s2us(rest));
		    break;
		default:
		    panic("BROWSER PANIC",__FILE__,__LINE__,
			  "tail_expander",
			  "Bad return from split_remote_name",0);
		}
	    } else		
		/* Empty directory assumed ... */
		tempbuf = new_string2(system_charset,s2us(""));

	    break;
	case selection_folder:

	    if (!rest && pos >= string_len(*buffer)) {
		/* Can be either IMAP or POP mailbox, use
		   dummy browser to store information 
		*/
		
		DPRINT(Debug,15,(&Debug,
				 "tail_expander: user=%s host=%s is either POP or IMAP\n",
				 X.username,X.host));

		if (change_type(dir,&dummy_browser))
		    clear_dir_vector(dir);
		
		if (dir->a.dummy_browser.remote)
		    free(dir->a.dummy_browser.remote);
		dir->a.dummy_browser.remote = 
		    elm_message(FRM("%s@%s"),X.username,X.host);
		
		free_remote_account(&X);
		
		*relative_to_type = NULL;
		
	    } else {
		/* Other case it must be IMAP connection ... */
		
		DPRINT(Debug,15,(&Debug,
				 "tail_expander: Trying user=%s host=%s as IMAP\n",
				 X.username,X.host));
		
		if (dir->type == &imap_browser && 
		    dir->a.imap_browser.Ch &&
		    dir->a.imap_browser.Ch->C.username &&
		    dir->a.imap_browser.Ch->C.host &&
		    0 == strcmp(dir->a.imap_browser.Ch->C.username,
				X.username) &&
		    0 == istrcmp(dir->a.imap_browser.Ch->C.host,
				 X.host)) {
		    DPRINT(Debug,15,(&Debug,
				     "tail_expander: already correct connection\n"));
		} else {
		    static struct connection_cache *CX;
		    PORTS ports_imaponly[] = { PORT_imap4, PORT_end };
		    int got;
		    
		    
		    /* Only IMAP connections are cached */
		    CX = locate_from_cache(X.username,X.host,&IMAP_connection,
					   0 /* Default port */);
		    if (CX) {
			if (change_type(dir,&imap_browser))
			    clear_dir_vector(dir);
			browser_from_connection(CX,dir);
			
		    } else if (connect_remote_account(&X,&got,
						      se,ports_imaponly,
						      PORT_end)) {
			if (change_type(dir,&imap_browser))
			    clear_dir_vector(dir);
			
			if (!join_connection(dir->a.imap_browser.Ch,&X,
					     CON_greeting)) {
			    free_remote_account(&X);
			    goto fail;
			}		
		    } else {
			free_remote_account(&X);
			free_temporary_service_entry(&se);
			goto fail;
		    }
		}
		
		/* Now we should have IMAP connection */
		
		if (rest) {
		    switch(rest[0]) {
		    case ':':
			tempbuf = new_string2(imap_charset,
					      s2us(rest+1));
			break;
		    case '/':
			tempbuf = new_string2(imap_charset,
					      s2us(rest));
			break;
		    default:
			panic("BROWSER PANIC",__FILE__,__LINE__,
			      "tail_expander",
			      "Bad return from split_remote_name",0);
		    }
		} else		
		    /* Sub directory of INBOX assumed */
		    tempbuf = new_string2(imap_charset,s2us("INBOX"));
	    }
	}	
    }  
    free_remote_account(&X);
    free_temporary_service_entry(&se);

#endif /* REMOTE_MBX */
    if (0 == code) {

	if (change_type(dir,
#if DIROPS
			&local_browser
#else
			&dummy_browser
#endif
			))
	    clear_dir_vector(dir);

	tempbuf = new_string2(local_fs_charset,s2us(spec));
    }

    if (pos < string_len(*buffer)) {
	char sep;
	int X = pos;
	struct string *XX;

	DPRINT(Debug,15,(&Debug,
		    "tail_expander: Changing directory for %s\n",
		    spec));

	/* Change first directory specified on 'spec' */
	if (!dir->type->browser_change_it(dir,tempbuf,&tempbuf))
	    goto fail;
	
	sep = dir->type->browser_separator_it(dir);

	if ('\0' == sep) {
	    struct string * msgname = dir->type->browser_name_it(dir);
	    
	    lib_error(CATGETS(elm_msg_cat, MeSet, MeFlatError,
			      "%S have no subdirectories"),
		      msgname);
	
	    free_string(&msgname);
	    goto fail;
	}

	XX = clip_from_string(*buffer,&X,string_len(*buffer));
	*relative_to_type = 
	    format_string(FRM("%S%c%S"),tempbuf,sep,XX);
	free_string(&XX);
    } else {         /* Directory given on 'spec' is current selection */
	*relative_to_type = tempbuf;
	tempbuf = NULL;
    }
    ret = 1;

 fail:

    if (tempbuf)
	free_string(&tempbuf);


    if (*relative_to_type) {
	DPRINT(Debug,12,(&Debug,
			 "tail_expander: *relative_to_type=%S\n",
			 *relative_to_type));
    } else {
       	DPRINT(Debug,12,(&Debug,
			 "tail_expander: *relative_to_type=NULL\n"));
    }
    DPRINT(Debug,12,(&Debug,
		     "tail_expander=%d\n",ret));

    return ret;
}



static int expander P_((struct folder_browser *dir,
			struct string **buffer,
			struct string **relative_to_type));
static int expander(dir,buffer,relative_to_type)
     struct folder_browser *dir; 
     struct string **buffer;
     struct string **relative_to_type;
{
    int ret = 0;
    int len;

    len = string_len(*buffer);
    *relative_to_type = NULL;

    if (len > 0) { 
	/* "." as last folder used,
	   "@alias" as default folder for user,
	   "="   as save by name and
	   "=?"  as conditionally save by name
	   must be implemented on src/file.c or src/savecopy.c
	   others we can implement on here ...

	   These include:
	   "."     as default directory
	   "~"     as home directory of current user
	   "~user" as home directory of given user
	   "="     as folder directory
	   "+"     as folder directory
	   "%"     as folder directory
	   "!"     as incoming mailbox
	   ">"     as received folder
	   "<"     as sent folder
	   "$"     as shell variable expansion (as local file)
	   "{rc}"  as .elm directory           (as local file)
	   "{lib}" as LIBHOME directory        (as local file)
	   "{etc}" as ETCHOME directory        (as local file)
	   "{doc}" as attachment-dir directory (as local file)
	*/

	uint16 code = give_unicode_from_string(*buffer,0);

	DPRINT(Debug,15,(&Debug,"expander: First char %04X\n",code));

	switch (code) {
	    int x, atpos,tailpos;

	case 0x002E: /* '.' */
	    if (1 == len) {   /* Default directory */
		if (change_type(dir,
#if DIROPS
				&local_browser
#else
				&dummy_browser
#endif
				))
		    clear_dir_vector(dir);
		*relative_to_type = dup_string(*buffer);
		ret = 1;
		break;
	    }
	    goto other;	 
	case 0x007E: /*  '~' */
	    if (1 == len) {   /* User's home directory */
		if (change_type(dir,
#if DIROPS
				&local_browser
#else
				&dummy_browser
#endif
				))
                    clear_dir_vector(dir);
		*relative_to_type = new_string2(local_fs_charset,
						s2us(home));
		ret = 1;
		break;
	    }

	    goto LOCAL_HANDLING;

	case 0x003D: /* '=' */
	case 0x002B: /* '+' */
	case 0x0025: /* '%' */
	    
	    /* Local folder directory */
	    if (1 == len) {   

		/* give_dt_estr_as_str adds / to end */
		char * folders_val = give_dt_estr_as_str(&folders_e,"maildir");

		if (!folders_val)
		    ret = 0;
		else {		    
		    if (change_type(dir,
#ifdef DIROPS
				    &local_browser
#else
				    &dummy_browser
#endif
				    ))
			clear_dir_vector(dir);
		    
		    *relative_to_type = new_string2(local_fs_charset,
						s2us(folders_val));
		    ret = 1;
		}
		break;
	    }

	    goto LOCAL_HANDLING;

	    /* NOTE:
	       
	       tail_expander assumes that 'spec' argument
	       (defaultfile, recvd_mail, sent_mail)
	       is mailbox. Therefore 'username@hostname'
	       is interpreted as 'username@hostname:INBOX'
	       for IMAP mailbox.
	       
	    */
	    
	case 0x0021: {
	    /* '!'      incoming mailbox */

	    char * default_val = give_dt_estr_as_str(&defaultfile_e,"incoming-mailbox");

	    if (!default_val)
		ret = 0;
	    else {
		ret = tail_expander(dir,buffer,1,default_val,
				    relative_to_type);
		if (ret && dir->sel_type == selection_file) {
		    if (*relative_to_type && 0 < string_len(*relative_to_type)) 
			lib_error(CATGETS(elm_msg_cat, MeSet, MeUnsafeFile1,
					  "Name %S (expands to %s, %S) is unsafe use as file"),
				  *buffer, default_val, *relative_to_type);
		    else
			lib_error(CATGETS(elm_msg_cat, MeSet, MeUnsafeFile2,
					  "Name %S (expands to %s) is unsafe use as file"),
				  *buffer, default_val);
		    ret = 0;
		}
	    }
	}
	    break;
	case 0x003E: { 
	    /* '>'      received folder  */ 

	    char * recvd_val = give_dt_estr_as_str(&recvd_mail_e,"receivedmail");

	    if (!recvd_val)
		ret = 0 ;
	    else {
		ret = tail_expander(dir,buffer,1,recvd_val,
				    relative_to_type);
		if (ret && dir->sel_type == selection_file) {
		    if (*relative_to_type && 0 < string_len(*relative_to_type)) 
			lib_error(CATGETS(elm_msg_cat, MeSet, MeUnsafeFile1,
					  "Name %S (expands to %s, %S) is unsafe use as file"),
				  *buffer, recvd_val, *relative_to_type);
		    else
			lib_error(CATGETS(elm_msg_cat, MeSet, MeUnsafeFile2,
					  "Name %S (expands to %s) is unsafe use as file"),
				  *buffer, recvd_val);
		    ret = 0;
		}
	    }
	}
	    break;
	case 0x003C: {
	    /*  '<'     sent folder     */

	    char * sent_val = give_dt_estr_as_str(&sent_mail_e,"sentmail");
	    
	    if (!sent_val)
		ret = 0 ;
	    else {
		
		ret = tail_expander(dir,buffer,1,sent_val,
				    relative_to_type);
		if (ret && dir->sel_type == selection_file) {
		    if (*relative_to_type && 0 < string_len(*relative_to_type)) 
			lib_error(CATGETS(elm_msg_cat, MeSet, MeUnsafeFile1,
					  "Name %S (expands to %s, %S) is unsafe use as file"),
				  *buffer, sent_val, *relative_to_type);
		    else
			lib_error(CATGETS(elm_msg_cat, MeSet, MeUnsafeFile2,
					  "Name %S (expands to %s) is unsafe use as file"),
				  *buffer, sent_val);
		    ret = 0;
		}
	    }
	}
	    break;
	    
	default:
	other:

	    /* Look if it starts with user@hostname */
	    atpos = -1;
	    tailpos = len;

	    for (x = 0; x < len; x++) {
		uint16 code1 = give_unicode_from_string(*buffer,x);

		if (0x0040 /* '@' */   == code1)
		    atpos = x;
		else if (0x003A /* ':' */ == code1) {
		    tailpos = x+1;
		    break;
		} else if (0x002F /* '/' */ == code1) {
		    tailpos = x;
		    break;
		}
	    }

	    if (-1 != atpos) {
		if (0 == atpos || atpos+1 == x) {
		    lib_error(CATGETS(elm_msg_cat, MeSet,MeBadRemoteMailbox1,
				      "Bad remote mailbox: %S"),
			      *buffer);
		    break;
		}
		
		if (x >= len) {
		    struct string * Lstr;

		    /* Can be either IMAP or POP mailbox, use
		       dummy browser to store information 
		    */

		    if (change_type(dir,&dummy_browser))
			clear_dir_vector(dir);
	    
		    if (dir->a.dummy_browser.remote)
			free(dir->a.dummy_browser.remote);
		    
		    Lstr = convert_string(system_charset,*buffer,0);
		    dir->a.dummy_browser.remote = 
			us2s(stream_from_string(Lstr,0,NULL));
		    free_string(&Lstr);

		    switch(dir->sel_type) {
		    case selection_file:
			DPRINT(Debug,15,(&Debug,
					 "expander: %s is remote unsupported directory\n",
					 dir->a.dummy_browser.remote));
			break;
		    case selection_folder:
			DPRINT(Debug,15,(&Debug,
					 "expander: %s is either POP or IMAP\n",
					 dir->a.dummy_browser.remote));
			break;
		    }
		    ret = 1;
		    
		} else {
#ifdef REMOTE_MBX
		    struct remote_account X;
		    struct service_entry *se = NULL;
		    char *rest = NULL;   /* Ignored -- need NOT to be free'ed
					  * 'rest' is pointer to area of 'str'
					  */

		    int ret_code;

		    struct string * Lstr = 
			convert_string(system_charset,*buffer,0);
		    unsigned char * str = 
			stream_from_string(Lstr,0,NULL);
		    
		    /* Other case it must be IMAP connection ... */
		    
		    zero_remote_account(&X);

		    DPRINT(Debug,15,(&Debug,
				     "expander: Trying %s as remote address...\n",
				     str));
		    
		    /* -1 == name not found or bad syntax
		       0 == not a remote address
		       1 == name found 
		    */
		    if (0 <= (ret_code = split_remote_name(us2s(str),
							   &X,&se,&rest,
							   STFLAG_is_imap
							   /* Wanna IMAP */
							   ))) {

			if (ret_code == 0)
			    panic("BROWSER PANIC",__FILE__,__LINE__,"expander",
				  "Bad return from split_remote_name",0);

			switch(dir->sel_type) {
			case selection_file:
			    DPRINT(Debug,15,(&Debug,
					     "expander: user=%s host=%s -- remote unsupported file\n",
					     X.username,X.host));
			    
			    break;
			case selection_folder:
			    DPRINT(Debug,15,(&Debug,
					     "expander: Trying user=%s host=%s as IMAP\n",
					     X.username,X.host));

			    if (dir->type == &imap_browser && 
				dir->a.imap_browser.Ch &&
				dir->a.imap_browser.Ch->C.username &&
				dir->a.imap_browser.Ch->C.host &&
				0 == strcmp(dir->a.imap_browser.Ch->C.username,
					    X.username) &&
				0 == istrcmp(dir->a.imap_browser.Ch->C.host,
					     X.host)) {
				int X1 = tailpos;
				
				DPRINT(Debug,15,(&Debug,
						 "expander: already correct connection\n"));
				*relative_to_type = 
				    clip_from_string(*buffer,&X1,len);
				ret = 1;
			    } else {
				static struct connection_cache *CX;
				PORTS ports_imaponly[] = { PORT_imap4, 
							   PORT_end };
				int got;
			    
				
				/* Only IMAP connections are cached */
				CX = locate_from_cache(X.username,X.host,
						       &IMAP_connection,
						       0 /* Default port */);
				if (CX) {
				    int X1 = tailpos;
				    
				    if (change_type(dir,&imap_browser))
					clear_dir_vector(dir);
				    browser_from_connection(CX,dir);
				    ret = 1;
				    
				    *relative_to_type = 
					clip_from_string(*buffer,&X1,len);
				} else if (connect_remote_account(&X,&got,
								  se,
								  ports_imaponly,
								  PORT_end)) {
				    int X1 = tailpos;
				    
				    if (change_type(dir,&imap_browser))
					clear_dir_vector(dir);
				    
				    ret = join_connection(dir->a.imap_browser.Ch,
							  &X,CON_greeting);
				    if (ret)
					*relative_to_type = 
					    clip_from_string(*buffer,&X1,len);
				}
			    }
			    break;
			}
		    }
		    free_string(&Lstr);
		    free(str);				    
		    free_remote_account(&X);		       
		    free_temporary_service_entry(&se);
#endif /* REMOTE_MBX */

		    if (!ret)
			lib_error(CATGETS(elm_msg_cat, MeSet, 
					  MeRemoteDirNotAvail1,
					  "Remote directries are not available in %.*S"),
				  x,*buffer);
		    
		}
	    } else {
		char expand_space[LONG_STRING];
		unsigned char * str;
		struct string * Lstr;

		/* Local mailbox handling */
		
		DPRINT(Debug,15,(&Debug,
				 "expander: does not start with 'user@hostname' atpos=%d, scan ended = %d (len=%d)\n",atpos,x,len));

		/* FALLTHRU */
	    case 0x0024: /* '$'  shell variable expansion (as local file) */
	    case 0x007B: /* '{'  {rc}  as .elm directory  (as local file)
			  *      {lib} as LIBHOME directory (as local file) 
			  *      {etc} as ETCHOME directory (as local file) 
			  *      {doc} as attachment-dir    (as local file) 
			  */

	    LOCAL_HANDLING:  /* goto label ... */
	    
	        Lstr = convert_string(local_fs_charset,*buffer,0);
	        str = stream_from_string(Lstr,0,NULL);

	       	DPRINT(Debug,15,(&Debug,
				 "expander: Expanding %s (%S) as local\n",
				 str,Lstr));
		
		if (expand_meta(expand_space,us2s(str),
				sizeof expand_space) >= 0) {
		    if (change_type(dir,
#ifdef DIROPS
				    &local_browser
#else
				    &dummy_browser
#endif
				    ))
			clear_dir_vector(dir);
		    *relative_to_type = new_string2(local_fs_charset,
						    s2us(expand_space));
		    ret = 1;
		}
		free_string(&Lstr);
		free(str);		
	    }
	    break;
	}
    }

    if (*relative_to_type) {
	DPRINT(Debug,12,(&Debug,
			 "expander: *relative_to_type=%S\n",
			 *relative_to_type));
    } else {
       	DPRINT(Debug,12,(&Debug,
			 "expander: *relative_to_type=NULL\n"));
    }
    DPRINT(Debug,12,(&Debug,"expander=%d\n",ret));

    return ret;
}
    

/* Returns 1 if succesfully, and may canonify argument
   may change type of dir
 */
int change_dir(dir,buffer)
     struct folder_browser *dir; 
     struct string **buffer;
{
    int ret = 0;

    if (!valid_browser(dir->type))
	panic("BROWSER PANIC",__FILE__,__LINE__,"change_dir",
	      "Bad browser (type)",0);

    DPRINT(Debug,10,(&Debug,
		     "change_dir: dir=%p (%s); type=%p", 
		     dir,dir->sys_dir ? dir->sys_dir : "<NULL>",
		     dir->type));

    if (*buffer) {
	if (!verify_string(*buffer)) {
	    panic("BROWSER PANIC",__FILE__,__LINE__,"change_dir",
		  "Bad buffer (string)",0);
	}
	DPRINT(Debug,10,(&Debug,", *buffer=%S\n",*buffer));

    } else {
	DPRINT(Debug,10,(&Debug,", *buffer=NULL\n"));
    }
	
    if (dir->filter)
	free_string(&(dir->filter));

    if (*buffer &&
	string_len(*buffer) > 0) {
	struct string *relative_to_type = NULL;

	if (expander(dir,buffer,&relative_to_type))
	    ret = dir->type->browser_change_it(dir,relative_to_type,buffer);

	if (relative_to_type)
	    free_string(&relative_to_type);

    } else {
	if (change_type(dir,&dummy_browser))
	    clear_dir_vector(dir);
	ret = dir->type->browser_change_it(dir,*buffer,buffer);
    }
	
    DPRINT(Debug,10,(&Debug,
		     "change_dir=%d; dir=%p (%s); type=%p", 
		     ret,
		     dir,dir->sys_dir ? dir->sys_dir : "<NULL>",
		     dir->type));
    if (*buffer) {
	DPRINT(Debug,10,(&Debug,", *buffer=%S\n",*buffer));
    } else {
       	DPRINT(Debug,10,(&Debug,", *buffer=NULL\n"));
    }

    return ret;
}

int change_dir_to_entry(dir,entry,buffer)
     struct folder_browser *dir;
     int entry;
     struct string **buffer;
{
    int ret = 0;
     
    if (!valid_browser(dir->type))
	panic("BROWSER PANIC",__FILE__,__LINE__,"change_dir_to_entry",
	      "Bad browser (type)",0);

    DPRINT(Debug,10,(&Debug,
		"change_dir_to_entry: dir=%p (%s); type=%p, entry=%d\n", 
		dir,dir->sys_dir ? dir->sys_dir : "<NULL>",
		dir->type,
	        entry));

    if (*buffer) {
	if (!verify_string(*buffer)) {
	    panic("BROWSER PANIC",__FILE__,__LINE__,"change_dir_to_entry",
		  "Bad buffer (string)",0);
	}

	DPRINT(Debug,10,(&Debug,
			 "change_dir_to_entry- *buffer=%p\n",*buffer));

    } else {
       	DPRINT(Debug,10,(&Debug,
			 "change_dir_to_entry- *buffer=NULL\n"));
    }

    if (dir->filter)
	free_string(&(dir->filter));
    
    /* browser_vector_len() will update cache */

    if (entry < 0 || entry >= browser_vector_len(dir)) {
	DPRINT(Debug,10,(&Debug,
			 "change_dir_to_entry: entry (%d) not in range (0-%d)\n",
			 entry,dir->vector_len-1));

	ret = 0;
    } else if (dir->dirname) { 
	ret = dir->type->browser_change_v_it(dir,&(dir->vector[entry]),buffer);
    } else {
	/* SPECIAL HANDLING FOR DEFAULT DIRECTORY */
	struct string *relative_to_type = NULL;

	if (*buffer)
	    free_string(buffer);
	*buffer = dup_string(dir->vector[entry].disp_name);
	
	if (expander(dir,buffer,&relative_to_type))
	    ret = dir->type->browser_change_it(dir,relative_to_type,buffer);
	
	if (relative_to_type)
	    free_string(&relative_to_type);	       
    }

    if (*buffer) {
	DPRINT(Debug,10,(&Debug,
			 "change_dir_to_entry=%d; *buffer=%S\n",
			 ret,*buffer));
    } else {
	DPRINT(Debug,10,(&Debug,
			 "change_dir_to_entry=%d; *buffer=NULL\n",ret));
    }
    return ret;
}

extern int change_dir_up(dir,buffer)
     struct folder_browser *dir;
     struct string **buffer;
{
   int ret = 0;
     
    if (!valid_browser(dir->type))
	panic("BROWSER PANIC",__FILE__,__LINE__,"change_dir_up",
	      "Bad browser (type)",0);

    DPRINT(Debug,10,(&Debug,
		     "change_dir_up: dir=%p (%s); type=%p\n", 
		     dir,dir->sys_dir ? dir->sys_dir : "<NULL>",
		     dir->type
		     ));
    
    if (*buffer) {
	if (!verify_string(*buffer)) {
	    panic("BROWSER PANIC",__FILE__,__LINE__,"change_dir_up",
		  "Bad buffer (string)",0);
	}

	DPRINT(Debug,10,(&Debug,
			 "change_dir_up- *buffer=%S\n",*buffer));
    } else {
       	DPRINT(Debug,10,(&Debug,
			 "change_dir_up- *buffer=NULL\n"));
    }

    if (dir->filter)
	free_string(&(dir->filter));

    if (dir->dirname) {  
	/* Not possible to go up from default directory / menu */

	ret = dir->type->browser_change_up_it(dir,buffer);
	if (ret < 0) {
	    DPRINT(Debug,10,(&Debug,
			     "change_dir_up: Returning to default menu...\n"));

	    if (change_type(dir,
			    &dummy_browser
			    ))
		clear_dir_vector(dir);
	    browser_gen_default_menu(dir);
	    if (*buffer)
		free_string(buffer);
	    ret = 1;
	} else if (ret > 0) {
	    /* Do actual change ... */
	    ret = change_dir(dir,buffer);
	}
    } 

    if (*buffer) {
	DPRINT(Debug,10,(&Debug,
			 "change_dir_up=%d; *buffer=%S\n",ret,*buffer));
    } else {
	DPRINT(Debug,10,(&Debug,
			 "change_dir_up=%d; *buffer=NULL\n",ret));
    }
    return ret;
}

struct string * give_title_dir(dir,entry_count)
     struct folder_browser *dir;
     int *entry_count;
{
    struct string * ret;

    if (!valid_browser(dir->type))
	panic("BROWSER PANIC",__FILE__,__LINE__,"give_title_dir",
	      "Bad browser (type)",0);

    DPRINT(Debug,10,(&Debug,
		     "give_title_dir: dir=%p (%s); type=%p\n", 
		     dir,dir->sys_dir ? dir->sys_dir : "<NULL>",
		     dir->type));

    /* browser_vector_len() will update cache */

    ret = dir->type->browser_give_title_it(dir);
    *entry_count = browser_vector_len(dir);;

    DPRINT(Debug,10,(&Debug,
		     "give_title_dir=%S  *entry_count=%d\n",
		     ret,*entry_count));
    return ret;
}


void  folder_sort_dir(dir,print)
     struct folder_browser *dir;
     print_sort_message * print;
{
    struct string * ret;

    if (!valid_browser(dir->type))
	panic("BROWSER PANIC",__FILE__,__LINE__,"folder_sort_dir",
	      "Bad browser (type)",0);

    DPRINT(Debug,10,(&Debug,
		     "folder_sort_dir: dir=%p (%s); type=%p\n", 
		     dir,dir->sys_dir ? dir->sys_dir : "<NULL>",
		     dir->type));

    /* browser_vector_len() will update cache */
    browser_vector_len(dir);

    dir->type->browser_folder_sort_it(dir,print);
}


/* return reference to array -- do not free_string() result !!! */
struct string * give_line_dir(dir,idx,flags)
     struct folder_browser *dir;
     int idx;
     int *flags;
{
    struct string * ret;

    if (!valid_browser(dir->type))
	panic("BROWSER PANIC",__FILE__,__LINE__,"give_line_dir",
	      "Bad browser (type)",0);
    
    DPRINT(Debug,10,(&Debug,
		     "give_line_dir: dir=%p (%s); type=%p, idx=%d\n", 
		     dir,dir->sys_dir ? dir->sys_dir : "<NULL>",
		     dir->type,
		     idx));


    /* browser_vector_len() will update cache */

    if (idx < 0 || idx >= browser_vector_len(dir))
	panic("BROWSER PANIC",__FILE__,__LINE__,"give_line_dir",
	      "Bad directory list index",0);

    ret    = dir->vector[idx].disp_name;

    if (dir->vector[idx].flags & BROWSER_NEEDSTAT) {
	DPRINT(Debug,10,(&Debug,
			 "give_line_dir: Updating flags -- doing stat\n"));
	dir->type->browser_stat_routine(dir,idx);
    }

    *flags = dir->vector[idx].flags;
    
    DPRINT(Debug,10,(&Debug,
		     "give_line_dir=%S; *flags=%X\n",ret,*flags));
    return ret;
}

int select_dir_item(dir,buffer)
     struct folder_browser *dir;
     struct string **buffer;
{
    int ret = 0;
    int len;

    if (!valid_browser(dir->type))
	panic("BROWSER PANIC",__FILE__,__LINE__,"select_dir_item",
	      "Bad browser (type)",0);

    DPRINT(Debug,10,(&Debug,
		     "select_dir_item: dir=%p (%s); type=%p\n", 
		     dir,dir->sys_dir ? dir->sys_dir : "<NULL>",
		     dir->type));

    if (!verify_string(*buffer)) {
	panic("BROWSER PANIC",__FILE__,__LINE__,"select_dir_item",
	      "Bad buffer (string)",0);
    }
    
    len = string_len(*buffer);
    if (len > 0) {
	struct string *relative_to_type = NULL;

	if (expander(dir,buffer,&relative_to_type))
	    ret = dir->type->browser_select_it(dir,relative_to_type,buffer);

	if (relative_to_type)
	    free_string(&relative_to_type);

    } else {
	if (change_type(dir,&dummy_browser))
	    clear_dir_vector(dir);
	ret = dir->type->browser_select_it(dir,*buffer,buffer);
    }

    DPRINT(Debug,10,(&Debug,
		     "select_dir_item=%d; dir=%p (%s); type=%p, *buffer=%p\n", 
		     ret,
		     dir,dir->sys_dir ? dir->sys_dir : "<NULL>",
		     dir->type,
		     *buffer));
    return ret;
}

int give_edit_buffer(dir,entry,buffer,fill_with_entry)
     struct folder_browser *dir;
     int entry; 
     struct string **buffer;
     int fill_with_entry;
{
    int ret = 1;
    struct string *res = NULL;

    if (!valid_browser(dir->type))
	panic("BROWSER PANIC",__FILE__,__LINE__,"give_edit_buffer",
	      "Bad browser (type)",0);

    DPRINT(Debug,10,(&Debug,
		     "give_edit_buffer: dir=%p (%s); type=%p, entry=%d, (fill)=%d\n", 
		     dir,dir->sys_dir ? dir->sys_dir : "<NULL>",
		     dir->type,
		     entry,fill_with_entry));

    if (*buffer) {	
	if (!verify_string(*buffer)) {
	    panic("BROWSER PANIC",__FILE__,__LINE__,"give_edit_buffer",
		  "Bad buffer (string)",0);
	}

	DPRINT(Debug,10,(&Debug,
			 "give_edit_buffer- *buffer=%p\n",*buffer));
    } else {
	DPRINT(Debug,10,(&Debug,
			 "give_edit_buffer- *buffer=NULL\n"));
    }

    /* NOTE: dir->sys_dir does not include possible user@host
     *  but  dir->dirname includes possible user@host !
     *
     *  dir->dirname is when default directory (menu) is selected
     *
     *  browser_vector_len() will update cache 
     */
    
    if (entry < 0 || 
	entry >= browser_vector_len(dir)) {

	DPRINT(Debug,10,(&Debug,
			 "give_edit_buffer: entry (%d) not in range (0-%d)\n",
			 entry,dir->vector_len-1));
	
	fill_with_entry = 0;
	ret = 0;
    }
	
    
    if (fill_with_entry) 
	res = dir->type->browser_cat_it(dir,
					dir->vector[entry].disp_name);
    else 
	res = dir->type->browser_cat_it(dir,NULL);
    
    if (res) {
	if (*buffer)
	    free_string(buffer);
	*buffer = res;
    } else {
	DPRINT(Debug,10,(&Debug,
			 "give_edit_buffer: failed -- NO CHANGE OF BUFFER!\n"));
	ret = 0;
    }
    
    if (*buffer) {
	DPRINT(Debug,10,(&Debug,
			 "give_edit_buffer=%d; *buffer=%S\n",ret,*buffer));
    } else {
	DPRINT(Debug,10,(&Debug,
			 "give_edit_buffer=%d; *buffer=NULL\n",ret));
    }
    return ret;
}


static void filter_director P_((struct folder_browser *dir));
static void filter_director(dir)
     struct folder_browser *dir;
{
    int i;
    DPRINT(Debug,12,(&Debug,
		     "filter_director: dir=%p; type=%p\n",
		     dir,dir->type));

    for (i = 0; i < dir->vector_len; i++) {
	if (!string_match(dir->vector[i].disp_name,
			  dir->filter)) {
	    int j;

	    if (dir->vector[i].sys_name) {
		free(dir->vector[i].sys_name);
		dir->vector[i].sys_name = NULL;
	    }
	    if (dir->vector[i].disp_name) 
		free_string(&(dir->vector[i].disp_name));
	    
	    for (j = i+1; j < dir->vector_len; j++) {
		dir->vector[j-1] = dir->vector[j];
		dir->vector[j].sys_name = NULL;
		dir->vector[j].disp_name = NULL;
	    }
	    dir->vector_len--;
	    i--;
	}
    }
}

int dir_is_wildcard(dir,buffer)
     struct folder_browser *dir;
     struct string **buffer;
{
    int ret = 0;

    if (!valid_browser(dir->type))
	panic("BROWSER PANIC",__FILE__,__LINE__,"dir_is_wildcard",
	      "Bad browser (type)",0);

    DPRINT(Debug,10,(&Debug,
		     "dir_is_wildcard: dir=%p (%s); type=%p\n", 
		     dir,dir->sys_dir ? dir->sys_dir : "<NULL>",
		     dir->type));

    if (dir->filter)
	free_string(&(dir->filter));

    if (*buffer) {
	int len,x;
	struct string *relative_to_type = NULL;
	
	if (!verify_string(*buffer)) {
	    panic("BROWSER PANIC",__FILE__,__LINE__,"dir_is_wildcard",
		  "Bad buffer (string)",0);
	}

	DPRINT(Debug,10,(&Debug,
		    "dir_is_wildcard- *buffer=%p\n",*buffer));

	len = string_len(*buffer);

	for (x = 0; x < len; x++) {
	    uint16 code1 = give_unicode_from_string(*buffer,x);

	    if (0x002A /* '*' */ == code1) {
		ret = 1;
		break;
	    } else if (0x003F /* '?' */ == code1) {
		ret = 1;
		break;
	    }
	}

	if (!ret) {
	    DPRINT(Debug,10,(&Debug,
			     "dir_is_wildcard: No wildcards\n"));
	    goto fail;
	}

	if (expander(dir,buffer,&relative_to_type)) {
	    int base_len = 0;
	    int sep = dir->type->browser_separator_it(dir);
	    int X = 0;
	    struct string * new_relative = NULL;
	    struct string * dispname = NULL;
	    int disp_len = 0;
	    int X2 = 0;
	    int len1 = 0;

	    if (relative_to_type) {		
		int x1;

		len1 = string_len(relative_to_type);

		for (x1 = 0; x1 < len1; x1++) {
		    uint16 code1 = 
			give_unicode_from_string(relative_to_type,x1);
		    
		    if (0x002A /* '*' */ == code1) {
			break;
		    } else if (0x003F /* '?' */ == code1) {
			break;
		    
		    } else if (sep && sep == code1) {
			/* (separator) character set is assumed to be 
			   ASCII compatible on here! */
			base_len = x1+1;
		    }
		}

		if (0 == base_len && 
		    (dir->type == &dummy_browser
#if DIROPS
		     || dir->type == &local_browser
#endif
		     )) { 
		    /* Special case: 
		       Treat adas*hjghj as filtering of current
		       directory  (and not filtering of default menu)
		    */
		   
		    new_relative = new_string2(system_charset,s2us("."));
		    dispname = new_string2(system_charset,s2us("."));
		    
		    if (change_type(dir,
#if DIROPS
				    &local_browser
#else
				    &dummy_browser
#endif
				    ))
			clear_dir_vector(dir);
		    
		} else {
		    for (x = 0; x < len; x++) {
			uint16 code1 = give_unicode_from_string(*buffer,x);
			
			if (0x002A /* '*' */ == code1) {
			    break;
			} else if (0x003F /* '?' */ == code1) {
			    break;
			} else if (sep && sep == code1) {
			    /* (separator) character set is assumed to be 
			       ASCII compatible on here! */
			    disp_len = x+1;
			}
		    }
		    
		    new_relative = clip_from_string(relative_to_type,&X,
						    base_len);
		    
		    if (0 == disp_len) { 
			/*  HACK for =* */
			dispname = dup_string(new_relative);
		    } else
			dispname = clip_from_string(*buffer,&X2,disp_len);
		}
		
	    } else {
		lib_error(CATGETS(elm_msg_cat, MeSet, MeBadWildcard,
				  "Bad wildcard specification %S"),
			  *buffer);
		
		/* new_relative == NULL, but that is OK */

		dispname = dup_string(*buffer);
	    }
	    
	    if (dir->type->browser_change_it(dir,new_relative,&dispname)) {

		dir->filter =  clip_from_string(relative_to_type,&X,len1);
		
		if (dir->vector_len >= 0)
		    filter_director(dir);

	    }

	    if (new_relative)
		free_string(&new_relative);
	    free_string(&dispname);
	}

	if (relative_to_type)
	    free_string(&relative_to_type);

      

    } else {
	DPRINT(Debug,10,(&Debug,
			 "dir_is_wildcard- *buffer=NULL\n"));
    }

 fail:
    if (*buffer) {
	DPRINT(Debug,10,(&Debug,
			 "dir_is_wildcard=%d; *buffer=%S\n",ret,*buffer));
    } else {
	DPRINT(Debug,10,(&Debug,
			 "dir_is_wildcard=%d; *buffer=NULL\n",ret));
    }

    return ret;
}


struct folder_info * folder_from_dir_item(dir)
     struct folder_browser *dir;
{
    struct folder_info * res = NULL;

    if (!valid_browser(dir->type))
	panic("BROWSER PANIC",__FILE__,__LINE__,"folder_from_dir_item",
	      "Bad browser (type)",0);

    DPRINT(Debug,10,(&Debug,
		     "folder_from_dir_item: dir=%p (%s); type=%p\n", 
		     dir,dir->sys_dir ? dir->sys_dir : "<NULL>",
		     dir->type));

    if (!dir->selection) 
	panic("BROWSER PANIC",__FILE__,__LINE__,"folder_from_dir_item",
	      "No selection",0);

    res = dir->type->browser_folder_from_it(dir);

    if (res) {
	DPRINT(Debug,10,(&Debug,
			 "folder_from_dir_item=%p (%s), type=%p\n",
			 res,res->cur_folder_sys,res->folder_type));
    } else {
	DPRINT(Debug,10,(&Debug,"folder_from_dir_item=NULL\n"));
    }

    return res;
}

int give_dir_flags(dir) 
    struct folder_browser *dir;
{
    int ret = 0;

    if (!valid_browser(dir->type))
	panic("BROWSER PANIC",__FILE__,__LINE__,"give_dir_flags",
	      "Bad browser (type)",0);

    DPRINT(Debug,10,(&Debug,
		     "folder_dir_flags: dir=%p (%s); type=%p\n", 
		     dir,dir->sys_dir ? dir->sys_dir : "<NULL>",
		     dir->type));

    if (dir->selection) 
	ret |= BROWSER_SELECTED | dir->selection->flags;

    DPRINT(Debug,10,(&Debug, "folder_dir_flags=%X\n",ret));
    return ret;
}

int create_selection_dir(dir)
     struct folder_browser *dir;
{
    int res = 0;

    if (!valid_browser(dir->type))
	panic("BROWSER PANIC",__FILE__,__LINE__,"create_selection_dir",
	      "Bad browser (type)",0);
    
    DPRINT(Debug,10,(&Debug,
		     "create_selection_dir: dir=%p (%s); type=%p\n", 
		     dir,dir->sys_dir ? dir->sys_dir : "<NULL>",
		     dir->type));

    if (!dir->selection) 
	panic("BROWSER PANIC",__FILE__,__LINE__,"create_selection_dir",
	      "No selection",0);

    res = dir->type->browser_create_selection_it(dir);
    
    DPRINT(Debug,10,(&Debug,"create_selection_dir=%d\n",res));

    return res;
}

static void malloc_keep_browser_write_state P_((WRITE_STATE * ptr));
static void malloc_keep_browser_write_state(ptr)
     WRITE_STATE * ptr;
{
    (*ptr) = safe_malloc(sizeof (struct browser_write_state));

    /* bzero is defined hdrs/defs.h */
    bzero((void *)*ptr,sizeof (struct browser_write_state));

    (*ptr)->magic             = WS_magic;
}

static void free_keep_browser_write_state P_((WRITE_STATE * ptr));
static void free_keep_browser_write_state(ptr)
     WRITE_STATE * ptr;
{
    if (*ptr) {
	/* bzero is defined hdrs/defs.h */
	bzero((void *)*ptr,sizeof (struct browser_write_state));

	free(*ptr);
	*ptr = NULL;
    }	    
}


int prepare_write_folder(dir,write_state_ptr)
     struct folder_browser *dir;
     WRITE_STATE * write_state_ptr;
{
    int ret = 0;
    WRITE_STATE ptr = NULL;

    if (!valid_browser(dir->type))
	panic("BROWSER PANIC",__FILE__,__LINE__,"prepare_write_folder",
	      "Bad browser (type)",0);
    
    DPRINT(Debug,10,(&Debug,
		     "prepare_write_folder: dir=%p (%s); type=%p\n", 
		     dir,dir->sys_dir ? dir->sys_dir : "<NULL>",
		     dir->type));

    if (!dir->selection) 
	panic("BROWSER PANIC",__FILE__,__LINE__,"prepare_write_folder",
	      "No selection",0);

    malloc_keep_browser_write_state(&ptr);
    dir->type->zero_ws_fields_it(ptr);

    ret = dir->type->browser_prepare_write_it(dir,ptr);

    if (!ret) {
	dir->type->free_ws_fields_it(ptr);
	free_keep_browser_write_state(&ptr);
    }

    *write_state_ptr = ptr;

    DPRINT(Debug,10,(&Debug,"prepare_write_folder=%d\n",ret));
    return ret;
}

int sync_write_folder(dir,write_state_ptr)
     struct folder_browser *dir;
     WRITE_STATE write_state_ptr;
{
    int ret = 0;

    if (!valid_browser(dir->type))
	panic("BROWSER PANIC",__FILE__,__LINE__,"sync_write_folder",
	      "Bad browser (type)",0);

    if (write_state_ptr->magic  != WS_magic)
	panic("BROWSER PANIC",__FILE__,__LINE__,"sync_write_folder",
	      "Bad magic",0);
    
    DPRINT(Debug,10,(&Debug,
		     "sync_write_folder: dir=%p (%s); type=%p\n", 
		     dir,dir->sys_dir ? dir->sys_dir : "<NULL>",
		     dir->type));

    ret = dir->type->browser_sync_write_it(dir,write_state_ptr);


    DPRINT(Debug,10,(&Debug,"sync_write_folder=%d\n",ret));

    return ret;   
}

int end_write_folder(dir,write_state_ptr)
     struct folder_browser *dir;
     WRITE_STATE * write_state_ptr;
{
    int ret = 0;
    WRITE_STATE ptr = *write_state_ptr;
    
    if (!valid_browser(dir->type))
	panic("BROWSER PANIC",__FILE__,__LINE__,"end_write_folder",
	      "Bad browser (type)",0);
    if (ptr->magic  != WS_magic)
	panic("BROWSER PANIC",__FILE__,__LINE__,"end_write_folder",
	      "Bad magic",0);

    DPRINT(Debug,10,(&Debug,
		     "end_write_folder: dir=%p (%s); type=%p\n", 
		     dir,dir->sys_dir ? dir->sys_dir : "<NULL>",
		     dir->type));

    ret = dir->type->browser_end_write_it(dir,ptr);
    dir->type->free_ws_fields_it(ptr);
    free_keep_browser_write_state(&ptr);

    *write_state_ptr = ptr;

    DPRINT(Debug,10,(&Debug,"end_write_folder=%d\n",ret));
    return ret;   
}

struct string * selection_name_dir(dir)
     struct folder_browser *dir;
{
    struct string * ret = NULL;

    if (!valid_browser(dir->type))
	panic("BROWSER PANIC",__FILE__,__LINE__,"selection_name_dir",
	      "Bad browser (type)",0);

    DPRINT(Debug,10,(&Debug,
		     "selection_name_dir: dir=%p (%s); type=%p\n", 
		     dir,dir->sys_dir ? dir->sys_dir : "<NULL>",
		     dir->type));
    
    if (!dir->selection) 
	panic("BROWSER PANIC",__FILE__,__LINE__,"selection_name_dir",
	      "No selection",0);

    ret = dir->type->browser_cat_it(dir,dir->selection->disp_name);

    if (ret) {
	DPRINT(Debug,10,(&Debug,
			 "selection_name_dir=%S\n",ret));
    } else {
	DPRINT(Debug,10,(&Debug,
			 "selection_name_dir=NULL\n"));
    }
    return ret;
}

void clear_selection_dir(dir)
     struct folder_browser *dir;
{
    if (!valid_browser(dir->type))
	panic("BROWSER PANIC",__FILE__,__LINE__,"clear_selection_dir",
	      "Bad browser (type)",0);

    DPRINT(Debug,10,(&Debug,
		     "clear_selection_dir: dir=%p (%s); type=%p\n", 
		     dir,dir->sys_dir ? dir->sys_dir : "<NULL>",
		     dir->type));

    clear_dir_selection(dir);
}

/* helper routines for fileio.c */
void start_fd_write_state(fd,dir,write_state_ptr)
     FILE *fd;
     struct folder_browser **dir;
     WRITE_STATE *write_state_ptr;
{
    WRITE_STATE ptr = NULL;
    
    DPRINT(Debug,10,(&Debug,"start_fd_write_state: fd=%p\n",fd));

    *dir = browser_malloc(selection_folder);
    
    malloc_keep_browser_write_state(&ptr);
    (*dir)->type->zero_ws_fields_it(ptr);

    ptr->a.local.save_file = fd;   /* WARNING: shared pointer! */

    *write_state_ptr = ptr;

    DPRINT(Debug,10,(&Debug,
		     "start_fd_write_state: *dir=%p; type=%p\n",
		     *dir,(*dir)->type));		
}

void end_fd_write_state(dir,write_state_ptr)
     struct folder_browser **dir;
     WRITE_STATE *write_state_ptr;
{
    WRITE_STATE ptr = *write_state_ptr;

    if (!valid_browser((*dir)->type))
	panic("BROWSER PANIC",__FILE__,__LINE__,"end_fd_write_state",
	      "Bad browser (type)",0);
    if (ptr->magic  != WS_magic)
	panic("BROWSER PANIC",__FILE__,__LINE__,"end_fd_write_state",
	      "Bad magic",0);

    DPRINT(Debug,10,(&Debug,
		     "end_fd_write_state: *dir=%p; type=%p\n", 
		     *dir,(*dir)->type));

    ptr->a.local.save_file = NULL;   /* Reset shared pointer! */

    (*dir)->type->free_ws_fields_it(ptr);
    free_keep_browser_write_state(&ptr);

    *write_state_ptr = ptr;

    free_browser(dir);
}

/* Returns -1 if not seekable, else position */
long tell_dir_write_state(dir,write_state_ptr)
     struct folder_browser *dir;
     WRITE_STATE write_state_ptr;
{
    long ret = -1;

    if (!valid_browser(dir->type))
	panic("BROWSER PANIC",__FILE__,__LINE__,"tell_dir_write_state",
	      "Bad browser (type)",0);
    if (write_state_ptr->magic  != WS_magic)
	panic("BROWSER PANIC",__FILE__,__LINE__,"tell_dir_write_state",
	      "Bad magic",0);

    DPRINT(Debug,10,(&Debug,
		     "tell_dir_write_state: *dir=%p; type=%p\n", 
		     dir,dir->type));

    ret = dir->type->browser_tell_it_ws(dir,write_state_ptr);

    DPRINT(Debug,10,(&Debug,"tell_dir_write_state=%ld\n",ret));
    return ret;
}

/* Returns 0 on failure! */
int seek_dir_write_state(dir,write_state_ptr,pos)
     struct folder_browser *dir;
     WRITE_STATE write_state_ptr;
     long pos;
{
    int ret = 0;

    if (!valid_browser(dir->type))
	panic("BROWSER PANIC",__FILE__,__LINE__,"seek_dir_write_state",
	      "Bad browser (type)",0);
    if (write_state_ptr->magic  != WS_magic)
	panic("BROWSER PANIC",__FILE__,__LINE__,"seek_dir_write_state",
	      "Bad magic",0);

    DPRINT(Debug,10,(&Debug,
		     "seek_dir_write_state: *dir=%p; type=%p, pos=%ld\n", 
		     dir,dir->type,pos));

    ret = dir->type->browser_seek_it_ws(dir,write_state_ptr,pos);

    DPRINT(Debug,10,(&Debug,"seek_dir_write_state=%d\n",ret));
    return ret;
}

/* Return 0 on failure */
int write_dir_write_state(dir,write_state_ptr,l,buffer)
     struct folder_browser *dir;
     WRITE_STATE write_state_ptr;
     int l; 
     CONST char *buffer;
{    
    int ret = 0;
	
    if (!valid_browser(dir->type))
	panic("BROWSER PANIC",__FILE__,__LINE__,"write_dir_write_state",
	      "Bad browser (type)",0);
    if (write_state_ptr->magic  != WS_magic)
	panic("BROWSER PANIC",__FILE__,__LINE__,"write_dir_write_state",
	      "Bad magic",0);

    DPRINT(Debug,10,(&Debug,
		     "write_dir_write_state: *dir=%p; type=%p, len=%d\n", 
		     dir,dir->type,l));

    ret = dir->type->browser_write_it_ws(dir,write_state_ptr,l,buffer);

    DPRINT(Debug,10,(&Debug,"write_dir_write_state=%d\n",ret));
    return ret;
}

int write_envelope_start(dir,write_state_ptr,write_envelope,current_header,
			 env_flags)
     struct folder_browser *dir;
     WRITE_STATE write_state_ptr;
     int write_envelope;
     struct header_rec *current_header;
     int *env_flags;
{
    int ret = 0;

    if (!valid_browser(dir->type))
	panic("BROWSER PANIC",__FILE__,__LINE__,"write_envelope_start",
	      "Bad browser (type)",0);
    if (write_state_ptr->magic  != WS_magic)
	panic("BROWSER PANIC",__FILE__,__LINE__,"write_envelope_start",
	      "Bad magic",0);

    DPRINT(Debug,10,(&Debug,
		     "write_write_envelope_start: *dir=%p; type=%p, write_envelope=%d\n", 
		     dir,dir->type,write_envelope));

    ret = dir->type->browser_start_we_it(dir,write_state_ptr,
					 write_envelope,current_header,
					 env_flags);

    DPRINT(Debug,10,(&Debug,
		     "write_write_envelope_start=%d    *env_flags=%d%s\n",
		     ret,*env_flags,
		     (*env_flags & WE_ADD_RETURN_PATH) ? 
		     " WE_ADD_RETURN_PATH" : ""));

    return ret;
}

int write_envelope_end(dir,write_state_ptr,write_envelope,current_header)
     struct folder_browser *dir;
     WRITE_STATE write_state_ptr;
     int write_envelope;
     struct header_rec *current_header;
{
    int ret = 0;

    if (!valid_browser(dir->type))
	panic("BROWSER PANIC",__FILE__,__LINE__,"write_envelope_end",
	      "Bad browser (type)",0);
    if (write_state_ptr->magic  != WS_magic)
	panic("BROWSER PANIC",__FILE__,__LINE__,"write_envelope_end",
	      "Bad magic",0);
    
    DPRINT(Debug,10,(&Debug,
		     "write_write_envelope_end: *dir=%p; type=%p, write_envelope=%d\n", 
		     dir,dir->type,write_envelope));

    ret = dir->type->browser_end_we_it(dir,write_state_ptr,
				       write_envelope,current_header);

    DPRINT(Debug,10,(&Debug,"write_write_envelope_end=%d\n",ret));

    return ret;
}

int selection_is_folder(dir,folder)
     struct folder_browser *dir;
     struct folder_info *folder;
{
    int ret = 0;

    if (!valid_browser(dir->type))
	panic("BROWSER PANIC",__FILE__,__LINE__,"selection_is_folder",
	      "Bad browser (type)",0);

    if (!dir->selection) 
	panic("BROWSER PANIC",__FILE__,__LINE__,"selection_is_folder",
	      "No selection",0);

    DPRINT(Debug,10,(&Debug,
		     "selection_is_folder: *dir=%p; type=%p, folder=%p\n", 
		     dir,dir->type,folder));

    ret = dir->type->browser_selection_is_it(dir,folder);

    DPRINT(Debug,10,(&Debug,"selection_is_folder=%d\n",ret));

    return ret;
}

int dir_make_ref(dir,refname,iscopy,is_text) 
     struct folder_browser *dir;
     char **refname; 
     int *iscopy;
     int is_text;
{
    int ret = 0;

    if (!valid_browser(dir->type))
	panic("BROWSER PANIC",__FILE__,__LINE__,"dir_make_ref",
	      "Bad browser (type)",0);

    if (!dir->selection) 
	panic("BROWSER PANIC",__FILE__,__LINE__,"dir_make_ref",
	      "No selection",0);

    DPRINT(Debug,10,(&Debug,
		     "dir_make_ref: *dir=%p; type=%p, is_text=%d\n", 
		     dir,dir->type,is_text));

    *refname = NULL;
    *iscopy  = 0;

    ret = dir->type->browser_make_ref_it(dir,refname,iscopy,is_text);

    DPRINT(Debug,10,(&Debug,"dir_make_ref=%d;   *refname=%s, *iscopy=%d\n",
		     ret,
		     *refname ? *refname : "<NULL>",
		     *iscopy));
    return ret;
}


int browser_vector_len(dir)
     struct folder_browser *dir;
{
    if (!valid_browser(dir->type))
	panic("BROWSER PANIC",__FILE__,__LINE__,"browser_vector_len",
	      "Bad browser (type)",0);

    if (dir->vector_len >= 0) {
	
	DPRINT(Debug,12,(&Debug,
			 "browser_vector_len=%d, dir=%p\n",
			 dir->vector_len,dir));
	return dir->vector_len;
    }
    DPRINT(Debug,12,(&Debug,
		     "browser_vector_len: dir=%p   (updating cache)\n",
		     dir));
    dir->type->browser_update_it(dir);

    if (dir->filter)
	filter_director(dir);

    DPRINT(Debug,12,(&Debug,
		     "browser_vector_len=%d\n",
		     dir->vector_len));

    return dir->vector_len;
}

/*
 * Local Variables:
 *  mode:c
 *  c-basic-offset:4
 *  buffer-file-coding-system: iso-8859-1
 * End:
 */


syntax highlighted by Code2HTML, v. 0.9.1