#line 2 "osdep/dosgen"		/* So compiler knows orig name of this file.*/
/*
 * $Id: dosgen,v 1.36 2004/05/05 22:01:21 hubert Exp $
 *
 * Program:	Operating system dependent routines - MS DOS Generic
 *
 *
 * Michael Seibel
 * Networks and Distributed Computing
 * Computing and Communications
 * University of Washington
 * Administration Builiding, AG-44
 * Seattle, Washington, 98195, USA
 * Internet: mikes@cac.washington.edu
 *
 * Please address all bugs and comments to "pine-bugs@cac.washington.edu"
 *
 *
 * Pine and Pico are registered trademarks of the University of Washington.
 * No commercial use of these trademarks may be made without prior written
 * permission of the University of Washington.
 * 
 * Pine, Pico, and Pilot software and its included text are Copyright
 * 1989-2001 by the University of Washington.
 * 
 * The full text of our legal notices is contained in the file called
 * CPYRIGHT, included with this distribution.
 *
 *
 * Notes:
 *     - This file should contain the cross section of functions useful
 *       in both DOS and Windows ports of pico.
 *
 */


/*
 * picosigs - Install any handlers for the signals we're interested
 *	      in catching.
 */
void
picosigs()
{
    /* no op */
}


/*
 * Useful definitions...
 */
#ifdef	MOUSE
static int mexist = 0;			/* is the mouse driver installed? */
static int nbuttons;			/* number of buttons on the mouse */
static unsigned mnoop;
#endif
static unsigned char okinfname[32] = {
      0,    0, 			/* ^@ - ^G, ^H - ^O  */
      0,    0,			/* ^P - ^W, ^X - ^_  */
      0x80, 0x17,		/* SP - ' ,  ( - /   */
      0xff, 0xe4,		/*  0 - 7 ,  8 - ?   */
      0x7f, 0xff,		/*  @ - G ,  H - O   */
      0xff, 0xe9,		/*  P - W ,  X - _   */
      0x7f, 0xff,		/*  ` - g ,  h - o   */
      0xff, 0xf6,		/*  p - w ,  x - DEL */
      0,    0, 			/*  > DEL   */
      0,    0,			/*  > DEL   */
      0,    0, 			/*  > DEL   */
      0,    0, 			/*  > DEL   */
      0,    0 			/*  > DEL   */
};


/*
 * fallowc - returns TRUE if c is allowable in filenames, FALSE otw
 */
fallowc(c)
int c;
{
    return(okinfname[c>>3] & 0x80>>(c&7));
}


#ifdef	MOUSE
/*
 * end_mouse - a no-op on DOS/Windows
 */
void
end_mouse()
{
}


/*
 * mouseexist - function to let outsiders know if mouse is turned on
 *              or not.
 */
mouseexist()
{
    return(mexist);
}
#endif	/* MOUSE */


/*
 * fexist - returns TRUE if the file exists, FALSE otherwise
 */
fexist(file, m, l)
char  *file, *m;
off_t *l;
{
    struct stat	sbuf;

    if(l != NULL)
      *l = (off_t)0;

    if(stat(file, &sbuf) < 0){
	if(errno == ENOENT)			/* File not found */
	  return(FIOFNF);
	else
	  return(FIOERR);
    }

    if(l != NULL)
      *l = (off_t)sbuf.st_size;

    if(sbuf.st_mode & S_IFDIR)
      return(FIODIR);
    else if(*m == 't')				/* no links, just say yes */
      return(FIOSUC);

    if(m[0] == 'r')				/* read access? */
      return((S_IREAD & sbuf.st_mode) ? FIOSUC : FIONRD);
    else if(m[0] == 'w')			/* write access? */
      return((S_IWRITE & sbuf.st_mode) ? FIOSUC : FIONWT);
    else if(m[0] == 'x')			/* execute access? */
      return((S_IEXEC & sbuf.st_mode) ? FIOSUC : FIONEX);
    return(FIOERR);				/* what? */
}


/*
 * isdir - returns true if fn is a readable directory, false otherwise
 *         silent on errors (we'll let someone else notice the problem;)).
 */
isdir(fn, l, d)
char *fn;
long *l;
time_t *d;
{
    struct stat sbuf;

    if(l)
      *l = 0;

    if(stat(fn, &sbuf) < 0)
      return(0);

    if(l)
      *l = sbuf.st_size;

    if(d)
      *d = sbuf.st_mtime;

    return(sbuf.st_mode & S_IFDIR);
}


/*
 * gethomedir - returns the users home directory
 *              Note: home is malloc'd for life of pico
 */
char *gethomedir(l)
int *l;
{
    static char *home = NULL;
    static short hlen = 0;

    if(home == NULL){
	char buf[NLINE], *p;
	if(Pmaster && Pmaster->home_dir)
	  p = Pmaster->home_dir;
	else {
	    sprintf(buf, "%c:\\", _getdrive() + 'A' - 1);
	    p = buf;
	}
	hlen = strlen(p);
	if((home=(char *)malloc(((size_t)hlen + 1) * sizeof(char))) == NULL){
	    emlwrite("Problem allocating space for home dir", NULL);
	    return(0);
	}
	strcpy(home, p);
    }

    if(l)
      *l = hlen;

    return(home);
}


/*
 * homeless - returns true if given file does not reside in the current
 *            user's home directory tree. 
 */
homeless(f)
char *f;
{
    char *home;
    int   len;

    home = gethomedir(&len);
    return(strncmp(home, f, len));
}


/*
 * errstr - return system error string corresponding to given errno
 *          Note: strerror() is not provided on all systems, so it's 
 *          done here once and for all.
 */
char *errstr(err)
int err;
{
    return((err >= 0 && err < sys_nerr) ? sys_errlist[err] : NULL);
}


/*
 * getfnames - return all file names in the given directory in a single 
 *             malloc'd string.  n contains the number of names
 */
char *getfnames(dn, pat, n, e)
char *dn, *pat, *e;
int  *n;
{
    int status;
    long l;
    size_t avail, alloced, incr = 1024;
    char *names, *np, *p;
    char buf[NLINE];
    struct stat sbuf;
#ifdef	WIN32
    struct _finddata_t dbuf;
    long   findrv;
#else
    struct find_t dbuf;					/* opened directory */
#endif

    *n = 0;

    if(stat(dn, &sbuf) < 0){
	if(e)
	  sprintf(e, "\007Dir \"%s\": %s", dn, strerror(errno));

	return(NULL);
    } 
    else{
#define MAX(x,y)        ((x) > (y) ? (x) : (y))
	avail = alloced = MAX(sbuf.st_size, incr);
	if(!(sbuf.st_mode & S_IFDIR)){
	    if(e)
	      sprintf(e, "\007Not a directory: \"%s\"", dn);

	    return(NULL);
	}
    }

    if((names=(char *)malloc(alloced * sizeof(char))) == NULL){
	if(e)
	  sprintf(e, "\007Can't malloc space for file names");

	return(NULL);
    }

    np = names;

    strcpy(buf, dn);
    sprintf(buf, "%s%s%s*%s%s", dn,
	    (dn[strlen(dn)-1] == '\\') ? "" : "\\",
	    (pat && *pat) ? pat : "",
	    (pat && *pat && strchr(pat, '.')) ? "" : ".",
	    (pat && *pat && strchr(pat, '.')) ? "" : "*");
#ifdef	WIN32
    if((findrv = _findfirst(buf, &dbuf)) < 0){
#else
    if(_dos_findfirst(buf, _A_NORMAL|_A_SUBDIR, &dbuf) != 0){
#endif
	if(e)
	  sprintf(e, "Can't find first file in \"%s\"", dn);

	free((char *) names);
	return(NULL);
    }

    do{
	(*n)++;
	p = dbuf.name;
	l = strlen(p);
	while(avail < l+1){
	    char *oldnames;

	    alloced += incr;
	    avail += incr;
	    oldnames = names;
	    if((names = (char *)realloc((void *)names, alloced * sizeof(char)))
		== NULL){
		if(e)
		  sprintf(e, "\007Can't malloc enough space for file names");

		return(NULL);
	    }

	    np = names + (np-oldnames);
	}

	avail -= (l+1);

	while((*np++ = *p++) != '\0')
	  ;
    }
#ifdef	WIN32
    while(_findnext(findrv, &dbuf) == 0);
    _findclose(findrv);
#else
    while(_dos_findnext(&dbuf) == 0);
#endif

    return(names);
}


/*
 * fioperr - given the error number and file name, display error
 */
void
fioperr(e, f)
int  e;
char *f;
{
    switch(e){
      case FIOFNF:				/* File not found */
	emlwrite("\007File \"%s\" not found", f);
	break;
      case FIOEOF:				/* end of file */
	emlwrite("\007End of file \"%s\" reached", f);
	break;
      case FIOLNG:				/* name too long */
	emlwrite("\007File name \"%s\" too long", f);
	break;
      case FIODIR:				/* file is a directory */
	emlwrite("\007File \"%s\" is a directory", f);
	break;
      case FIONWT:
	emlwrite("\007Write permission denied: %s", f);
	break;
      case FIONRD:
	emlwrite("\007Read permission denied: %s", f);
	break;
      case FIONEX:
	emlwrite("\007Execute permission denied: %s", f);
	break;
      default:
	emlwrite("\007File I/O error: %s", f);
    }
}


/*
 * pfnexpand - pico's function to expand the given file name if there is 
 *	       a leading '~'
 */
char *pfnexpand(fn, len)
    char  *fn;
    size_t len;
{
    register char *x, *y, *z;
    char *home = NULL;
    char name[_MAX_PATH];
    
    if(*fn == '~' && *(x = fn + 1) == '\\') {
	if(!(home = (char *) getenv("HOME"))
	   && getenv("HOMEDRIVE") && getenv("HOMEPATH"))
	  sprintf(home = name, "%s%s",
		  (char *) getenv("HOMEDRIVE"), (char *) getenv("HOMEPATH"));

	if(home && strlen(home)+strlen(x)<len){
	    /* make room for expanded path */
	    for(z = x + strlen(x), y = fn + strlen(x) + strlen(home);
		z >= x;
		*y-- = *z--)
	      ;

	    /* and insert the expanded address */
	    for(x = fn, y = home; *y != '\0'; *x++ = *y++)
	      ;
	}
    }

    return(fn);
}


/*
 * fixpath - make the given pathname into an absolute path
 */
void
fixpath(name, len)
    char  *name;
    size_t len;
{
    char file[_MAX_PATH];
    int  dr;

    if(!len)
      return;
    
    /* return the full path of given file, so drive spec? */
    if(name[1] == ':' && isalpha((unsigned char) name[0])){
	if(name[2] != '\\'){				  /* including path? */
	    dr = toupper((unsigned char)name[0]) - 'A' + 1;
	    if(_getdcwd(dr, file, _MAX_PATH) != NULL){
		if(file[strlen(file)-1] != '\\')
		  strncat(file, "\\", sizeof(file)-1-strlen(file));

		/* add file name */
		strncat(file, &name[2], sizeof(file)-1-strlen(file));
	    }
	    else
	      return;
	}
	else
	  return;		/* fully qualified with drive and path! */
    }
    else if(name[0] == '\\' && name[1] != '\\') {     /* no drive spec! */
	sprintf(file, "%c:%.*s", _getdrive()+'A'-1, len-3, name);
    }
    else if(name[0] == '\\' && name[1] == '\\'){  /* Windows network drive */
      return;
    }
    else{
	if(Pmaster && !(gmode & MDCURDIR)){
	  strncpy(file, ((gmode & MDTREE) || opertree[0])
			  ? opertree : gethomedir(NULL), sizeof(file)-1);
	  file[sizeof(file)-1] = '\0';
	}
	else if(!_getcwd(file, sizeof(file)))	/* no qualification */
	  return;

	if(*name){				/* if name, append it */
	    if(*file && file[strlen(file)-1] != '\\')
	      strncat(file, "\\", sizeof(file)-1-strlen(file));

	    strncat(file, name, sizeof(file)-1-strlen(file));
	}
    }

    strncpy(name, file, len-1);			/* copy back to real buffer */
    name[len-1] = '\0';				/* tie off just in case */
}


/*
 * compresspath - given a base path and an additional directory, collapse
 *                ".." and "." elements and return absolute path (appending
 *                base if necessary).  
 *
 *                returns  1 if OK, 
 *                         0 if there's a problem
 *                         new path, by side effect, if things went OK
 */
compresspath(base, path, len)
char *base, *path;
int  len;
{
    register int i;
    int  depth = 0;
    char *p;
    char *stack[32];
    char  pathbuf[NLINE];

#define PUSHD(X)  (stack[depth++] = X)
#define POPD()    ((depth > 0) ? stack[--depth] : "")

    strcpy(pathbuf, path);
    fixpath(pathbuf, len);

    p = pathbuf;
    for(i=0; pathbuf[i] != '\0'; i++){		/* pass thru path name */
	if(pathbuf[i] == C_FILESEP){
	    if(p != pathbuf)
	      PUSHD(p);				/* push dir entry */
	    p = &pathbuf[i+1];			/* advance p */
	    pathbuf[i] = '\0';			/* cap old p off */
	    continue;
	}

	if(pathbuf[i] == '.'){			/* special cases! */
	    if(pathbuf[i+1] == '.'			/* parent */
	       && (pathbuf[i+2] == C_FILESEP || pathbuf[i+2] == '\0')){
		if(!strcmp(POPD(),""))		/* bad news! */
		  return(0);

		i += 2;
		p = (pathbuf[i] == '\0') ? "" : &pathbuf[i+1];
	    }
	    else if(pathbuf[i+1] == C_FILESEP || pathbuf[i+1] == '\0'){
		i++;
		p = (pathbuf[i] == '\0') ? "" : &pathbuf[i+1];
	    }
	}
    }

    if(*p != '\0')
      PUSHD(p);					/* get last element */

    path[0] = '\0';
    for(i = 0; i < depth; i++){
	strcat(path, S_FILESEP);
	strcat(path, stack[i]);
    }

    return(1);					/* everything's ok */
}


/*
 * This routine is derived from BSD4.3 code,
 * Copyright (c) 1987 Regents of the University of California.
 * All rights reserved.
 */
#if defined(LIBC_SCCS) && !defined(lint)
static char sccsid[] = "@(#)mktemp.c	5.7 (Berkeley) 6/27/88";
#endif /* LIBC_SCCS and not lint */

static char *
was_nonexistent_tmp_name(as, create_it, ext)
    char *as;
    int   create_it;
    char *ext;
{
    register char *start, *trv;
    char           treplace;
    struct stat    sbuf;
    unsigned       pid;
    static unsigned n = 0;
    int            fd, tries = 0;

    pid = ((unsigned)getpid() * 100) + n++;

    /* extra X's get set to 0's */
    for(trv = as; *trv; ++trv)
      ;

    /*
     * We should probably make the name random instead of having it
     * be the pid.
     */
    while(*--trv == 'X'){
	*trv = (pid % 10) + '0';
	pid /= 10;
    }

    /* add the extension, enough room guaranteed by caller */
    if(ext){
	strcat(as, ".");
	strcat(as, ext);
    }

    /*
     * Check for write permission on target directory; if you have
     * six X's and you can't write the directory, this will run for
     * a *very* long time.
     */
    for(start = ++trv; trv > as && *trv != '\\'; --trv)
      ;

    if(*trv == '\\'){
        if((trv - as == 2) && isalpha(as[0]) && as[1] == ':')
	  trv++;
	treplace = *trv;
	*trv = '\0';
	if(stat(as==trv ? "\\" : as, &sbuf) || !(sbuf.st_mode & S_IFDIR))
	  return((char *)NULL);

	*trv = treplace;
    }
    else if (stat(".", &sbuf) == -1)
      return((char *)NULL);

    for(;;){
	if(stat(as, &sbuf)){		/* stat failed */
	    if(errno == ENOENT){		/* no such file, success */
		/*
		 * If create_it is set, create the file so that the
		 * evil ones don't have a chance to put something there
		 * that they can read or write before we create it
		 * ourselves.
		 */
		if(!create_it ||
		   ((fd=open(as, O_CREAT|O_EXCL|O_WRONLY,0600)) >= 0
		    && close(fd) == 0))
		  return(as);
		else if(++tries > 3)		/* open failed unexpectedly */
		  return((char *)NULL);
	    }
	    else				/* failed for unknown reason */
	      return((char *)NULL);
	}

	for(trv = start;;){
	    if(!*trv)
	      return((char *)NULL);

	    /*
	     * Change the digits from the initial values into
	     * lower case letters and try again.
	     */
	    if(*trv == 'z')
	      *trv++ = 'a';
	    else{
		if(isdigit((unsigned char)*trv))
		  *trv = 'a';
		else
		  ++*trv;

		break;
	    }
	}
    }
    /*NOTREACHED*/
}


/*
 * This routine is derived from BSD4.3 code,
 * Copyright (c) 1988 Regents of the University of California.
 * All rights reserved.
 */
#if defined(LIBC_SCCS) && !defined(lint)
static char sccsid[] = "@(#)tmpnam.c	4.5 (Berkeley) 6/27/88";
#endif /* LIBC_SCCS and not lint */
/*----------------------------------------------------------------------
      Return a unique file name in a given directory.  This is not quite
      the same as the usual tempnam() function, though it is similar.
      We want it to use the TMP/TMPDIR environment variable only if dir
      is NULL, instead of using it regardless if it is set.
      We also want it to be safer than tempnam().
      If we return a filename, we are saying that the file did not exist
      at the time this function was called (and it wasn't a symlink pointing
      to a file that didn't exist, either).
      If dir is NULL this is a temp file in a public directory. In that
      case we create the file with permission 0600 before returning.

  Args: dir      -- The directory to create the name in
        prefix   -- Prefix of the name
 
 Result: Malloc'd string equal to new name is returned.  It must be free'd
	 by the caller.  Returns the string on success and NULL on failure.
  ----*/
char *
temp_nam(dir, prefix)
    char *dir, *prefix;
{
    struct stat buf;
    size_t      l, ll;
    char       *f, *name;

    if(!(name = (char *)malloc((unsigned int)NFILEN)))
        return((char *)NULL);

    if(!dir && (f = getenv("TMPDIR")) && !stat(f, &buf) &&
                         (buf.st_mode&S_IFMT) == S_IFDIR &&
			 !can_access(f, WRITE_ACCESS)){
	strncpy(name, f, NFILEN-1);
	name[NFILEN-1] = '\0';
        goto done;
    }

    if(!dir && (f = getenv("TMP")) && !stat(f, &buf) &&
                         (buf.st_mode&S_IFMT) == S_IFDIR &&
			 !can_access(f, WRITE_ACCESS)){
	strncpy(name, f, NFILEN-1);
	name[NFILEN-1] = '\0';
        goto done;
    }

    if(!dir && (f = getenv("TEMP")) && !stat(f, &buf) &&
                         (buf.st_mode&S_IFMT) == S_IFDIR &&
			 !can_access(f, WRITE_ACCESS)){
	strncpy(name, f, NFILEN-1);
	name[NFILEN-1] = '\0';
        goto done;
    }

    if(dir){
	strncpy(name, dir, NFILEN-1);
	name[NFILEN-1] = '\0';
	if(!*dir || (isalpha(*dir) && *(dir+1) == ':' && !*(dir+2)))
	  strcat(name, "\\");

	if((!stat(name, &buf) && (buf.st_mode&S_IFMT) == S_IFDIR)
		    && !can_access(name, WRITE_ACCESS)){
	    strncpy(name, dir, NFILEN-1);
	    name[NFILEN-1] = '\0';
            goto done;
	}
    }

#ifndef P_tmpdir
#define	P_tmpdir	"\\tmp"
#endif
    if(!stat(P_tmpdir, &buf) &&
                         (buf.st_mode&S_IFMT) == S_IFDIR &&
			 !can_access(P_tmpdir, WRITE_ACCESS)){
	strncpy(name, P_tmpdir, NFILEN-1);
	name[NFILEN-1] = '\0';
        goto done;
    }

    free(name);
    return((char *)NULL);

done:
    if(name[0] && *((f = &name[l=strlen(name)]) - 1) != '\\' && l+1 < NFILEN){
	*f++ = '\\';
	*f = '\0';
	l++;
    }

    if(prefix && (ll = strlen(prefix)) && l+ll < NFILEN){
	strcpy(f, prefix);
	f += ll;
	l += ll;
    }

    if(l+6 < NFILEN)
      strcpy(f, "XXXXXX");
    else{
	free(name);
	return((char *)NULL);
    }

    return(was_nonexistent_tmp_name(name, 1, NULL));
}


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

     Like temp_nam but create a unique name with an extension.
 
 Result: Malloc'd string equal to new name is returned.  It must be free'd
	 by the caller.  Returns the string on success and NULL on failure.
  ----*/
char *
temp_nam_ext(dir, prefix, ext)
    char *dir, *prefix, *ext;
{
    struct stat buf;
    size_t      l, ll;
    char       *f, *name;

    if(ext == NULL || *ext == '\0')
      return(temp_nam(dir, prefix));

    if(!(name = (char *)malloc((unsigned int)NFILEN)))
        return((char *)NULL);

    if(!dir && (f = getenv("TMPDIR")) && !stat(f, &buf) &&
                         (buf.st_mode&S_IFMT) == S_IFDIR &&
			 !can_access(f, WRITE_ACCESS)){
	strncpy(name, f, NFILEN-1);
	name[NFILEN-1] = '\0';
        goto done;
    }

    if(!dir && (f = getenv("TMP")) && !stat(f, &buf) &&
                         (buf.st_mode&S_IFMT) == S_IFDIR &&
			 !can_access(f, WRITE_ACCESS)){
	strncpy(name, f, NFILEN-1);
	name[NFILEN-1] = '\0';
        goto done;
    }

    if(!dir && (f = getenv("TEMP")) && !stat(f, &buf) &&
                         (buf.st_mode&S_IFMT) == S_IFDIR &&
			 !can_access(f, WRITE_ACCESS)){
	strncpy(name, f, NFILEN-1);
	name[NFILEN-1] = '\0';
        goto done;
    }

    if(dir){
	strncpy(name, dir, NFILEN-1);
	name[NFILEN-1] = '\0';
	if(!*dir || (isalpha(*dir) && *(dir+1) == ':' && !*(dir+2)))
	  strcat(name, "\\");

	if((!stat(name, &buf) && (buf.st_mode&S_IFMT) == S_IFDIR)
		    && !can_access(name, WRITE_ACCESS)){
	    strncpy(name, dir, NFILEN-1);
	    name[NFILEN-1] = '\0';
            goto done;
	}
    }

#ifndef P_tmpdir
#define	P_tmpdir	"\\tmp"
#endif
    if(!stat(P_tmpdir, &buf) &&
                         (buf.st_mode&S_IFMT) == S_IFDIR &&
			 !can_access(P_tmpdir, WRITE_ACCESS)){
	strncpy(name, P_tmpdir, NFILEN-1);
	name[NFILEN-1] = '\0';
        goto done;
    }

    free(name);
    return((char *)NULL);

done:
    if(name[0] && *((f = &name[l=strlen(name)]) - 1) != '\\' && l+1 < NFILEN){
	*f++ = '\\';
	*f = '\0';
	l++;
    }

    if(prefix && (ll = strlen(prefix)) && l+ll < NFILEN){
	strcpy(f, prefix);
	f += ll;
	l += ll;
    }

    if(l+6+strlen(ext)+1 < NFILEN)
      strcpy(f, "XXXXXX");
    else{
	free(name);
	return((char *)NULL);
    }

    return(was_nonexistent_tmp_name(name, 1, ext));
}


/*
 * tmpname - return a temporary file name in the given buffer, the filename
 * is in the directory dir unless dir is NULL
 */
void
tmpname(dir, name)
char *dir;
char *name;
{
    char  tmp[_MAX_PATH];
    char *t;

    if(!((dir && *dir) ||
	 (dir = getenv("TMPDIR")) ||
	 (dir = getenv("TMP")) ||
	 (dir = getenv("TEMP"))))
      if(!(getcwd(dir = tmp, _MAX_PATH)
	   && fexist(dir, "w", (off_t *) NULL) == FIOSUC))
	dir = "c:\\";
      
    if(t = temp_nam_ext(dir, "ae", "txt")){
	strncpy(name, t, NFILEN-1);
	name[NFILEN-1] = '\0';
	free(t);
    }
    else{
	emlwrite("Unable to construct temp file name", NULL);
	name[0] = '\0';
    }
}


/*
 * Take a file name, and from it
 * fabricate a buffer name. This routine knows
 * about the syntax of file names on the target system.
 * I suppose that this information could be put in
 * a better place than a line of code.
 */
void
makename(bname, fname)
char    bname[];
char    fname[];
{
        register char   *cp1;
        register char   *cp2;

        cp1 = &fname[0];
        while (*cp1 != 0)
                ++cp1;

        while (cp1!=&fname[0] && cp1[-1]!='\\')
                --cp1;
        cp2 = &bname[0];
        while (cp2!=&bname[NBUFN-1] && *cp1!=0 && *cp1!=';')
                *cp2++ = *cp1++;
        *cp2 = 0;
}


/*
 * copy - copy contents of file 'a' into a file named 'b'.  Return error
 *        if either isn't accessible or is a directory
 */
copy(a, b)
char *a, *b;
{
    int    in, out, n, rv = 0;
    char   *cb;
    struct stat tsb, fsb;

    if(stat(a, &fsb) < 0){		/* get source file info */
	emlwrite("Can't Copy: %s", errstr(errno));
	return(-1);
    }

    if(!(fsb.st_mode&S_IREAD)){		/* can we read it? */
	emlwrite("\007Read permission denied: %s", a);
	return(-1);
    }

    if((fsb.st_mode&S_IFMT) == S_IFDIR){ /* is it a directory? */
	emlwrite("\007Can't copy: %s is a directory", a);
	return(-1);
    }

    if(stat(b, &tsb) < 0){		/* get dest file's mode */
	switch(errno){
	  case ENOENT:
	    break;			/* these are OK */
	  default:
	    emlwrite("\007Can't Copy: %s", errstr(errno));
	    return(-1);
	}
    }
    else{
	if(!(tsb.st_mode&S_IWRITE)){	/* can we write it? */
	    emlwrite("\007Write permission denied: %s", b);
	    return(-1);
	}

	if((tsb.st_mode&S_IFMT) == S_IFDIR){	/* is it directory? */
	    emlwrite("\007Can't copy: %s is a directory", b);
	    return(-1);
	}

	if(fsb.st_dev == tsb.st_dev && fsb.st_ino == tsb.st_ino){
	    emlwrite("\007Identical files.  File not copied", NULL);
	    return(-1);
	}
    }

    if((in = open(a, _O_RDONLY)) < 0){
	emlwrite("Copy Failed: %s", errstr(errno));
	return(-1);
    }

    if((out=creat(b, fsb.st_mode&0xfff)) < 0){
	emlwrite("Can't Copy: %s", errstr(errno));
	close(in);
	return(-1);
    }

    if((cb = (char *)malloc(NLINE*sizeof(char))) == NULL){
	emlwrite("Can't allocate space for copy buffer!", NULL);
	close(in);
	close(out);
	return(-1);
    }

    while(1){				/* do the copy */
	if((n = read(in, cb, NLINE)) < 0){
	    emlwrite("Can't Read Copy: %s", errstr(errno));
	    rv = -1;
	    break;			/* get out now */
	}

	if(n == 0)			/* done! */
	  break;

	if(write(out, cb, n) != n){
	    emlwrite("Can't Write Copy: %s", errstr(errno));
	    rv = -1;
	    break;
	}
    }

    free(cb);
    close(in);
    close(out);
    return(rv);
}


/*
 * Open a file for writing. Return TRUE if all is well, and FALSE on error
 * (cannot create).
 */
ffwopen(fn, readonly)
char    *fn;
int	 readonly;
{
    extern FIOINFO g_pico_fio;

    g_pico_fio.flags = FIOINFO_WRITE;
    if ((g_pico_fio.fp = fopen(g_pico_fio.name = fn, "w")) == NULL) {
        emlwrite("Cannot open file for writing", NULL);
        return (FIOERR);
    }

#ifdef	MODE_READONLY
    if(readonly)
      chmod(fn, MODE_READONLY);		/* fix access rights */
#endif

    return (FIOSUC);
}


/*
 * Close a file. Should look at the status in all systems.
 */
ffclose()
{
    extern FIOINFO g_pico_fio;

    if (fclose(g_pico_fio.fp) != FALSE) {
        emlwrite("Error closing file", NULL);
        return(FIOERR);
    }

    return(FIOSUC);
}


/*
 * ffelbowroom - make sure the destination's got enough room to receive
 *		 what we're about to write...
 */
ffelbowroom()
{
    return(TRUE);
}


/*
 * worthit - generic sort of test to roughly gage usefulness of using 
 *           optimized scrolling.
 *
 * note:
 *	returns the line on the screen, l, that the dot is currently on
 */
worthit(l)
int *l;
{
    int i;			/* l is current line */
    unsigned below;		/* below is avg # of ch/line under . */

    *l = doton(&i, &below);
    below = (i > 0) ? below/(unsigned)i : 0;

    return(below > 3);
}


/*
 * o_insert - optimize screen insert of char c
 */
o_insert(c)
    int c;
{
    return(0);
}


/*
 * o_delete - optimized character deletion
 */
o_delete()
{
    return(0);
}


/*
 * pico_new_mail - just checks mtime and atime of mail file and notifies user 
 *	           if it's possible that they have new mail.
 */
pico_new_mail()
{
    return(0);
}



/*
 * time_to_check - checks the current time against the last time called 
 *                 and returns true if the elapsed time is > below.
 *                 Newmail won't necessarily check, but we want to give it
 *                 a chance to check or do a keepalive.
 */
time_to_check()
{
    static time_t lasttime = 0L;

    if(!timeo)
      return(FALSE);

    if(time((time_t *) 0) - lasttime > (Pmaster ? (time_t)(FUDGE-10) : timeo)){
	lasttime = time((time_t *) 0);
	return(TRUE);
    }
    else
      return(FALSE);
}


/*
 * sstrcasecmp - compare two pointers to strings case independently
 */
sstrcasecmp(s1, s2)
QcompType *s1, *s2;
{
    return((*pcollator)(*(char **)s1, *(char **)s2));
}


int
strucmp(o, r)
    char *o, *r;
{
    return(o ? (r ? stricmp(o, r) : 1) : (r ? -1 : 0));
}


int
struncmp(o, r, n)
    char *o, *r;
    int   n;
{
    return(o ? (r ? strnicmp(o, r, n) : 1) : (r ? -1 : 0));
}


/*
 * chkptinit -- initialize anything we need to support composer
 *		checkpointing
 */
void
chkptinit(file, n)
    char *file;
    int   n;
{
    if(!file[0]){
	long gmode_save = gmode;

	if(gmode&MDCURDIR)
	  gmode &= ~MDCURDIR;  /* so fixpath will use home dir */

	strcpy(file, "#picoTM0.txt");
	fixpath(file, NLINE);
	gmode = gmode_save;
    }
    else{
	int l = strlen(file);

	if(file[l-1] != '\\'){
	    file[l++] = '\\';
	    file[l]   = '\0';
	}

	strcpy(file + l, "#picoTM0.txt");
    }

    if(fexist(file, "r", (off_t *)NULL) == FIOSUC){ /* does file exist? */
	char copy[NLINE];

	strcpy(copy, "#picoTM1.txt");
	fixpath(copy, NLINE);
	rename(file, copy);  /* save so we don't overwrite it */
    }

    unlink(file);
}


void
set_collation(collation, ctype)
    int collation;
    int ctype;
{
    extern int collator();  /* strcoll isn't declared on all systems */
#ifdef LC_COLLATE
    char *status = NULL;
#endif

    pcollator = strucmp;

#ifdef LC_COLLATE
  if(collation){
    /*
     * This may not have the desired effect, if collator is not
     * defined to be strcoll in os.h and strcmp and friends
     * don't know about locales. If your system does have strcoll
     * but we haven't defined collator to be strcoll in os.h, let us know.
     */
    status = setlocale(LC_COLLATE, "");

    /*
     * If there is an error or if the locale is the "C" locale, then we
     * don't want to use strcoll because in the default "C" locale strcoll
     * uses strcmp ordering and we want strucmp ordering.
     *
     * The test for "C" isn't really correct, since status does not have to
     * be "C" even if we're in the "C" locale. But this works on some systems.
     */
    if(status && !(status[0] == 'C' && status[1] == '\0'))
      pcollator = collator;
  }
#endif
#ifdef LC_CTYPE
  if(ctype){
    (void)setlocale(LC_CTYPE, "");
  }
#endif
}


/*
 * sleep the given number of microseconds
 */
ssleep(s)
    clock_t s;
{
    s += clock();
    while(s > clock())
      ;
}


/*
 * sleep the given number of seconds
 */
sleep(t)
    int t;
{
    time_t out = (time_t)t + time((long *) 0);
    while(out > time((long *) 0))
      ;
}


/*
 * map the ftruncate call into DOS' chsize
 */
int
truncate(file, size)
    char *file;
    long  size;
{
    int fd, rv = -1;

    if((fd = open(file, O_RDWR | O_CREAT | S_IREAD | S_IWRITE)) != -1){
	if(chsize(fd, size) == 0)
	  rv = 0;

	close(fd);
    }

    return(rv);
}
