#include "elm_defs.h"
#include "elm_globals.h"
#include "s_elm.h"

/*
 * These routines perform many common file operations, with the
 * addition of error checking.  They include:
 *
 * file_open	fopen() with error checking
 * file_close	fclose() with error checking
 * file_seek	fseek() with error checking
 * file_copy	copy from one (FILE *) to another (FILE *).
 * file_rename	rename() with error checking
 *
 * These routines follow ALMOST the same calling and return semantics as
 * their Unix namesakes.  (The chief difference is that some require
 * some additional filename args in the event an error message must be
 * displayed.)  Whenever an error occurs, these routines will terminate
 * with a message showing on the error line (via the error() routines).
 */


PUBLIC FILE *file_open(fname, fmode)
const char *fname, *fmode;
{
    FILE *fp;
    const char *modestr;
    int err;
    int fp_handle;
   
   
    if (fmode[0] != 'r') { save_file_stats(fname); } 
    /* no reason to call save_file_stats if it's read only */
   
    if ( (fmode[0] == 'w') && (fmode[1] != '+') ) /* it's temp file */
    {
       unlink(fname);
       if ( ((fp_handle = open(fname, O_RDWR|O_CREAT|O_EXCL,0600)) != -1) &&
	    ((fp = fdopen(fp_handle,fmode)) != NULL) ) {
	  restore_file_stats(fname);
          return fp;
       }
    }
    else if ((fp = fopen(fname, fmode)) != NULL) {
	if (fmode[0] != 'r') {restore_file_stats(fname);}
	return fp;
    }

    err = errno;
    dprint(1, (debugfile, "file_open(%s,%s) FAILED [%s]\n",
	fname, fmode, strerror(err)));
    switch (*fmode) {
    case 'r':
	modestr = (fmode[1] == '+'
	    ? catgets(elm_msg_cat, ElmSet, ElmFileOpenModeUpdate, "update")
	    : catgets(elm_msg_cat, ElmSet, ElmFileOpenModeRead, "read"));
	break;
    case 'w':
	modestr = (fmode[1] == '+'
	    ? catgets(elm_msg_cat, ElmSet, ElmFileOpenModeCreate, "create")
	    : catgets(elm_msg_cat, ElmSet, ElmFileOpenModeWrite, "write"));
	break;
    case 'a':
	modestr = catgets(elm_msg_cat, ElmSet, ElmFileOpenModeAppend, "append");
	break;
    default:
	modestr = fmode;
	break;
    }
    error3(catgets(elm_msg_cat, ElmSet, ElmFileOpenFailed,
	    "Cannot open \"%s\" for %s.  [%s]"),
	    fname, modestr, strerror(err));

    errno = err;
    return (FILE *) NULL;
}


PUBLIC int file_close(fp, fname)
FILE *fp;
const char *fname;
{
    int err;

    if (ferror(fp) != 0) {
	err = errno;
	dprint(1, (debugfile, "file_close(%s) ferror() FAILED [%s]\n",
	    fname, strerror(err)));
	goto done;
    }
    if (fclose(fp) != 0) {
	err = errno;
	dprint(1, (debugfile, "file_close(%s) fclose() FAILED [%s]\n",
	    fname, strerror(err)));
	goto done;
    }
    return 0;

done:
    error2(catgets(elm_msg_cat, ElmSet, ElmFileCloseFailed,
		"Error closing \"%s\".  [%s]"), fname, strerror(errno));
    errno = err;
    return -1;
}


PUBLIC int file_access(name, mode)
const char *name;
int mode;
{
    int rc, err;

    if ((rc = can_access(name, mode)) < 0) {
	err = errno;
	error2(catgets(elm_msg_cat, ElmSet, ElmFileAccessFailed,
		    "Cannot access file \"%s\".  [%s]"),
		    basename(name), strerror(err));
	errno = err;
    }
    return rc;
}


PUBLIC int file_seek(fp, offset, whence, fname)
FILE *fp;
long offset;
int whence;
const char *fname;
{
    int rc, err;

    if (fseek(fp, offset, whence) == 0)
	return 0;
    err = errno;
    dprint(1, (debugfile, "file_seek(%s,%lu,%d) FAILED [%s]\n",
	fname, (unsigned long) offset, whence, strerror(err)));
    switch (whence) {
    case SEEK_END:
	error3(catgets(elm_msg_cat, ElmSet, ElmFileSeekEndFailed,
		    "Error seeking %lu bytes from end in \"%s\".  [%s]"),
		    (unsigned long)offset, fname, strerror(err));
	break;
    case SEEK_CUR:
	error3(catgets(elm_msg_cat, ElmSet, ElmFileSeekCurFailed,
		    "Error seeking ahead %lu bytes in \"%s\".  [%s]"),
		    (unsigned long)offset, fname, strerror(err));
	break;
    case SEEK_SET:
    default:
	error3(catgets(elm_msg_cat, ElmSet, ElmFileSeekSetFailed,
		    "Error seeking byte %lu in \"%s\".  [%s]"),
		    (unsigned long)offset, fname, strerror(err));
	break;
    }
    errno = err;
    return -1;
}


#ifndef BUFSIZ
# define BUFSIZ 1024
#endif

PUBLIC int file_copy(fpsrc, fpdest, srcname, destname)
FILE *fpsrc, *fpdest;
const char *srcname, *destname;
{
    char buf[BUFSIZ];
    int i, err;

    while ((i = fread(buf, 1, sizeof(buf), fpsrc)) > 0) {
	if (fwrite(buf, 1, i, fpdest) != i) {
	    err = errno;
	    dprint(1, (debugfile, "file_copy(%s,%s) fwrite FAILED [%s]\n",
		    (srcname ? srcname : "<src>"),
		    (destname ? destname: "<dest>"),
		    strerror(err)));
	    if (destname) {
		error2(catgets(elm_msg_cat, ElmSet,
			ElmFileCopyErrorWritingFile,
			"Error writing \"%s\".  [%s]"),
			destname, strerror(err));
	    } else {
		error1(catgets(elm_msg_cat, ElmSet,
			ElmFileCopyErrorWritingOutput,
			"Error writing output file.  [%s]"),
			strerror(err));
	    }
	    return -1;
	}
    }
    if (i < 0) {
	err = errno;
	dprint(1, (debugfile, "file_copy(%s,%s) fread FAILED [%s]\n",
		(srcname ? srcname : "<src>"),
		(destname ? destname: "<dest>"),
		strerror(err)));
	if (srcname) {
	    error2(catgets(elm_msg_cat, ElmSet, ElmFileCopyErrorReadingFile,
		    "Error reading \"%s\".  [%s]"), srcname, strerror(err));
	} else {
	    error2(catgets(elm_msg_cat, ElmSet, ElmFileCopyErrorReadingInput,
		    "Error reading input file.  [%s]"), strerror(err));
	}
	return -1;
    }

    return 0;
}


PUBLIC int file_rename(srcname, destname)
const char *srcname, *destname;
{
    int err;

    if (rename(srcname, destname) == 0)
	return 0;
    err = errno;
    dprint(1, (debugfile, "file_rename(%s,%s) FAILED [%s]\n",
	    srcname, destname, strerror(err)));
    error2(catgets(elm_msg_cat, ElmSet, ElmFileRenameFailed,
	    "Error renaming \"%s\".  [%s]"), srcname, strerror(err));
    return -1;
}

