📄 sflfile.c
字号:
/* ----------------------------------------------------------------<Prolog>-
Name: sflfile.c
Title: File-access functions
Package: Standard Function Library (SFL)
Written: 1992/10/28 iMatix SFL project team <sfl@imatix.com>
Revised: 2000/01/19
Copyright: Copyright (c) 1996-2000 iMatix Corporation
License: This is free software; you can redistribute it and/or modify
it under the terms of the SFL License Agreement as provided
in the file LICENSE.TXT. This software is distributed in
the hope that it will be useful, but without any warranty.
------------------------------------------------------------------</Prolog>-*/
#include "prelude.h" /* Universal header file */
#include "sflstr.h" /* String handling functions */
#include "sfllist.h" /* Linked-list functions */
#include "sflmem.h" /* Memory allocation functions */
#include "sflnode.h" /* Linked-list functions */
#include "sfldir.h" /* Directory access functions */
#include "sfldate.h" /* Date/time access functions */
#include "sflsymb.h" /* Symbol-table functions */
#include "sfltok.h" /* Token mashing functions */
#include "sflcons.h" /* Console output functions */
#include "sflenv.h" /* Environment access functions */
#include "sflprint.h" /* snprintf functions */
#include "sflfile.h" /* Prototypes for functions */
/* Ensure our buffers will be big enough for dir + name + delimiters */
#if ((LINE_MAX - FILE_NAME_MAX) < (FILE_DIR_MAX + 10))
# error "Cannot compile; FILE_NAME_MAX is too large."
#endif
static char
#if (PATHFOLD == TRUE || defined (MSDOS_FILESYSTEM))
path_name [PATH_MAX + 1], /* Copy of path symbol */
#endif
work_name [LINE_MAX + 1], /* Name plus ext */
full_name [LINE_MAX + 1], /* Dir plus name plus ext */
exec_name [LINE_MAX + 1]; /* Executable file name */
Bool file_crlf = FALSE; /* Initial default */
/* Function prototypes */
#if (defined (MSDOS_FILESYSTEM))
static Bool system_devicename (const char *filename);
#endif
static char *build_next_path (char *dest, const char *path,
const char *name);
static char *build_next_path_ext (char *dest, const char *path,
const char *name,
const char *ext);
static dbyte file_mode (const char *filename);
#if (defined (__WINDOWS__))
static Bool is_exe_file (const char *filename);
#endif
static DESCR *file_load_data (const char *filename, size_t limit);
static Bool fully_specified (const char *filename);
/* ---------------------------------------------------------------------[<]-
Function: file_open
Synopsis: opens a text file for reading or writing. Use in combination
with the file_read() and file_write() functions. These functions handle
end-of-line sequences using a heuristic that works as follows.
... (at this point the author went for a pint of beer and has not been
seen since. We're hoping that the old version - following - is ok.)
Synopsis: Opens the specified file for input or output. If you use
the file_read / file_write functions you must open the file using this
function. This set of functions lets you read files without concern
for the line format (CRLF or LF). Mode should be one of 'r' 'w' 'a'.
Returns a FILE pointer if the file is opened correctly; else NULL.
Sets the global variable file_crlf to FALSE on all systems except MS-DOS
(and Windows by inheritence) where it is set to TRUE by default.
When opening a file in append mode, automatically removes any Ctrl-Z
character under MS-DOS or OS/2.
---------------------------------------------------------------------[>]-*/
FILE *
file_open (
const char *filename, /* Name of file to open */
char mode) /* 'r', 'w', or 'a' */
{
#if (defined (MSDOS_FILESYSTEM))
if (system_devicename (filename))
return (NULL); /* Not allowed on device names */
file_crlf = TRUE;
# if (defined (WIN32))
SetErrorMode (SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
# endif
# else
ASSERT (filename);
file_crlf = FALSE;
#endif
if (mode == 'r')
return (fopen (filename, FOPEN_READ_BINARY));
else
if (mode == 'w')
return (fopen (filename, FOPEN_WRITE_BINARY));
else
if (mode == 'a'
&& safe_to_extend (filename))
return (fopen (filename, FOPEN_APPEND_BINARY));
else
return (NULL); /* Invalid mode */
}
#if (defined (MSDOS_FILESYSTEM))
/* Under MS-DOS, use of filenames containing 'aux', 'con', 'prn', or
* 'nul' can cause problems, especially for reading. We reject any
* use of these names for directories or filenames.
*/
static Bool
system_devicename (const char *supplied_filename)
{
char
*filename,
*char_ptr,
*token,
**tokens;
int
token_nbr;
Bool
feedback;
filename = mem_strdup (supplied_filename);
strconvch (filename, ' ', '_'); /* Don't break on real spaces */
strconvch (filename, '/', ' ');
strconvch (filename, '\\', ' ');
strlwc (filename); /* All comparisons in lowercase */
/* Skip disk specifier if present */
if (strlen (filename) > 2 && filename [1] == ':')
filename [0] = filename [1] = ' ';
/* Wipe out file extensions */
for (char_ptr = filename; *char_ptr; char_ptr++)
{
if (*char_ptr == '.') /* Wipe over file extensions */
while (*char_ptr && *char_ptr != ' ')
*char_ptr++ = ' ';
if (*char_ptr == '\0')
break;
}
tokens = tok_split (filename);
feedback = FALSE;
for (token_nbr = 0; tokens [token_nbr]; token_nbr++)
{
token = tokens [token_nbr];
#if (defined (WIN32))
/* Ask Windows to check if it's a device */
if (QueryDosDevice (token, NULL, 0) == ERROR_INSUFFICIENT_BUFFER)
#else
if (streq (token, "aux")
|| streq (token, "con")
|| streq (token, "nul")
|| streq (token, "prn")
|| (strprefixed (token, "com") && isdigit (token [3]))
|| (strprefixed (token, "lpt") && isdigit (token [3])))
#endif
{
feedback = TRUE;
break;
}
}
tok_free (tokens);
mem_free (filename);
return (feedback);
}
#endif
/* ---------------------------------------------------------------------[<]-
Function: file_locate
Synopsis: Combines the functions of file_where() and file_open when you
want to read a file. Searches for a file on a specified path, opens the
file if found, and returns a FILE * for the open file. Returns NULL if
the file was not found or could not be opened for reading.
---------------------------------------------------------------------[>]-*/
FILE *
file_locate (
const char *path,
const char *name,
const char *ext)
{
char
*filename;
ASSERT (name);
filename = file_where ('r', path, name, ext);
if (filename)
return (file_open (filename, 'r'));
else
return (NULL);
}
/* ---------------------------------------------------------------------[<]-
Function: file_close
Synopsis: Closes an open file stream. Returns 0 if okay, -1 if there
was an error. For now, equivalent to fclose, and supplied because it
looks nice when you use file_open() and file_close() together.
---------------------------------------------------------------------[>]-*/
int
file_close (
FILE *stream)
{
if (stream)
return (fclose (stream));
else
return (0);
}
/* ---------------------------------------------------------------------[<]-
Function: file_read
Synopsis: Reads a line of text delimited by newline from the stream.
The string must be LINE_MAX + 1 long. Places a null byte in place of
the newline character. Expands tab characters to every 8th column.
Returns TRUE when there is more input waiting; FALSE when the last line
of the file has been read.
Sets the global variable file_crlf to TRUE if CR was found in the file.
This variable is by default FALSE. It is also used by file_write.
---------------------------------------------------------------------[>]-*/
Bool
file_read (
FILE *stream,
char *string)
{
int
ch, /* Character read from file */
cnbr; /* Index into returned string */
ASSERT (stream);
ASSERT (string);
cnbr = 0; /* Start at the beginning... */
memset (string, ' ', LINE_MAX); /* and prepare entire line */
for (;;)
{
ch = fgetc (stream); /* Get next character from file */
if (ch == '\t') /* Jump if tab */
cnbr = ((cnbr >> 3) << 3) + 8;
else
if (ch == '\r') /* Found carriage-return */
file_crlf = TRUE; /* Set flag and ignore CR */
else
if ((ch == '\n') /* Have end of line */
|| (ch == EOF) /* or end of file */
|| (ch == 26)) /* or MS-DOS Ctrl-Z */
{
string [cnbr] = '\0'; /* Terminate string */
return (ch == '\n' || cnbr); /* and return TRUE/FALSE */
}
else
if (cnbr < LINE_MAX)
string [cnbr++] = (char) ch; /* Else add char to string */
if (cnbr >= LINE_MAX) /* Return in any case if line is */
{ /* too long - the line will be */
string [LINE_MAX] = '\0'; /* cut into pieces */
return (TRUE);
}
}
}
/* ---------------------------------------------------------------------[<]-
Function: file_readn
Synopsis: Works as file_read() but with a maximum line-length specified
by the caller. The supplied buffer must be at least as large as the
specified line_max + 1.
---------------------------------------------------------------------[>]-*/
Bool
file_readn (
FILE *stream,
char *string,
int line_max)
{
int
ch, /* Character read from file */
cnbr; /* Index into returned string */
ASSERT (stream);
ASSERT (string);
cnbr = 0; /* Start at the beginning... */
memset (string, ' ', line_max); /* and prepare entire line */
for (;;)
{
ch = fgetc (stream); /* Get next character from file */
if (ch == '\t') /* Jump if tab */
cnbr = ((cnbr >> 3) << 3) + 8;
else
if (ch == '\r') /* Found carriage-return */
file_crlf = TRUE; /* Set flag and ignore CR */
else
if ((ch == '\n') /* Have end of line */
|| (ch == EOF) /* or end of file */
|| (ch == 26)) /* or MS-DOS Ctrl-Z */
{
string [cnbr] = '\0'; /* Terminate string */
return (ch == '\n' || cnbr); /* and return TRUE/FALSE */
}
else
if (cnbr < line_max)
string [cnbr++] = (char) ch; /* Else add char to string */
if (cnbr >= line_max) /* Return in any case if line is */
{ /* too long - the line will be */
string [line_max] = '\0'; /* cut into pieces */
return (TRUE);
}
}
}
/* ---------------------------------------------------------------------[<]-
Function: file_write
Synopsis: Writes a line of text to the specified output stream. If the
variable file_crlf is TRUE, adds a carriage-return to the line being
written to the output stream. This variable is supplied so that you can
either ignore crlf issues (do nothing), or handle them explicitly (play
with file_crlf). Returns the string written, or NULL if no data could
be written to the file.
---------------------------------------------------------------------[>]-*/
char *
file_write (
FILE *stream,
const char *string)
{
ASSERT (stream);
ASSERT (string);
fputs (string, stream);
if (file_crlf)
fputc ('\r', stream);
if (fputc ('\n', stream) == EOF)
return (NULL);
else
return ((char *) string);
}
/* ---------------------------------------------------------------------[<]-
Function: file_copy
Synopsis: Copies a file called src to one called dest. The dest file
may not already exist. If mode is 'b', copies a binary file; if mode is
't', copies a text file. This distinction only applies to MS-DOS file
systems; on other platforms the two modes are equivalent. Returns 0
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -