2017-04-25 14:41:48 +02:00
|
|
|
/* path.c
|
|
|
|
|
*
|
2020-05-23 23:13:14 +02:00
|
|
|
* *********************************************************************
|
|
|
|
|
* * Copyright (C) 1985, 1990 Regents of the University of California. *
|
|
|
|
|
* * Permission to use, copy, modify, and distribute this *
|
|
|
|
|
* * software and its documentation for any purpose and without *
|
|
|
|
|
* * fee is hereby granted, provided that the above copyright *
|
|
|
|
|
* * notice appear in all copies. The University of California *
|
|
|
|
|
* * makes no representations about the suitability of this *
|
|
|
|
|
* * software for any purpose. It is provided "as is" without *
|
|
|
|
|
* * express or implied warranty. Export of this software outside *
|
|
|
|
|
* * of the United States of America may require an export license. *
|
2017-04-25 14:41:48 +02:00
|
|
|
* *********************************************************************
|
|
|
|
|
*
|
|
|
|
|
* This file contains routines that implement a path mechanism, whereby
|
|
|
|
|
* several places may be searched for files.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#ifndef lint
|
|
|
|
|
static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/utils/path.c,v 1.3 2009/05/13 15:03:18 tim Exp $";
|
|
|
|
|
#endif /* not lint */
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <pwd.h>
|
|
|
|
|
#include <ctype.h>
|
|
|
|
|
#include <sys/param.h>
|
2022-11-02 22:12:46 +01:00
|
|
|
#include <sys/stat.h>
|
|
|
|
|
#include <fcntl.h>
|
2025-07-14 15:18:49 +02:00
|
|
|
#include <unistd.h>
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2022-05-10 15:19:39 +02:00
|
|
|
#ifdef HAVE_ZLIB
|
|
|
|
|
#include <zlib.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
#include "tcltk/tclmagic.h"
|
|
|
|
|
#include "utils/magic.h"
|
|
|
|
|
#include "utils/hash.h"
|
|
|
|
|
#include "utils/malloc.h"
|
|
|
|
|
#include "utils/utils.h"
|
2024-10-04 21:07:03 +02:00
|
|
|
#include "utils/magic_zlib.h"
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* Library routines: */
|
|
|
|
|
|
|
|
|
|
/* extern char *getenv(); */
|
|
|
|
|
/* extern char *strcmp(); */
|
|
|
|
|
|
|
|
|
|
/* A hash table is used to keep track of the logins and variable expansions we've
|
|
|
|
|
* already looked up.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static HashTable expansionTable;
|
|
|
|
|
static bool noTable = TRUE;
|
|
|
|
|
|
Played around with the file locking and discovered to my chagrin that
whenever a process writes a cell to disk, it immediately releases the
file lock it had on that cell, which is clearly not the intent of file
locking. Fixed this issue. On a related topic, revised the "cellname
writeable" command so that it can make a cell editable even if the cell
has an advisory lock and cannot be made writeable. Perhaps there should
be a clearer distinction here between "writeable" and "editable". Also:
Reconsidered the previous commit, which removed the "--disable-locking"
from the configuration options. Because some operating systems may not
implement fnctl()-based file locking (Cygwin, for one, apparently doesn't),
it is still useful to be able to completely remove the function, in case
the operating system will fail to recognize the fnctl() values in the
code. Now, file locking behavior can be permanently removed through the
configuration option, or temporarily disabled from the command line.
2022-01-01 22:53:46 +01:00
|
|
|
#ifdef FILE_LOCKS
|
2022-01-01 20:28:59 +01:00
|
|
|
bool FileLocking = TRUE;
|
Played around with the file locking and discovered to my chagrin that
whenever a process writes a cell to disk, it immediately releases the
file lock it had on that cell, which is clearly not the intent of file
locking. Fixed this issue. On a related topic, revised the "cellname
writeable" command so that it can make a cell editable even if the cell
has an advisory lock and cannot be made writeable. Perhaps there should
be a clearer distinction here between "writeable" and "editable". Also:
Reconsidered the previous commit, which removed the "--disable-locking"
from the configuration options. Because some operating systems may not
implement fnctl()-based file locking (Cygwin, for one, apparently doesn't),
it is still useful to be able to completely remove the function, in case
the operating system will fail to recognize the fnctl() values in the
code. Now, file locking behavior can be permanently removed through the
configuration option, or temporarily disabled from the command line.
2022-01-01 22:53:46 +01:00
|
|
|
#endif
|
2022-01-01 20:28:59 +01:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* Limit on how long a single file name may be: */
|
|
|
|
|
|
|
|
|
|
#define MAXSIZE MAXPATHLEN
|
|
|
|
|
|
2022-11-02 22:12:46 +01:00
|
|
|
#ifdef HAVE_ZLIB
|
2025-02-13 09:22:28 +01:00
|
|
|
/* this was found repeated a number of times, only sometimes checking NULL
|
|
|
|
|
* return from gzdopen() which is unlikely/difficult to ever see from libz,
|
|
|
|
|
* but then static code analyser raises multiple concerns over multiple paths
|
|
|
|
|
* leaking an fd. So at least this resolve these things and quietens the
|
|
|
|
|
* output somewhat.
|
2025-02-13 09:23:07 +01:00
|
|
|
* Made non-static as flock() can use it but still considered module internal API.
|
2025-02-13 09:22:28 +01:00
|
|
|
*/
|
2025-02-13 09:23:07 +01:00
|
|
|
gzFile
|
|
|
|
|
path_gzdopen_internal(const char *path, int oflags, const char *modestr, int *fdp)
|
2025-02-13 09:22:28 +01:00
|
|
|
{
|
|
|
|
|
ASSERT(fdp, "fdp");
|
|
|
|
|
|
|
|
|
|
int fd = open(path, oflags);
|
|
|
|
|
if (fd < 0)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
/* when gzdopen() successful ownership of fd is transferred */
|
|
|
|
|
gzFile gzhandle = gzdopen(fd, modestr);
|
|
|
|
|
if (gzhandle == NULL)
|
|
|
|
|
goto close;
|
|
|
|
|
|
|
|
|
|
if (fdp) /* but sometimes the caller still wants to know the fd */
|
|
|
|
|
*fdp = fd;
|
|
|
|
|
return gzhandle;
|
|
|
|
|
|
|
|
|
|
close:
|
|
|
|
|
close(fd);
|
|
|
|
|
if (fdp)
|
|
|
|
|
*fdp = -1;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-02 22:12:46 +01:00
|
|
|
/*
|
|
|
|
|
*-------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* PaCheckCompressed() ---
|
|
|
|
|
*
|
|
|
|
|
* Check if a file is compressed by adding ".gz" to the name and
|
|
|
|
|
* attempting to open the file.
|
|
|
|
|
*
|
|
|
|
|
* Return value:
|
2024-10-12 09:05:28 +02:00
|
|
|
* The string value that resulted in a valid file descriptor
|
|
|
|
|
* (a file that could be opened using that filename).
|
|
|
|
|
* Return value is a dynamically allocated string the calling
|
|
|
|
|
* routine should free.
|
2022-11-02 22:12:46 +01:00
|
|
|
*
|
|
|
|
|
*-------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
|
PaCheckCompressed(filename)
|
2024-10-12 09:05:57 +02:00
|
|
|
const char *filename;
|
2022-11-02 22:12:46 +01:00
|
|
|
{
|
|
|
|
|
char *gzname;
|
|
|
|
|
|
|
|
|
|
gzname = (char *)mallocMagic(strlen(filename) + 4);
|
|
|
|
|
sprintf(gzname, "%s.gz", filename);
|
|
|
|
|
|
2025-02-13 09:22:43 +01:00
|
|
|
int err = access(gzname, R_OK);
|
|
|
|
|
if (err < 0)
|
2024-10-12 09:05:28 +02:00
|
|
|
strcpy(gzname, filename); /* always shorter than allocation */
|
2022-11-02 22:12:46 +01:00
|
|
|
|
|
|
|
|
return gzname;
|
|
|
|
|
}
|
|
|
|
|
#endif /* HAVE_ZLIB */
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*-------------------------------------------------------------------
|
|
|
|
|
* PaAppend --
|
|
|
|
|
* Add a string to the designated path variable.
|
|
|
|
|
*
|
|
|
|
|
* pathptr is a pointer to the path variable; srcptr is the
|
2020-05-23 23:13:14 +02:00
|
|
|
* newstring is the new string to append to the path.
|
2017-04-25 14:41:48 +02:00
|
|
|
*/
|
|
|
|
|
void
|
2024-10-12 09:10:39 +02:00
|
|
|
PaAppend(char **pathptr, const char *newstring)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
int oldlength, addlength;
|
|
|
|
|
char *new;
|
|
|
|
|
|
|
|
|
|
oldlength = strlen(*pathptr);
|
|
|
|
|
addlength = strlen(newstring);
|
|
|
|
|
new = (char *)mallocMagic((unsigned) (oldlength + addlength + 2));
|
|
|
|
|
(void) strcpy(new, *pathptr);
|
|
|
|
|
new[oldlength] = ' ';
|
|
|
|
|
(void) strcpy(new + oldlength + 1, newstring);
|
|
|
|
|
freeMagic(*pathptr);
|
|
|
|
|
*pathptr = new;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*-------------------------------------------------------------------
|
|
|
|
|
* PaExpand --
|
|
|
|
|
* This routine converts tilde notation and environment variable
|
|
|
|
|
* notation into standard directory names.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* If the conversion was done successfully, then the return value
|
|
|
|
|
* is the number of bytes of space left in the destination area.
|
|
|
|
|
* If a user name couldn't be found in the password file, then
|
|
|
|
|
* -1 is returned.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
* If the first character of the string indicated by psource is a
|
|
|
|
|
* tilde ("~") then the subsequent user name is converted to a login
|
|
|
|
|
* directory name and stored in the string indicated by dest. Then
|
|
|
|
|
* remaining characters in the file name at psource are copied to
|
|
|
|
|
* pdest (the file name is terminated by white space, a null character,
|
|
|
|
|
* or a colon) and psource is updated. Upon return, psource points
|
|
|
|
|
* to the terminating character in the source file name, and pdest
|
|
|
|
|
* points to the null character terminating the expanded name.
|
|
|
|
|
* If a tilde cannot be converted because the user name cannot
|
|
|
|
|
* be found, psource is still advanced past the current entry, but
|
|
|
|
|
* nothing is stored at the destination. At most size characters
|
|
|
|
|
* (including the terminating null character) will be stored at pdest.
|
|
|
|
|
* Note: the name "~" with no user name expands to the home directory.
|
|
|
|
|
*
|
|
|
|
|
* If the first character of the string indicated by psource is a
|
|
|
|
|
* dollar sign ("$") then the subsequent name is converted to the
|
|
|
|
|
* expansion of the indicated environment variable. If the environment
|
|
|
|
|
* variable does not exist and the indicated name is "CAD_ROOT", then
|
|
|
|
|
* we substitute the value of CAD_DIR passed down from the Makefile.
|
|
|
|
|
* Otherwise, we return error status -1.
|
|
|
|
|
*
|
|
|
|
|
*-------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
PaExpand(psource, pdest, size)
|
2024-10-12 09:15:58 +02:00
|
|
|
const char **psource; /* Pointer to a pointer to the source string */
|
2017-04-25 14:41:48 +02:00
|
|
|
char **pdest; /* Pointer to a ptr to dest string area. */
|
|
|
|
|
int size; /* Number of bytes available at pdest */
|
|
|
|
|
|
|
|
|
|
{
|
2024-10-12 09:15:58 +02:00
|
|
|
const char *ps;
|
|
|
|
|
char *pd;
|
2017-04-25 14:41:48 +02:00
|
|
|
struct passwd *passwd, *getpwnam();
|
2020-12-04 20:46:48 +01:00
|
|
|
char expandName[512], *string, *newEntry;
|
2017-04-25 14:41:48 +02:00
|
|
|
HashEntry *h;
|
|
|
|
|
int i, length;
|
|
|
|
|
|
|
|
|
|
size -= 1;
|
|
|
|
|
ps = *psource;
|
|
|
|
|
if (*ps == '~')
|
|
|
|
|
{
|
|
|
|
|
/* Strip off the login name from the front of the file name. */
|
|
|
|
|
|
|
|
|
|
pd = expandName;
|
|
|
|
|
for (i=0; ; i++)
|
|
|
|
|
{
|
|
|
|
|
*pd = *++ps;
|
|
|
|
|
if (isspace(*pd) || (*pd=='\0') || (*pd=='/') || (*pd==':'))
|
|
|
|
|
break;
|
2020-12-04 20:46:48 +01:00
|
|
|
if (i < 511) pd++;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
*pd = '\0';
|
|
|
|
|
|
|
|
|
|
/* Lookup the login name in the hash table. Create a hash
|
|
|
|
|
* table if we don't have one already.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (noTable)
|
|
|
|
|
{
|
|
|
|
|
HashInit(&expansionTable, 16, 0);
|
|
|
|
|
noTable = FALSE;
|
|
|
|
|
}
|
|
|
|
|
h = HashFind(&expansionTable, expandName);
|
|
|
|
|
string = HashGetValue(h);
|
|
|
|
|
if (string != 0) goto gotname;
|
|
|
|
|
|
|
|
|
|
/* We haven't seen this name before. Look it up in the
|
|
|
|
|
* password file. If the name is "~", then just use the
|
|
|
|
|
* home directory.
|
|
|
|
|
*/
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
if (strcmp(expandName, "") == 0)
|
2017-04-25 14:41:48 +02:00
|
|
|
string = getenv("HOME");
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
string = NULL;
|
|
|
|
|
passwd = getpwnam(expandName);
|
|
|
|
|
if (passwd != NULL) string = passwd->pw_dir;
|
|
|
|
|
}
|
|
|
|
|
if (string != NULL)
|
|
|
|
|
{
|
|
|
|
|
newEntry = (char *) mallocMagic((unsigned) (strlen(string) + 1));
|
|
|
|
|
(void) strcpy(newEntry, string);
|
|
|
|
|
HashSetValue(h, newEntry);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* No login entry. Skip the rest of the file name. */
|
|
|
|
|
|
|
|
|
|
while ((*ps != '\0') && !isspace(*ps) && (*ps != ':')) ps++;
|
|
|
|
|
*psource = ps;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gotname: length = strlen(string);
|
|
|
|
|
if (length > size) length = size;
|
|
|
|
|
(void) strncpy(*pdest, string, length+1);
|
|
|
|
|
size -= length;
|
|
|
|
|
pd = *pdest+length;
|
|
|
|
|
}
|
|
|
|
|
else if (*ps == '$') /* (Possible) environment variable expansion */
|
|
|
|
|
{
|
|
|
|
|
char saveChar;
|
|
|
|
|
|
|
|
|
|
pd = expandName;
|
|
|
|
|
for (i=0; ; i++)
|
|
|
|
|
{
|
|
|
|
|
*pd = *++ps;
|
|
|
|
|
if (isspace(*pd) || (*pd=='\0') || (*pd=='/') || (*pd==':'))
|
|
|
|
|
break;
|
2020-12-04 20:46:48 +01:00
|
|
|
if ((i < 511) && (*pd != '{') && (*pd != '}')) pd++;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
saveChar = *pd;
|
|
|
|
|
*pd = '\0';
|
|
|
|
|
|
|
|
|
|
/* Lookup the environment variable in the hash table. Make a new
|
|
|
|
|
* table if we don't have one already.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (noTable)
|
|
|
|
|
{
|
|
|
|
|
HashInit(&expansionTable, 16, 0);
|
|
|
|
|
noTable = FALSE;
|
|
|
|
|
}
|
|
|
|
|
h = HashFind(&expansionTable, expandName);
|
|
|
|
|
string = HashGetValue(h);
|
|
|
|
|
if (string != 0) goto gotvar;
|
|
|
|
|
|
|
|
|
|
/* We haven't seen this name before. Get it with "getenv".
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
string = (char *)Tcl_GetVar(magicinterp, expandName, TCL_GLOBAL_ONLY);
|
|
|
|
|
#else
|
|
|
|
|
string = getenv(expandName);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (string == NULL)
|
|
|
|
|
{
|
|
|
|
|
/* Check for CAD_ROOT = CAD_DIR, the only internal variable */
|
2021-01-04 15:47:03 +01:00
|
|
|
/* recognized (this is passed down from the Makefile). */
|
|
|
|
|
/* Note that in the MAGIC_WRAPPER version, CAD_ROOT was set */
|
|
|
|
|
/* as a Tcl variable in tcltk/tclmagic.c, such that if */
|
|
|
|
|
/* expandName == "CAD_ROOT", then string should not be NULL */
|
|
|
|
|
/* here. */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
if (!strcmp(expandName, "CAD_ROOT"))
|
|
|
|
|
string = CAD_DIR;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
*pd = saveChar;
|
|
|
|
|
ps = *psource;
|
|
|
|
|
goto noexpand;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
newEntry = (char *) mallocMagic((unsigned) (strlen(string) + 1));
|
|
|
|
|
(void) strcpy(newEntry, string);
|
|
|
|
|
HashSetValue(h, newEntry);
|
|
|
|
|
|
|
|
|
|
gotvar: length = strlen(string);
|
|
|
|
|
if (length > size) length = size;
|
|
|
|
|
(void) strncpy(*pdest, string, length+1);
|
|
|
|
|
size -= length;
|
|
|
|
|
pd = *pdest+length;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* No tilde or variable to expand. As a minor convenience, check
|
|
|
|
|
* to see if the first two characters of the name are "./". If so,
|
|
|
|
|
* then just skip over them.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
noexpand:
|
|
|
|
|
while (ps[0] == '.')
|
|
|
|
|
{
|
|
|
|
|
if (ps[1] == '/') ps += 2;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if ((ps[1] == 0) || (ps[1] == ':') || isspace(ps[1]))
|
|
|
|
|
ps += 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
pd = *pdest;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Copy the rest of the directory name from the source to the dest. */
|
|
|
|
|
|
|
|
|
|
while ((*ps != '\0') && !isspace(*ps) && (*ps != ':'))
|
|
|
|
|
if (size > 0)
|
|
|
|
|
{
|
|
|
|
|
*pd++ = *ps++;
|
|
|
|
|
size--;
|
|
|
|
|
}
|
|
|
|
|
else ps++;
|
|
|
|
|
*pd = 0;
|
|
|
|
|
*psource = ps;
|
|
|
|
|
*pdest = pd;
|
|
|
|
|
return size;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* ----------------------------------------------------------------------------
|
|
|
|
|
* nextName --
|
|
|
|
|
* This local procedure is used to step through a path, adding a
|
|
|
|
|
* directory name from the path to a file name.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* The return value is a pointer to a path-extended name, or
|
|
|
|
|
* NULL if the end of the path has been reached. If a tilde
|
|
|
|
|
* couldn't be expanded, then a zero-length string is returned.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* The pointer at *ppath is updated to refer to the terminating
|
|
|
|
|
* character of the path entry used this time.
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
|
nextName(ppath, file, dest, size)
|
2024-10-12 09:17:21 +02:00
|
|
|
const char **ppath; /* Pointer to a pointer to the next
|
2017-04-25 14:41:48 +02:00
|
|
|
* entry in the path.
|
|
|
|
|
*/
|
2024-10-12 09:17:21 +02:00
|
|
|
const char *file; /* Pointer to a file name. */
|
2017-04-25 14:41:48 +02:00
|
|
|
char *dest; /* Place to build result name. */
|
|
|
|
|
int size; /* Size of result area. */
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
char *p;
|
|
|
|
|
|
|
|
|
|
/* Don't bother with NULL paths */
|
|
|
|
|
if (*ppath == 0) return NULL;
|
|
|
|
|
|
|
|
|
|
/* Skip leading blanks and colons. Then make sure that there's
|
|
|
|
|
* another entry in the path.
|
|
|
|
|
*/
|
|
|
|
|
while (isspace(**ppath) || (**ppath == ':')) *ppath += 1;
|
|
|
|
|
if (**ppath == 0) return NULL;
|
|
|
|
|
|
|
|
|
|
/* Grab the next directory name and terminate it with a slash if
|
|
|
|
|
* there isn't one there already.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
p = dest;
|
|
|
|
|
dest[size-1] = 0;
|
|
|
|
|
size = PaExpand(ppath, &p, size);
|
|
|
|
|
if (**ppath) *ppath += 1; /* Skip the terminating character. */
|
|
|
|
|
if (size < 0)
|
|
|
|
|
{
|
|
|
|
|
dest[0] = 0;
|
|
|
|
|
return dest;
|
|
|
|
|
}
|
|
|
|
|
if ((p != dest) && (*(p-1) != '/'))
|
|
|
|
|
{
|
|
|
|
|
*p++ = '/';
|
|
|
|
|
size -= 1;
|
|
|
|
|
}
|
|
|
|
|
if (size < strlen(file)) strncpy(p, file, size);
|
|
|
|
|
else strcpy(p, file);
|
|
|
|
|
return dest;
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-02 22:12:46 +01:00
|
|
|
#ifdef HAVE_ZLIB
|
|
|
|
|
|
|
|
|
|
/*-------------------------------------------------------------------
|
|
|
|
|
* PaLockZOpen --
|
|
|
|
|
* This routine does a file lookup using the current path and
|
|
|
|
|
* supplying a default extension.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* A gzFile type, or NULL if the file couldn't be found.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
*-------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
gzFile
|
|
|
|
|
PaLockZOpen(file, mode, ext, path, library, pRealName, is_locked, fdp)
|
2024-10-12 09:36:17 +02:00
|
|
|
const char *file; /* Name of the file to be opened. */
|
|
|
|
|
const char *mode; /* The file mode, as given to fopen. */
|
|
|
|
|
const char *ext; /* The extension to be added to the file name,
|
2022-11-02 22:12:46 +01:00
|
|
|
* or NULL. Note: this string must include
|
|
|
|
|
* the dot (or whatever separator you use).
|
|
|
|
|
*/
|
2024-10-12 09:36:17 +02:00
|
|
|
const char *path; /* A search path: a list of directory names
|
2022-11-02 22:12:46 +01:00
|
|
|
* separated by colons or blanks. To use
|
|
|
|
|
* only the working directory, use "." for
|
|
|
|
|
* the path.
|
|
|
|
|
*/
|
2024-10-12 09:36:17 +02:00
|
|
|
const char *library; /* A 2nd path containing library names. Can be
|
2022-11-02 22:12:46 +01:00
|
|
|
* NULL to indicate no library.
|
|
|
|
|
*/
|
|
|
|
|
char **pRealName; /* Pointer to a location that will be filled
|
|
|
|
|
* in with the address of the real name of
|
|
|
|
|
* the file that was successfully opened.
|
|
|
|
|
* If NULL, then nothing is stored.
|
|
|
|
|
*/
|
|
|
|
|
bool *is_locked; /* Pointer to a location to store the result
|
|
|
|
|
* of the attempt to grab an advisory lock
|
|
|
|
|
* on the file. If NULL, then nothing is
|
|
|
|
|
* stored.
|
|
|
|
|
*/
|
|
|
|
|
int *fdp; /* If non-NULL, put the file descriptor here */
|
|
|
|
|
{
|
2024-10-12 09:36:17 +02:00
|
|
|
char extendedName[MAXSIZE], *p1;
|
|
|
|
|
const char *p2;
|
2022-11-02 22:12:46 +01:00
|
|
|
static char realName[MAXSIZE];
|
|
|
|
|
int length, extLength, i, fd;
|
|
|
|
|
int oflag = 0;
|
|
|
|
|
gzFile f;
|
|
|
|
|
|
|
|
|
|
if (fdp != NULL) *fdp = -1;
|
|
|
|
|
if (file == NULL) return (gzFile) NULL;
|
|
|
|
|
if (file[0] == '\0') return (gzFile) NULL;
|
|
|
|
|
if (pRealName != NULL) (*pRealName) = realName;
|
|
|
|
|
|
|
|
|
|
/* Get equivalent flag for file descriptor mode */
|
|
|
|
|
if (mode[0] == 'r')
|
|
|
|
|
oflag = (mode[1] == '+') ? O_RDWR : O_RDONLY;
|
|
|
|
|
else if (mode[0] == 'w')
|
|
|
|
|
oflag = (mode[1] == '+') ? O_APPEND : O_WRONLY;
|
|
|
|
|
|
|
|
|
|
/* See if we must supply an extension. */
|
|
|
|
|
|
|
|
|
|
length = strlen(file);
|
|
|
|
|
if (length >= MAXSIZE) length = MAXSIZE - 1;
|
|
|
|
|
if (ext != NULL)
|
|
|
|
|
{
|
|
|
|
|
(void) strncpy(extendedName, file, length + 1);
|
|
|
|
|
i = MAXSIZE - 1 - length;
|
|
|
|
|
extLength = strlen(ext);
|
|
|
|
|
if (extLength > i) extLength = i;
|
|
|
|
|
|
|
|
|
|
/* (Modified by Tim, 1/13/2015; assume that "file" has */
|
|
|
|
|
/* the extension already stripped, therefore always add */
|
|
|
|
|
/* the extension if one is specified. This allows the */
|
|
|
|
|
/* code to distinguish between, say, "a.mag" and */
|
|
|
|
|
/* "a.mag.mag".) */
|
|
|
|
|
|
|
|
|
|
/* If the extension is already on the name, don't add it */
|
|
|
|
|
// if ((length < extLength) || ((extLength > 0)
|
|
|
|
|
// && (strcmp(ext, file + length - extLength))))
|
|
|
|
|
|
|
|
|
|
(void) strncpy(&(extendedName[length]), ext, extLength + 1);
|
|
|
|
|
|
|
|
|
|
extendedName[MAXSIZE-1] = '\0';
|
|
|
|
|
file = extendedName;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If the first character of the file name is a tilde or dollar sign,
|
|
|
|
|
* do tilde or environment variable expansion but don't touch a search
|
|
|
|
|
* path.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (file[0] == '~' || file[0] == '$')
|
|
|
|
|
{
|
|
|
|
|
p1 = realName;
|
|
|
|
|
p2 = file;
|
|
|
|
|
if (PaExpand(&p2, &p1, MAXSIZE) < 0) return NULL;
|
|
|
|
|
|
|
|
|
|
#ifdef FILE_LOCKS
|
|
|
|
|
if (FileLocking)
|
|
|
|
|
return flock_zopen(realName, mode, is_locked, fdp);
|
|
|
|
|
#endif
|
2025-02-13 09:23:07 +01:00
|
|
|
return path_gzdopen_internal(realName, oflag, mode, fdp);
|
2022-11-02 22:12:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If we were already given a full rooted file name,
|
|
|
|
|
* or a relative pathname, just use it.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (file[0] == '/'
|
|
|
|
|
|| (file[0] == '.' && (strcmp(file, ".") == 0
|
|
|
|
|
|| strncmp(file, "./", 2) == 0
|
|
|
|
|
|| strcmp(file, "..") == 0
|
|
|
|
|
|| strncmp(file, "../", 3) == 0)))
|
|
|
|
|
{
|
|
|
|
|
(void) strncpy(realName, file, MAXSIZE-1);
|
|
|
|
|
realName[MAXSIZE-1] = '\0';
|
|
|
|
|
|
|
|
|
|
#ifdef FILE_LOCKS
|
|
|
|
|
if (FileLocking)
|
|
|
|
|
return flock_zopen(realName, mode, is_locked, fdp);
|
|
|
|
|
#endif
|
2025-02-13 09:23:07 +01:00
|
|
|
return path_gzdopen_internal(realName, oflag, mode, fdp);
|
2022-11-02 22:12:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Now try going through the path, one entry at a time. */
|
|
|
|
|
|
|
|
|
|
while (nextName(&path, file, realName, MAXSIZE) != NULL)
|
|
|
|
|
{
|
|
|
|
|
if (*realName == 0) continue;
|
|
|
|
|
|
|
|
|
|
#ifdef FILE_LOCKS
|
|
|
|
|
if (FileLocking)
|
|
|
|
|
f = flock_zopen(realName, mode, is_locked, &fd);
|
|
|
|
|
else
|
2025-02-13 09:23:07 +01:00
|
|
|
f = path_gzdopen_internal(realName, oflag, mode, &fd);
|
2022-11-02 22:12:46 +01:00
|
|
|
#else
|
2025-02-13 09:23:07 +01:00
|
|
|
f = path_gzdopen_internal(realName, oflag, mode, &fd);
|
2022-11-02 22:12:46 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (f != NULL)
|
|
|
|
|
{
|
|
|
|
|
if (fdp != NULL) *fdp = fd;
|
|
|
|
|
return f;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If any error other than "file not found" occurred,
|
|
|
|
|
// then halt immediately.
|
|
|
|
|
if (errno != ENOENT) return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* We've tried the path and that didn't work. Now go through
|
|
|
|
|
* the library area, one entry at a time.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (library == NULL) return NULL;
|
|
|
|
|
while (nextName(&library, file, realName, MAXSIZE) != NULL)
|
|
|
|
|
{
|
|
|
|
|
#ifdef FILE_LOCKS
|
|
|
|
|
if (FileLocking)
|
|
|
|
|
f = flock_zopen(realName, mode, is_locked, &fd);
|
|
|
|
|
else
|
2025-02-13 09:23:07 +01:00
|
|
|
f = path_gzdopen_internal(realName, oflag, mode, &fd);
|
2022-11-02 22:12:46 +01:00
|
|
|
#else
|
2025-02-13 09:23:07 +01:00
|
|
|
f = path_gzdopen_internal(realName, oflag, mode, &fd);
|
2022-11-02 22:12:46 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (f != NULL)
|
|
|
|
|
{
|
|
|
|
|
if (fdp != NULL) *fdp = fd;
|
|
|
|
|
return f;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If any error other than "file not found" occurred,
|
|
|
|
|
// then halt immediately.
|
|
|
|
|
if (errno != ENOENT) return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif /* HAVE_ZLIB */
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*-------------------------------------------------------------------
|
|
|
|
|
* PaLockOpen --
|
|
|
|
|
* This routine does a file lookup using the current path and
|
|
|
|
|
* supplying a default extension.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* A pointer to a FILE, or NULL if the file couldn't be found.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
* If ext is specified, then it is tacked onto the end of
|
|
|
|
|
* the given file name. If the first character of the
|
|
|
|
|
* file name is "~" or "/" or if nosearch is TRUE, then we try
|
|
|
|
|
* to look up the file with the original name, doing tilde
|
2020-05-23 23:13:14 +02:00
|
|
|
* expansion of course and returning that result. If none of
|
2017-04-25 14:41:48 +02:00
|
|
|
* these conditions is met, we go through the path trying to
|
|
|
|
|
* look up the file once for each path entry by prepending the
|
|
|
|
|
* path entry to the original file name. This concatenated name
|
|
|
|
|
* is stored in a static string and made available to the caller
|
|
|
|
|
* through prealName if the open succeeds. If the entire path is
|
|
|
|
|
* tried, and still nothing works, then we try each entry in the
|
|
|
|
|
* library path next.
|
|
|
|
|
* Note: the static string will be trashed on the next call to this
|
|
|
|
|
* routine. Also, note that no individual file name is allowed to
|
|
|
|
|
* be more than MAXSIZE characters long. Excess characters are lost.
|
|
|
|
|
*
|
|
|
|
|
* Path Format:
|
|
|
|
|
* A path is a string containing directory names separated by
|
|
|
|
|
* colons or white space. Tilde notation may be used within paths.
|
|
|
|
|
*-------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
FILE *
|
2022-11-02 22:12:46 +01:00
|
|
|
PaLockOpen(file, mode, ext, path, library, pRealName, is_locked, fdp)
|
2024-10-12 09:36:54 +02:00
|
|
|
const char *file; /* Name of the file to be opened. */
|
|
|
|
|
const char *mode; /* The file mode, as given to fopen. */
|
|
|
|
|
const char *ext; /* The extension to be added to the file name,
|
2017-04-25 14:41:48 +02:00
|
|
|
* or NULL. Note: this string must include
|
|
|
|
|
* the dot (or whatever separator you use).
|
|
|
|
|
*/
|
2024-10-12 09:36:54 +02:00
|
|
|
const char *path; /* A search path: a list of directory names
|
2017-04-25 14:41:48 +02:00
|
|
|
* separated by colons or blanks. To use
|
|
|
|
|
* only the working directory, use "." for
|
|
|
|
|
* the path.
|
|
|
|
|
*/
|
2024-10-12 09:36:54 +02:00
|
|
|
const char *library; /* A 2nd path containing library names. Can be
|
2017-04-25 14:41:48 +02:00
|
|
|
* NULL to indicate no library.
|
|
|
|
|
*/
|
|
|
|
|
char **pRealName; /* Pointer to a location that will be filled
|
|
|
|
|
* in with the address of the real name of
|
|
|
|
|
* the file that was successfully opened.
|
|
|
|
|
* If NULL, then nothing is stored.
|
|
|
|
|
*/
|
|
|
|
|
bool *is_locked; /* Pointer to a location to store the result
|
|
|
|
|
* of the attempt to grab an advisory lock
|
|
|
|
|
* on the file. If NULL, then nothing is
|
|
|
|
|
* stored.
|
|
|
|
|
*/
|
2022-11-02 22:12:46 +01:00
|
|
|
int *fdp; /* If non-NULL, put the file descriptor here. */
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2024-10-12 09:36:54 +02:00
|
|
|
char extendedName[MAXSIZE], *p1;
|
|
|
|
|
const char *p2;
|
2017-04-25 14:41:48 +02:00
|
|
|
static char realName[MAXSIZE];
|
|
|
|
|
int length, extLength, i;
|
|
|
|
|
FILE *f;
|
|
|
|
|
|
2022-11-02 22:12:46 +01:00
|
|
|
if (fdp != NULL) *fdp = -1;
|
2017-04-25 14:41:48 +02:00
|
|
|
if (file == NULL) return (FILE *) NULL;
|
|
|
|
|
if (file[0] == '\0') return (FILE *) NULL;
|
|
|
|
|
if (pRealName != NULL) (*pRealName) = realName;
|
|
|
|
|
|
|
|
|
|
/* See if we must supply an extension. */
|
|
|
|
|
|
|
|
|
|
length = strlen(file);
|
|
|
|
|
if (length >= MAXSIZE) length = MAXSIZE - 1;
|
|
|
|
|
if (ext != NULL)
|
|
|
|
|
{
|
|
|
|
|
(void) strncpy(extendedName, file, length + 1);
|
|
|
|
|
i = MAXSIZE - 1 - length;
|
|
|
|
|
extLength = strlen(ext);
|
|
|
|
|
if (extLength > i) extLength = i;
|
|
|
|
|
|
|
|
|
|
/* (Modified by Tim, 1/13/2015; assume that "file" has */
|
|
|
|
|
/* the extension already stripped, therefore always add */
|
|
|
|
|
/* the extension if one is specified. This allows the */
|
|
|
|
|
/* code to distinguish between, say, "a.mag" and */
|
|
|
|
|
/* "a.mag.mag".) */
|
|
|
|
|
|
|
|
|
|
/* If the extension is already on the name, don't add it */
|
|
|
|
|
// if ((length < extLength) || ((extLength > 0)
|
|
|
|
|
// && (strcmp(ext, file + length - extLength))))
|
|
|
|
|
|
|
|
|
|
(void) strncpy(&(extendedName[length]), ext, extLength + 1);
|
|
|
|
|
|
|
|
|
|
extendedName[MAXSIZE-1] = '\0';
|
|
|
|
|
file = extendedName;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If the first character of the file name is a tilde or dollar sign,
|
|
|
|
|
* do tilde or environment variable expansion but don't touch a search
|
|
|
|
|
* path.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (file[0] == '~' || file[0] == '$')
|
|
|
|
|
{
|
|
|
|
|
p1 = realName;
|
|
|
|
|
p2 = file;
|
|
|
|
|
if (PaExpand(&p2, &p1, MAXSIZE) < 0) return NULL;
|
|
|
|
|
|
Played around with the file locking and discovered to my chagrin that
whenever a process writes a cell to disk, it immediately releases the
file lock it had on that cell, which is clearly not the intent of file
locking. Fixed this issue. On a related topic, revised the "cellname
writeable" command so that it can make a cell editable even if the cell
has an advisory lock and cannot be made writeable. Perhaps there should
be a clearer distinction here between "writeable" and "editable". Also:
Reconsidered the previous commit, which removed the "--disable-locking"
from the configuration options. Because some operating systems may not
implement fnctl()-based file locking (Cygwin, for one, apparently doesn't),
it is still useful to be able to completely remove the function, in case
the operating system will fail to recognize the fnctl() values in the
code. Now, file locking behavior can be permanently removed through the
configuration option, or temporarily disabled from the command line.
2022-01-01 22:53:46 +01:00
|
|
|
#ifdef FILE_LOCKS
|
2022-01-01 20:28:59 +01:00
|
|
|
if (FileLocking)
|
2022-11-02 22:12:46 +01:00
|
|
|
f = flock_open(realName, mode, is_locked, NULL);
|
2022-01-01 20:28:59 +01:00
|
|
|
else
|
Played around with the file locking and discovered to my chagrin that
whenever a process writes a cell to disk, it immediately releases the
file lock it had on that cell, which is clearly not the intent of file
locking. Fixed this issue. On a related topic, revised the "cellname
writeable" command so that it can make a cell editable even if the cell
has an advisory lock and cannot be made writeable. Perhaps there should
be a clearer distinction here between "writeable" and "editable". Also:
Reconsidered the previous commit, which removed the "--disable-locking"
from the configuration options. Because some operating systems may not
implement fnctl()-based file locking (Cygwin, for one, apparently doesn't),
it is still useful to be able to completely remove the function, in case
the operating system will fail to recognize the fnctl() values in the
code. Now, file locking behavior can be permanently removed through the
configuration option, or temporarily disabled from the command line.
2022-01-01 22:53:46 +01:00
|
|
|
#endif
|
2022-11-02 22:12:46 +01:00
|
|
|
f = fopen(realName, mode);
|
|
|
|
|
|
|
|
|
|
if ((fdp != NULL) && (f != NULL)) *fdp = fileno(f);
|
|
|
|
|
return f;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If we were already given a full rooted file name,
|
|
|
|
|
* or a relative pathname, just use it.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (file[0] == '/'
|
|
|
|
|
|| (file[0] == '.' && (strcmp(file, ".") == 0
|
|
|
|
|
|| strncmp(file, "./", 2) == 0
|
|
|
|
|
|| strcmp(file, "..") == 0
|
|
|
|
|
|| strncmp(file, "../", 3) == 0)))
|
|
|
|
|
{
|
|
|
|
|
(void) strncpy(realName, file, MAXSIZE-1);
|
|
|
|
|
realName[MAXSIZE-1] = '\0';
|
2022-01-01 20:28:59 +01:00
|
|
|
|
Played around with the file locking and discovered to my chagrin that
whenever a process writes a cell to disk, it immediately releases the
file lock it had on that cell, which is clearly not the intent of file
locking. Fixed this issue. On a related topic, revised the "cellname
writeable" command so that it can make a cell editable even if the cell
has an advisory lock and cannot be made writeable. Perhaps there should
be a clearer distinction here between "writeable" and "editable". Also:
Reconsidered the previous commit, which removed the "--disable-locking"
from the configuration options. Because some operating systems may not
implement fnctl()-based file locking (Cygwin, for one, apparently doesn't),
it is still useful to be able to completely remove the function, in case
the operating system will fail to recognize the fnctl() values in the
code. Now, file locking behavior can be permanently removed through the
configuration option, or temporarily disabled from the command line.
2022-01-01 22:53:46 +01:00
|
|
|
#ifdef FILE_LOCKS
|
2022-01-01 20:28:59 +01:00
|
|
|
if (FileLocking)
|
2022-11-02 22:12:46 +01:00
|
|
|
f = flock_open(realName, mode, is_locked, NULL);
|
2022-01-01 20:28:59 +01:00
|
|
|
else
|
Played around with the file locking and discovered to my chagrin that
whenever a process writes a cell to disk, it immediately releases the
file lock it had on that cell, which is clearly not the intent of file
locking. Fixed this issue. On a related topic, revised the "cellname
writeable" command so that it can make a cell editable even if the cell
has an advisory lock and cannot be made writeable. Perhaps there should
be a clearer distinction here between "writeable" and "editable". Also:
Reconsidered the previous commit, which removed the "--disable-locking"
from the configuration options. Because some operating systems may not
implement fnctl()-based file locking (Cygwin, for one, apparently doesn't),
it is still useful to be able to completely remove the function, in case
the operating system will fail to recognize the fnctl() values in the
code. Now, file locking behavior can be permanently removed through the
configuration option, or temporarily disabled from the command line.
2022-01-01 22:53:46 +01:00
|
|
|
#endif
|
2022-11-02 22:12:46 +01:00
|
|
|
f = fopen(realName, mode);
|
|
|
|
|
|
2024-08-16 23:45:10 +02:00
|
|
|
if ((fdp != NULL) && (f != NULL)) *fdp = fileno(f);
|
|
|
|
|
if ((f != NULL) || (file[0] == '/'))
|
|
|
|
|
return f;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Now try going through the path, one entry at a time. */
|
|
|
|
|
|
|
|
|
|
while (nextName(&path, file, realName, MAXSIZE) != NULL)
|
|
|
|
|
{
|
|
|
|
|
if (*realName == 0) continue;
|
2022-01-01 20:28:59 +01:00
|
|
|
|
Played around with the file locking and discovered to my chagrin that
whenever a process writes a cell to disk, it immediately releases the
file lock it had on that cell, which is clearly not the intent of file
locking. Fixed this issue. On a related topic, revised the "cellname
writeable" command so that it can make a cell editable even if the cell
has an advisory lock and cannot be made writeable. Perhaps there should
be a clearer distinction here between "writeable" and "editable". Also:
Reconsidered the previous commit, which removed the "--disable-locking"
from the configuration options. Because some operating systems may not
implement fnctl()-based file locking (Cygwin, for one, apparently doesn't),
it is still useful to be able to completely remove the function, in case
the operating system will fail to recognize the fnctl() values in the
code. Now, file locking behavior can be permanently removed through the
configuration option, or temporarily disabled from the command line.
2022-01-01 22:53:46 +01:00
|
|
|
#ifdef FILE_LOCKS
|
2022-01-01 20:28:59 +01:00
|
|
|
if (FileLocking)
|
2022-11-02 22:12:46 +01:00
|
|
|
f = flock_open(realName, mode, is_locked, NULL);
|
2022-01-01 20:28:59 +01:00
|
|
|
else
|
Played around with the file locking and discovered to my chagrin that
whenever a process writes a cell to disk, it immediately releases the
file lock it had on that cell, which is clearly not the intent of file
locking. Fixed this issue. On a related topic, revised the "cellname
writeable" command so that it can make a cell editable even if the cell
has an advisory lock and cannot be made writeable. Perhaps there should
be a clearer distinction here between "writeable" and "editable". Also:
Reconsidered the previous commit, which removed the "--disable-locking"
from the configuration options. Because some operating systems may not
implement fnctl()-based file locking (Cygwin, for one, apparently doesn't),
it is still useful to be able to completely remove the function, in case
the operating system will fail to recognize the fnctl() values in the
code. Now, file locking behavior can be permanently removed through the
configuration option, or temporarily disabled from the command line.
2022-01-01 22:53:46 +01:00
|
|
|
#endif
|
2022-01-01 20:28:59 +01:00
|
|
|
f = fopen(realName, mode);
|
|
|
|
|
|
2022-11-02 22:12:46 +01:00
|
|
|
if (f != NULL)
|
|
|
|
|
{
|
|
|
|
|
if (fdp != NULL) *fdp = fileno(f);
|
|
|
|
|
return f;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
// If any error other than "file not found" occurred,
|
|
|
|
|
// then halt immediately.
|
|
|
|
|
if (errno != ENOENT) return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* We've tried the path and that didn't work. Now go through
|
|
|
|
|
* the library area, one entry at a time.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (library == NULL) return NULL;
|
|
|
|
|
while (nextName(&library, file, realName, MAXSIZE) != NULL)
|
|
|
|
|
{
|
Played around with the file locking and discovered to my chagrin that
whenever a process writes a cell to disk, it immediately releases the
file lock it had on that cell, which is clearly not the intent of file
locking. Fixed this issue. On a related topic, revised the "cellname
writeable" command so that it can make a cell editable even if the cell
has an advisory lock and cannot be made writeable. Perhaps there should
be a clearer distinction here between "writeable" and "editable". Also:
Reconsidered the previous commit, which removed the "--disable-locking"
from the configuration options. Because some operating systems may not
implement fnctl()-based file locking (Cygwin, for one, apparently doesn't),
it is still useful to be able to completely remove the function, in case
the operating system will fail to recognize the fnctl() values in the
code. Now, file locking behavior can be permanently removed through the
configuration option, or temporarily disabled from the command line.
2022-01-01 22:53:46 +01:00
|
|
|
#ifdef FILE_LOCKS
|
2022-01-01 20:28:59 +01:00
|
|
|
if (FileLocking)
|
2022-11-02 22:12:46 +01:00
|
|
|
f = flock_open(realName, mode, is_locked, NULL);
|
2022-01-01 20:28:59 +01:00
|
|
|
else
|
Played around with the file locking and discovered to my chagrin that
whenever a process writes a cell to disk, it immediately releases the
file lock it had on that cell, which is clearly not the intent of file
locking. Fixed this issue. On a related topic, revised the "cellname
writeable" command so that it can make a cell editable even if the cell
has an advisory lock and cannot be made writeable. Perhaps there should
be a clearer distinction here between "writeable" and "editable". Also:
Reconsidered the previous commit, which removed the "--disable-locking"
from the configuration options. Because some operating systems may not
implement fnctl()-based file locking (Cygwin, for one, apparently doesn't),
it is still useful to be able to completely remove the function, in case
the operating system will fail to recognize the fnctl() values in the
code. Now, file locking behavior can be permanently removed through the
configuration option, or temporarily disabled from the command line.
2022-01-01 22:53:46 +01:00
|
|
|
#endif
|
2022-01-01 20:28:59 +01:00
|
|
|
f = fopen(realName, mode);
|
|
|
|
|
|
2022-11-02 22:12:46 +01:00
|
|
|
if (f != NULL)
|
|
|
|
|
{
|
|
|
|
|
if (fdp != NULL) *fdp = fileno(f);
|
|
|
|
|
return f;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
// If any error other than "file not found" occurred,
|
|
|
|
|
// then halt immediately.
|
|
|
|
|
if (errno != ENOENT) return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-10 15:19:39 +02:00
|
|
|
#ifdef HAVE_ZLIB
|
|
|
|
|
|
|
|
|
|
/*-------------------------------------------------------------------
|
|
|
|
|
* PaZOpen --
|
|
|
|
|
* This routine does a file lookup using the current path and
|
|
|
|
|
* supplying a default extension. The return type is a Zlib-
|
|
|
|
|
* type compressed stream.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* A gzFile type, or NULL if the file couldn't be found.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
* See notes for PaLockOpen() for handling of extensions.
|
|
|
|
|
*
|
|
|
|
|
* Path Format:
|
|
|
|
|
* A path is a string containing directory names separated by
|
|
|
|
|
* colons or white space. Tilde notation may be used within paths.
|
|
|
|
|
*-------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
gzFile
|
|
|
|
|
PaZOpen(file, mode, ext, path, library, pRealName)
|
2024-10-12 09:37:21 +02:00
|
|
|
const char *file; /* Name of the file to be opened. */
|
|
|
|
|
const char *mode; /* The file mode, as given to gzopen. */
|
|
|
|
|
const char *ext; /* The extension to be added to the file name,
|
2022-05-10 15:19:39 +02:00
|
|
|
* or NULL. Note: this string must include
|
|
|
|
|
* the dot (or whatever separator you use).
|
|
|
|
|
*/
|
2024-10-12 09:37:21 +02:00
|
|
|
const char *path; /* A search path: a list of directory names
|
2022-05-10 15:19:39 +02:00
|
|
|
* separated by colons or blanks. To use
|
|
|
|
|
* only the working directory, use "." for
|
|
|
|
|
* the path.
|
|
|
|
|
*/
|
2024-10-12 09:37:21 +02:00
|
|
|
const char *library; /* A 2nd path containing library names. Can be
|
2022-05-10 15:19:39 +02:00
|
|
|
* NULL to indicate no library.
|
|
|
|
|
*/
|
|
|
|
|
char **pRealName; /* Pointer to a location that will be filled
|
|
|
|
|
* in with the address of the real name of
|
|
|
|
|
* the file that was successfully opened.
|
|
|
|
|
* If NULL, then nothing is stored.
|
|
|
|
|
*/
|
|
|
|
|
{
|
2024-10-12 09:37:21 +02:00
|
|
|
char extendedName[MAXSIZE], *p1;
|
|
|
|
|
const char *p2;
|
2022-05-10 15:19:39 +02:00
|
|
|
static char realName[MAXSIZE];
|
|
|
|
|
int length, extLength, i;
|
|
|
|
|
gzFile f;
|
|
|
|
|
|
|
|
|
|
if (file == NULL) return (gzFile) NULL;
|
|
|
|
|
if (file[0] == '\0') return (gzFile) NULL;
|
|
|
|
|
if (pRealName != NULL) (*pRealName) = realName;
|
|
|
|
|
|
|
|
|
|
/* See if we must supply an extension. */
|
|
|
|
|
|
|
|
|
|
length = strlen(file);
|
|
|
|
|
if (length >= MAXSIZE) length = MAXSIZE - 1;
|
|
|
|
|
if (ext != NULL)
|
|
|
|
|
{
|
|
|
|
|
(void) strncpy(extendedName, file, length + 1);
|
|
|
|
|
i = MAXSIZE - 1 - length;
|
|
|
|
|
extLength = strlen(ext);
|
|
|
|
|
if (extLength > i) extLength = i;
|
|
|
|
|
|
|
|
|
|
(void) strncpy(&(extendedName[length]), ext, extLength + 1);
|
|
|
|
|
|
|
|
|
|
extendedName[MAXSIZE-1] = '\0';
|
|
|
|
|
file = extendedName;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If the first character of the file name is a tilde or dollar sign,
|
|
|
|
|
* do tilde or environment variable expansion but don't touch a search
|
|
|
|
|
* path.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (file[0] == '~' || file[0] == '$')
|
|
|
|
|
{
|
|
|
|
|
p1 = realName;
|
|
|
|
|
p2 = file;
|
|
|
|
|
if (PaExpand(&p2, &p1, MAXSIZE) < 0) return NULL;
|
|
|
|
|
return gzopen(realName, mode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If we were already given a full rooted file name,
|
|
|
|
|
* or a relative pathname, just use it.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (file[0] == '/'
|
|
|
|
|
|| (file[0] == '.' && (strcmp(file, ".") == 0
|
|
|
|
|
|| strncmp(file, "./", 2) == 0
|
|
|
|
|
|| strcmp(file, "..") == 0
|
|
|
|
|
|| strncmp(file, "../", 3) == 0)))
|
|
|
|
|
{
|
2024-08-16 23:45:10 +02:00
|
|
|
gzFile result;
|
2022-05-10 15:19:39 +02:00
|
|
|
(void) strncpy(realName, file, MAXSIZE-1);
|
|
|
|
|
realName[MAXSIZE-1] = '\0';
|
2024-08-16 23:45:10 +02:00
|
|
|
|
|
|
|
|
/* For full paths, halt immediately if not found. Otherwise,
|
|
|
|
|
* treat the path as relative to something in the search path.
|
|
|
|
|
*/
|
|
|
|
|
result = gzopen(realName, mode);
|
|
|
|
|
if ((result != NULL) || (file[0] == '/')) return result;
|
2022-05-10 15:19:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Now try going through the path, one entry at a time. */
|
|
|
|
|
|
|
|
|
|
while (nextName(&path, file, realName, MAXSIZE) != NULL)
|
|
|
|
|
{
|
|
|
|
|
if (*realName == 0) continue;
|
|
|
|
|
f = gzopen(realName, mode);
|
|
|
|
|
|
|
|
|
|
if (f != NULL) return f;
|
|
|
|
|
|
|
|
|
|
// If any error other than "file not found" occurred,
|
|
|
|
|
// then halt immediately.
|
|
|
|
|
if (errno != ENOENT) return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* We've tried the path and that didn't work. Now go through
|
|
|
|
|
* the library area, one entry at a time.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (library == NULL) return NULL;
|
|
|
|
|
while (nextName(&library, file, realName, MAXSIZE) != NULL)
|
|
|
|
|
{
|
|
|
|
|
f = gzopen(realName, mode);
|
|
|
|
|
|
|
|
|
|
if (f != NULL) return f;
|
|
|
|
|
|
|
|
|
|
// If any error other than "file not found" occurred,
|
|
|
|
|
// then halt immediately.
|
|
|
|
|
if (errno != ENOENT) return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif /* HAVE_ZLIB */
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
*-------------------------------------------------------------------
|
|
|
|
|
* PaOpen --
|
|
|
|
|
* This is a wrapper for PaLockOpen() that is backwardly-
|
|
|
|
|
* compatible with the original (non-file-locking) PaOpen(),
|
|
|
|
|
* and is here because it's a pain to add an extra argument
|
|
|
|
|
* to every call to PaOpen() when only .mag files are locked.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* See PaLockOpen()
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
* See PaLockOpen()
|
|
|
|
|
*
|
|
|
|
|
*-------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
FILE *
|
|
|
|
|
PaOpen(file, mode, ext, path, library, pRealName)
|
2024-10-12 09:37:41 +02:00
|
|
|
const char *file; /* Name of the file to be opened. */
|
|
|
|
|
const char *mode; /* The file mode, as given to fopen. */
|
|
|
|
|
const char *ext; /* The extension to be added to the file name,
|
2017-04-25 14:41:48 +02:00
|
|
|
* or NULL. Note: this string must include
|
|
|
|
|
* the dot (or whatever separator you use).
|
|
|
|
|
*/
|
2024-10-12 09:37:41 +02:00
|
|
|
const char *path; /* A search path: a list of directory names
|
2017-04-25 14:41:48 +02:00
|
|
|
* separated by colons or blanks. To use
|
|
|
|
|
* only the working directory, use "." for
|
|
|
|
|
* the path.
|
|
|
|
|
*/
|
2024-10-12 09:37:41 +02:00
|
|
|
const char *library; /* A 2nd path containing library names. Can be
|
2017-04-25 14:41:48 +02:00
|
|
|
* NULL to indicate no library.
|
|
|
|
|
*/
|
|
|
|
|
char **pRealName; /* Pointer to a location that will be filled
|
|
|
|
|
* in with the address of the real name of
|
|
|
|
|
* the file that was successfully opened.
|
|
|
|
|
* If NULL, then nothing is stored.
|
|
|
|
|
*/
|
|
|
|
|
{
|
2022-11-02 22:12:46 +01:00
|
|
|
return PaLockOpen(file, mode, ext, path, library, pRealName, NULL, NULL);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
* PaSubsWD --
|
|
|
|
|
*
|
|
|
|
|
* Replaces all uses of the working directory in a path
|
|
|
|
|
* by some fixed directory.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* The return result is a path that is just like the path
|
|
|
|
|
* argument except that every implicit or explicit use of
|
|
|
|
|
* the working directory is replaced by the newWD argument.
|
|
|
|
|
* The result is a static array, which will be trashed on
|
|
|
|
|
* the next call to this procedure.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* None.
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
|
PaSubsWD(path, newWD)
|
2024-10-12 09:43:01 +02:00
|
|
|
const char *path; /* Path in which to substitute. */
|
|
|
|
|
const char *newWD; /* New working directory to be used. Must
|
2017-04-25 14:41:48 +02:00
|
|
|
* end in a slash.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
#define NEWPATHSIZE 1000
|
|
|
|
|
static char newPath[NEWPATHSIZE];
|
2024-10-12 09:43:01 +02:00
|
|
|
const char *pOld;
|
|
|
|
|
char *pNew;
|
|
|
|
|
const char *pWD;
|
2017-04-25 14:41:48 +02:00
|
|
|
int spaceLeft;
|
|
|
|
|
|
|
|
|
|
pOld = path;
|
|
|
|
|
pNew = newPath;
|
|
|
|
|
spaceLeft = NEWPATHSIZE;
|
|
|
|
|
|
|
|
|
|
while (TRUE)
|
|
|
|
|
{
|
|
|
|
|
/* Scan through the old path, copying separators to the new
|
|
|
|
|
* path until getting the first character of the directory.
|
|
|
|
|
*/
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
while (isspace(*pOld) || (*pOld == ':'))
|
|
|
|
|
{
|
|
|
|
|
if (spaceLeft <= 0) goto subsDone;
|
|
|
|
|
spaceLeft -= 1;
|
|
|
|
|
*pNew++ = *pOld++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If the first character of the directory isn't a "/", "$", or "~",
|
|
|
|
|
* then add in the new directory name in front of it in newPath.
|
|
|
|
|
*/
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
if ((*pOld != '/') && (*pOld != '~') && (*pOld != '$') && (*pOld != 0))
|
|
|
|
|
{
|
|
|
|
|
pWD = newWD;
|
|
|
|
|
while (*pWD != 0)
|
|
|
|
|
{
|
|
|
|
|
if (spaceLeft <= 0) goto subsDone;
|
|
|
|
|
spaceLeft -= 1;
|
|
|
|
|
*pNew++ = *pWD++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Add in the rest of the path entry from the old path to the new. */
|
|
|
|
|
|
|
|
|
|
while ((!isspace(*pOld)) && (*pOld != ':') && (*pOld != 0))
|
|
|
|
|
{
|
|
|
|
|
if (spaceLeft <= 0) goto subsDone;
|
|
|
|
|
spaceLeft -= 1;
|
|
|
|
|
*pNew++ = *pOld++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* See if we're done. */
|
|
|
|
|
|
|
|
|
|
if (*pOld == 0) break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
subsDone: if (spaceLeft > 0) *pNew = 0;
|
|
|
|
|
else newPath[NEWPATHSIZE-1] = 0;
|
|
|
|
|
return newPath;
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
* PaEnum --
|
|
|
|
|
*
|
|
|
|
|
* Call a client procedure with each directory in a path
|
|
|
|
|
* prepended to a filename. The client procedure is as
|
|
|
|
|
* follows:
|
|
|
|
|
*
|
|
|
|
|
* int
|
|
|
|
|
* (*proc)(name, cdata)
|
|
|
|
|
* char *name; /# A directory in the path prepended to
|
|
|
|
|
* # a file name.
|
|
|
|
|
* #/
|
|
|
|
|
* ClientData *cdata; /# Provided by caller #/
|
|
|
|
|
* {
|
|
|
|
|
* }
|
|
|
|
|
*
|
|
|
|
|
* The client procedure should return 0 normally, or 1 to abort
|
|
|
|
|
* the path enumeration. If a directory in the search path
|
|
|
|
|
* refers to a non-existent user name (using the ~user syntax),
|
|
|
|
|
* we skip that component.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* Returns 0 if all the clients returned 0, or 1 if
|
|
|
|
|
* some client returned 1. When a client returns 1
|
|
|
|
|
* we abort the enumeration.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Calls the client procedure.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
PaEnum(path, file, proc, cdata)
|
2024-10-12 09:43:34 +02:00
|
|
|
const char *path; /* Search path */
|
|
|
|
|
const char *file; /* Each element of the search path is prepended to
|
2017-04-25 14:41:48 +02:00
|
|
|
* this file name and passed to the client.
|
|
|
|
|
*/
|
|
|
|
|
int (*proc)(); /* Client procedure */
|
|
|
|
|
ClientData cdata; /* Passed to (*proc)() */
|
|
|
|
|
{
|
|
|
|
|
char component[MAXSIZE], *next;
|
|
|
|
|
|
2024-10-04 18:21:15 +02:00
|
|
|
while ((next = nextName(&path, file, component, sizeof component)))
|
2017-04-25 14:41:48 +02:00
|
|
|
if (next[0] && (*proc)(next, cdata))
|
|
|
|
|
return (1);
|
|
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
|
}
|