/* file_rw.c  -  consist of ReadFile and WriteFile procedures to be used
		 together with Motif applications.
		 (C) A. Stochniol,  1991 - 1994
		 Last revision: 20 April 1994
*/
/*
 * Copyright 1991 - 1994,  Andrzej Stochniol, London, UK
 *
 * ASEDIT text editor, both binary and source (hereafter, Software) is
 * copyrighted by Andrzej Stochniol (hereafter, AS) and ownership remains
 * with AS.
 *
 * AS grants you (hereafter, Licensee) a license to use the Software
 * for academic, research and internal business purposes only, without a
 * fee.  Licensee may distribute the binary and source code (if released)
 * to third parties provided that the copyright notice and this statement
 * appears on all copies and that no charge is associated with such copies.
 *
 * Licensee may make derivative works.  However, if Licensee distributes
 * any derivative work based on or derived from the Software, then
 * Licensee will:
 * (1) notify AS regarding its distribution of the derivative work, and
 * (2) clearly notify users that such derivative work is a modified version
 *      and not the original ASEDIT distributed by AS.
 *
 * Any Licensee wishing to make commercial use of the Software should
 * contact AS to negotiate an appropriate license for such commercial use.
 * Commercial use includes:
 * (1) integration of all or part of the source code into a product for sale
 *     or license by or on behalf of Licensee to third parties, or 
 * (2) distribution of the binary code or source code to third parties that
 *     need it to utilize a commercial product sold or licensed by or on 
 *     behalf of Licensee.
 *
 * A. STOCHNIOL MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS
 * SOFTWARE FOR ANY PURPOSE.  IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR
 * IMPLIED WARRANTY.  IN NO EVENT SHALL A. STOCHNIOL BE LIABLE FOR ANY
 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
 * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 * By using or copying this Software, Licensee agrees to abide by the
 * copyright law and all other applicable laws, and the terms of this
 * license.
 * AS shall have the right to terminate this license immediately by
 * written notice upon Licensee's breach of, or non-compliance with, any
 * of its terms.  Licensee may be held legally responsible for any
 * copyright infringement that is caused or encouraged by Licensee's
 * failure to abide by the terms of this license.
 *
 *
 * 	Andrzej Stochniol	(A.Stochniol@ic.ac.uk)
 * 	30 Hatch Road
 * 	London SW16 4PN
 * 	UK
 */


#include <stdio.h>

#ifndef vax11c
#   include <fcntl.h>
#else
#   include <file.h>
#endif  /* !vax11c */

#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <utime.h>

#include <X11/Xatom.h>

#include <Xm/Xm.h>


#include "asedit.h"		/* the only reason to include that is to get access
				   to lstr.* locale dependent strings */
#if defined(vax11c) || defined(__DECC)
#   define unlink remove
/*  Note: in some cases the definition of BACKUP_FILE_SUFFIX might not be necessary */
#define BACKUP_FILE_SUFFIX    ".bak"
#endif /* vax11c */


/* declare make_tmpnam function implemeted in version 1.2 of asedit */
#ifdef _NO_PROTO
extern char *make_tmpnam ();
#else  /* ! _NO_PROTO */

extern char *make_tmpnam (void);
#endif

#ifdef _NO_PROTO
Boolean isFileRegular(file_name)
   char *file_name;
#else  /* _NO_PROTO */

Boolean isFileRegular(char *file_name)
#endif
{
   struct stat statbuf;		/* Information on a file. */

   /* check if the file is a regular text file */
#ifdef __DECC
#define stat decc$stat
#endif /* __DECC */
   if(stat(file_name, &statbuf) == 0  &&
	(statbuf.st_mode & S_IFMT) != S_IFREG )
   {
	return(False);
   }
#ifdef __DECC
#undef stat
#endif /* __DECC */

   return(True);

}  /* isFileRegular */



/****************************  ReadFile  ***********************************
**
**		Open the specified file and reads its contents.
**		Returns a pointer to the file contents  if file exists and
**		open is sucessful (otherwise returns NULL).
**		If the file was opened in the read only mode
**		*read_only is True.
*/
#ifdef _NO_PROTO
char * ReadFile(file_name, read_only)
   char *file_name;
   Boolean *read_only;
#else  /* _NO_PROTO */

char * ReadFile(char *file_name, Boolean *read_only)
#endif
{
   struct stat statbuf;		/* Information on a file. */
   long file_length;		/* Length of file. 	  */
   FILE *fp = NULL;		/* Pointer to open file   */
   static char *file_string; /* pointer to the file contents */
   long n_read_items;		/* how many items (here bytes) were read in */

   *read_only = False;

   /* make sure the file is a regular text file and open it */

   if(!isFileRegular(file_name)) return(NULL);	/* not regular */

   if ((fp = fopen(file_name, "r+")) == NULL)
   {
	if ((fp = fopen(file_name, "r")) != NULL)    *read_only = True;
	else    return(NULL);
   }


#ifdef __DECC
#define stat decc$stat
#endif /* __DECC */

   if (stat(file_name, &statbuf) == 0)
	 file_length = statbuf.st_size;
   else
	 file_length = 500000L; /* arbitrary file length */

#ifdef __DECC
#undef stat
#endif /* __DECC */


   /* read the file string */
   file_string = XtMalloc((unsigned)(file_length+1));	/* +1 for NULL character */

   n_read_items = fread(file_string, sizeof(char), file_length, fp);
   file_string[n_read_items]='\0';	/* adding the NULL character to the end
					   of the file_string */
   /* n_read_items was specially introduced because the file string should be
      null terminated. For DOS file file_length takes into account both line feed
      and carriege return, but during read operation they are translated into
      a single line feed character!!! (so n_read_items is smaller than file_length
      for the DOS file !!!) */

   /* close up the file */
   if (fclose(fp)) fprintf(stderr, (char *)lstr.file_close_err, file_name);

   return(file_string);

}  /* ReadFile */


/****************************  WriteFile  **********************************
	WriteFile  - writes a text pointed to by *file_string to the file
	file_name. To preserve hard links no intermediate temporary file is used for save result.
	If the file already exist it is overwritten without any message
	(this procedure is used mainly for saving option. If successful
	returns True.)
*/
#ifdef _NO_PROTO
Boolean WriteFile(file_name, file_string)
    char *file_name;
    char *file_string;
#else  /* _NO_PROTO */

Boolean WriteFile(char *file_name, char *file_string)
#endif
{
    FILE *fp;				/* Pointer to an open file. */
    FILE *tfp;           		/* Pointer to open temporary file. */
    Boolean result;
    int status;
    char system_call_buf[BUFSIZ];

    char *tempname;	/* Temporary file name .*/

    tempname = make_tmpnam();	/* use tmpnam function and consider tmpDir X resource
				   or TMPDIR environmental variable (if set) */

    /* Note: before asedit 1.2 we used mktemp procedure and a piece of code below; since
       asedit 1.2 we use make_tmpnam procedure that takes account of user specified
       tmdDir recource;
       Old piece of code follows (note that in the old code we didn't have to release
       memory allocated for tempname!):
	char tempname[25];
	strcpy(tempname,"/tmp/aseditXXXXXX");
	mktemp(tempname);
    **/

    /* to preserve hard links we are not using rename command any more; instead
       of that we are using copy command
    */

    if ((tfp = fopen(tempname, "w")) == NULL) {
       fprintf(stderr, (char *)lstr.open_tmp_err, tempname);
       XtFree(tempname);
       return(False);
    }


    /* check if the file_name already existed, if yes make a backup of it (make the backup when 
       the user requested it by defining BACKUP_FILE_SUFFIX or backupFileSuffix X resource) */
    if ((fp = fopen(file_name, "r")) != NULL)
    {
	char *backupFileSuffix=NULL;		/* asedit v. 1.2 */	
        char *backname;

	/* if a user set backupFileSuffix recource use it, otherwise use
	   BACKUP_FILE_SUFFIX defined in the Imakefile/Makefile file
	*/
	backupFileSuffix = (char *)lstr.backupFileSuffix;
	if(!backupFileSuffix) 	/* it is NULL, use the install default */
		    backupFileSuffix = (char *)BACKUP_FILE_SUFFIX;
	/* Backup  file name.    */
	backname = XtMalloc(strlen(file_name) + strlen(backupFileSuffix) +1);

	fclose(fp);
	strcpy(backname, file_name);
	if(backupFileSuffix && strlen(backupFileSuffix))
        {    /* make the backup of the file */
             strcat(backname, backupFileSuffix);
	     /* to preserve any possible hard link we copy the old file onto
		the backup (instead of moving it)  */
	     /* unfortunately we lose the old date/time but we might set
		that properly in the future if needed
	     */

#ifndef vax11c
	     sprintf (system_call_buf, "cp %s %s\0", file_name , backname);
#else
	     sprintf (system_call_buf, "copy/nolog/noconfirm %s %s\0", file_name , backname);
#endif /* !vax11c */
  	     status = system(system_call_buf);
  	     if (status != 0)
	     {
		fprintf(stderr, (char *)lstr.backup_file_err,
				backname, file_name);
	        XtFree(backname);
		XtFree(tempname);
	        return(False);
	     }

	     /* 20.12.93 addition;
	        now set the access and modify times of the backup file to the values
		of the original file
	     */
	     {
		struct stat statbuf;
		struct utimbuf times;
		
		if(stat(file_name, &statbuf) == 0)
		{
		    times.actime  = statbuf.st_atime;
		    times.modtime = statbuf.st_mtime;
		    utime(backname, &times);	/* should be OK */
		}
	     }
	     /*** old (1.2) place for XtFree(backname) ***/
	}
	XtFree(backname);
    }

    /* write to a temp file */
    fwrite(file_string, sizeof(char), strlen(file_string) , tfp);

    /* because asedit might be used to edit programs we are checking
       if the last line is a complete line (if it is not we add the line
       feed there to avoid any problems during compilation of the program,
       especially true for FORTRAN programs;
       it is the same what vi does)
       Note: from 1.3 we are doing that if appropriate flag is True.
    */
    if(file_string[strlen(file_string)-1] != '\n' &&
	lstr.autoAddEOFnewline)
		fwrite("\n", sizeof(char), 1, tfp);

    result = True;
    /* flush and close the file (note that if we are unlucky we use in both
       cases the same error message saying we are unable to close the file;
       the flush error is very unlikely here) */
    if (fflush(tfp))
	{ result=False;  fprintf(stderr,(char *)lstr.close_tmp_err,tempname); }
    if (fclose(tfp))
	{ result=False;  fprintf(stderr,(char *)lstr.close_tmp_err,tempname); }

    if(!result) { XtFree(tempname); return(result); }

    /* Move the tempname to the saved file, but do it independent
     of filesystem boundaries; on some modern operating systems the move/rename command is
     not supported accross different file systems!! (e.g. IBM RS/6000) (/tmp probably lies
     in a different file system than the user data files)  */

    sprintf (system_call_buf, "cp %s %s\0", tempname, file_name);
    status = system(system_call_buf);
    unlink (tempname);
    if (status == 0) 	result = True;
    else
    {
	fprintf(stderr, (char *)lstr.file_move_err, file_name);
	result = False;
    }
    XtFree(tempname);
    return(result);

}   /* WriteFile */

