1098 lines
26 KiB
C
1098 lines
26 KiB
C
/*
|
|
* Copyright (c) 2001 by The XFree86 Project, Inc.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
|
|
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*
|
|
* Except as contained in this notice, the name of the XFree86 Project shall
|
|
* not be used in advertising or otherwise to promote the sale, use or other
|
|
* dealings in this Software without prior written authorization from the
|
|
* XFree86 Project.
|
|
*
|
|
* Author: Paulo César Pereira de Andrade
|
|
*/
|
|
|
|
/* $XFree86: xc/programs/xedit/lisp/pathname.c,v 1.17tsi Exp $ */
|
|
|
|
#include <stdio.h> /* including dirent.h first may cause problems */
|
|
#include <sys/types.h>
|
|
#include <dirent.h>
|
|
#include <errno.h>
|
|
#include <sys/stat.h>
|
|
#include "lisp/pathname.h"
|
|
#include "lisp/private.h"
|
|
|
|
#define NOREAD_SKIP 0
|
|
#define NOREAD_ERROR 1
|
|
|
|
/*
|
|
* Initialization
|
|
*/
|
|
LispObj *Oparse_namestring, *Kerror, *Kabsolute, *Krelative, *Kskip;
|
|
|
|
/*
|
|
* Implementation
|
|
*/
|
|
void
|
|
LispPathnameInit(void)
|
|
{
|
|
Kerror = KEYWORD("ERROR");
|
|
Oparse_namestring = STATIC_ATOM("PARSE-NAMESTRING");
|
|
Kabsolute = KEYWORD("ABSOLUTE");
|
|
Krelative = KEYWORD("RELATIVE");
|
|
}
|
|
|
|
static int
|
|
glob_match(char *cmp1, char *cmp2)
|
|
/*
|
|
* Note: this code was written from scratch, and may generate incorrect
|
|
* results for very complex glob masks.
|
|
*/
|
|
{
|
|
for (;;) {
|
|
while (*cmp1 && *cmp1 == *cmp2) {
|
|
++cmp1;
|
|
++cmp2;
|
|
}
|
|
if (*cmp2) {
|
|
if (*cmp1 == '*') {
|
|
while (*cmp1 == '*')
|
|
++cmp1;
|
|
if (*cmp1) {
|
|
int count = 0, settmp = 1;
|
|
char *tmp = cmp2, *sav2;
|
|
|
|
while (*cmp1 && *cmp1 == '?') {
|
|
++cmp1;
|
|
++count;
|
|
}
|
|
|
|
/* need to recurse here to make sure
|
|
* all cases are tested.
|
|
*/
|
|
while (*cmp2 && *cmp2 != *cmp1)
|
|
++cmp2;
|
|
if (!*cmp1 && cmp2 - tmp < count)
|
|
return (0);
|
|
sav2 = cmp2;
|
|
|
|
/* if recursive calls fails, make sure all '?'
|
|
* following '*' are processed */
|
|
while (*sav2 && sav2 - tmp < count)
|
|
++sav2;
|
|
|
|
for (; *cmp2;) {
|
|
if (settmp) /* repeated letters: *?o? => boot, root */
|
|
tmp = cmp2;
|
|
else
|
|
settmp = 1;
|
|
while (*cmp2 && *cmp2 != *cmp1)
|
|
++cmp2;
|
|
if (cmp2 - tmp < count) {
|
|
if (*cmp2)
|
|
++cmp2;
|
|
settmp = 0;
|
|
continue;
|
|
}
|
|
if (*cmp2) {
|
|
if (glob_match(cmp1, cmp2))
|
|
return (1);
|
|
++cmp2;
|
|
}
|
|
}
|
|
cmp2 = sav2;
|
|
}
|
|
else {
|
|
while (*cmp2)
|
|
++cmp2;
|
|
break;
|
|
}
|
|
}
|
|
else if (*cmp1 == '?') {
|
|
while (*cmp1 == '?' && *cmp2) {
|
|
++cmp1;
|
|
++cmp2;
|
|
}
|
|
continue;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
else {
|
|
while (*cmp1 == '*')
|
|
++cmp1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return (*cmp1 == '\0' && *cmp2 == '\0');
|
|
}
|
|
|
|
/*
|
|
* Since directory is a function to be extended by the implementation,
|
|
* current extensions are:
|
|
* all => list files and directories
|
|
* it is an error to call
|
|
* (directory "<pathname-spec>/" :all t)
|
|
* if non nil, it is like the shell command
|
|
* echo <pathname-spec>, but normally, not in the
|
|
* same order, as the code does not sort the result.
|
|
* !=nil => list files and directories
|
|
* (default) nil => list only files, or only directories if
|
|
* <pathname-spec> ends with PATH_SEP char.
|
|
* if-cannot-read => if opendir fails on a directory
|
|
* :error => generate an error
|
|
* (default) :skip => skip search in this directory
|
|
*/
|
|
LispObj *
|
|
Lisp_Directory(LispBuiltin *builtin)
|
|
/*
|
|
directory pathname &key all if-cannot-read
|
|
*/
|
|
{
|
|
GC_ENTER();
|
|
DIR *dir;
|
|
struct stat st;
|
|
struct dirent *ent;
|
|
int length, listdirs, i, ndirs, nmatches;
|
|
char name[PATH_MAX + 1], path[PATH_MAX + 2], directory[PATH_MAX + 2];
|
|
char *sep, *base, *ptr, **dirs, **matches,
|
|
dot[] = {'.', PATH_SEP, '\0'},
|
|
dotdot[] = {'.', '.', PATH_SEP, '\0'};
|
|
int cannot_read;
|
|
|
|
LispObj *pathname, *all, *if_cannot_read, *result, *cons, *object;
|
|
|
|
if_cannot_read = ARGUMENT(2);
|
|
all = ARGUMENT(1);
|
|
pathname = ARGUMENT(0);
|
|
result = NIL;
|
|
|
|
cons = NIL;
|
|
|
|
if (if_cannot_read != UNSPEC) {
|
|
if (!KEYWORDP(if_cannot_read) ||
|
|
(if_cannot_read != Kskip &&
|
|
if_cannot_read != Kerror))
|
|
LispDestroy("%s: bad :IF-CANNOT-READ %s",
|
|
STRFUN(builtin), STROBJ(if_cannot_read));
|
|
if (if_cannot_read != Kskip)
|
|
cannot_read = NOREAD_SKIP;
|
|
else
|
|
cannot_read = NOREAD_ERROR;
|
|
}
|
|
else
|
|
cannot_read = NOREAD_SKIP;
|
|
|
|
if (PATHNAMEP(pathname))
|
|
pathname = CAR(pathname->data.pathname);
|
|
else if (STREAMP(pathname) && pathname->data.stream.type == LispStreamFile)
|
|
pathname = CAR(pathname->data.stream.pathname->data.pathname);
|
|
else if (!STRINGP(pathname))
|
|
LispDestroy("%s: %s is not a pathname",
|
|
STRFUN(builtin), STROBJ(pathname));
|
|
|
|
strncpy(name, THESTR(pathname), sizeof(name) - 1);
|
|
name[sizeof(name) - 1] = '\0';
|
|
length = strlen(name);
|
|
if (length < STRLEN(pathname))
|
|
LispDestroy("%s: pathname too long %s",
|
|
STRFUN(builtin), name);
|
|
|
|
if (length == 0) {
|
|
if (getcwd(path, sizeof(path) - 2) == NULL)
|
|
LispDestroy("%s: getcwd(): %s", STRFUN(builtin), strerror(errno));
|
|
length = strlen(path);
|
|
if (!length || path[length - 1] != PATH_SEP) {
|
|
path[length++] = PATH_SEP;
|
|
path[length] = '\0';
|
|
}
|
|
result = APPLY1(Oparse_namestring, LSTRING(path, length));
|
|
GC_LEAVE();
|
|
|
|
return (result);
|
|
}
|
|
|
|
if (name[length - 1] == PATH_SEP) {
|
|
listdirs = 1;
|
|
if (length > 1) {
|
|
--length;
|
|
name[length] = '\0';
|
|
}
|
|
}
|
|
else
|
|
listdirs = 0;
|
|
|
|
if (name[0] != PATH_SEP) {
|
|
if (getcwd(path, sizeof(path) - 2) == NULL)
|
|
LispDestroy("%s: getcwd(): %s", STRFUN(builtin), strerror(errno));
|
|
length = strlen(path);
|
|
if (!length || path[length - 1] != PATH_SEP) {
|
|
path[length++] = PATH_SEP;
|
|
path[length] = '\0';
|
|
}
|
|
}
|
|
else
|
|
path[0] = '\0';
|
|
|
|
result = NIL;
|
|
|
|
/* list intermediate directories */
|
|
matches = NULL;
|
|
nmatches = 0;
|
|
dirs = LispMalloc(sizeof(char*));
|
|
ndirs = 1;
|
|
if (snprintf(directory, sizeof(directory), "%s%s%c",
|
|
path, name, PATH_SEP) > PATH_MAX)
|
|
LispDestroy("%s: pathname too long %s", STRFUN(builtin), directory);
|
|
|
|
/* Remove ../ */
|
|
sep = directory;
|
|
for (sep = strstr(sep, dotdot); sep; sep = strstr(sep, dotdot)) {
|
|
if (sep <= directory + 1)
|
|
strcpy(directory, sep + 2);
|
|
else if (sep[-1] == PATH_SEP) {
|
|
for (base = sep - 2; base > directory; base--)
|
|
if (*base == PATH_SEP)
|
|
break;
|
|
strcpy(base, sep + 2);
|
|
sep = base;
|
|
}
|
|
else
|
|
++sep;
|
|
}
|
|
|
|
/* Remove "./" */
|
|
sep = directory;
|
|
for (sep = strstr(sep, dot); sep; sep = strstr(sep, dot)) {
|
|
if (sep == directory || sep[-1] == PATH_SEP)
|
|
strcpy(sep, sep + 2);
|
|
else
|
|
++sep;
|
|
}
|
|
|
|
/* This will happen when there are too many '../' in the path */
|
|
if (directory[1] == '\0') {
|
|
directory[1] = PATH_SEP;
|
|
directory[2] = '\0';
|
|
}
|
|
|
|
base = directory;
|
|
sep = strchr(base + 1, PATH_SEP);
|
|
dirs[0] = LispMalloc(2);
|
|
dirs[0][0] = PATH_SEP;
|
|
dirs[0][1] = '\0';
|
|
|
|
for (base = directory + 1, sep = strchr(base, PATH_SEP); ;
|
|
base = sep + 1, sep = strchr(base, PATH_SEP)) {
|
|
*sep = '\0';
|
|
if (sep[1] == '\0')
|
|
sep = NULL;
|
|
length = strlen(base);
|
|
if (length == 0) {
|
|
if (sep)
|
|
*sep = PATH_SEP;
|
|
else
|
|
break;
|
|
continue;
|
|
}
|
|
|
|
for (i = 0; i < ndirs; i++) {
|
|
length = strlen(dirs[i]);
|
|
if (length > 1)
|
|
dirs[i][length - 1] = '\0'; /* remove trailing / */
|
|
if ((dir = opendir(dirs[i])) != NULL) {
|
|
(void)readdir(dir); /* "." */
|
|
(void)readdir(dir); /* ".." */
|
|
if (length > 1)
|
|
dirs[i][length - 1] = PATH_SEP; /* add trailing / again */
|
|
|
|
snprintf(path, sizeof(path), "%s", dirs[i]);
|
|
length = strlen(path);
|
|
ptr = path + length;
|
|
|
|
while ((ent = readdir(dir)) != NULL) {
|
|
int isdir;
|
|
unsigned d_namlen = strlen(ent->d_name);
|
|
|
|
if (length + d_namlen + 2 < sizeof(path))
|
|
strcpy(ptr, ent->d_name);
|
|
else {
|
|
closedir(dir);
|
|
LispDestroy("%s: pathname too long %s",
|
|
STRFUN(builtin), dirs[i]);
|
|
}
|
|
|
|
if (stat(path, &st) != 0)
|
|
isdir = 0;
|
|
else
|
|
isdir = S_ISDIR(st.st_mode);
|
|
|
|
if (all != UNSPEC || ((isdir && (listdirs || sep)) ||
|
|
(!listdirs && !sep && !isdir))) {
|
|
if (glob_match(base, ent->d_name)) {
|
|
if (isdir) {
|
|
length = strlen(ptr);
|
|
ptr[length++] = PATH_SEP;
|
|
ptr[length] = '\0';
|
|
}
|
|
/* XXX won't closedir on memory allocation failure! */
|
|
matches = LispRealloc(matches, sizeof(char*) *
|
|
nmatches + 1);
|
|
matches[nmatches++] = LispStrdup(ptr);
|
|
}
|
|
}
|
|
}
|
|
closedir(dir);
|
|
|
|
if (nmatches == 0) {
|
|
if (sep || !listdirs || *base) {
|
|
LispFree(dirs[i]);
|
|
if (i + 1 < ndirs)
|
|
memmove(dirs + i, dirs + i + 1,
|
|
sizeof(char*) * (ndirs - (i + 1)));
|
|
--ndirs;
|
|
--i; /* XXX playing with for loop */
|
|
}
|
|
}
|
|
else {
|
|
int j;
|
|
|
|
length = strlen(dirs[i]);
|
|
if (nmatches > 1) {
|
|
dirs = LispRealloc(dirs, sizeof(char*) *
|
|
(ndirs + nmatches));
|
|
if (i + 1 < ndirs)
|
|
memmove(dirs + i + nmatches, dirs + i + 1,
|
|
sizeof(char*) * (ndirs - (i + 1)));
|
|
}
|
|
for (j = 1; j < nmatches; j++) {
|
|
dirs[i + j] = LispMalloc(length +
|
|
strlen(matches[j]) + 1);
|
|
sprintf(dirs[i + j], "%s%s", dirs[i], matches[j]);
|
|
}
|
|
dirs[i] = LispRealloc(dirs[i],
|
|
length + strlen(matches[0]) + 1);
|
|
strcpy(dirs[i] + length, matches[0]);
|
|
i += nmatches - 1; /* XXX playing with for loop */
|
|
ndirs += nmatches - 1;
|
|
|
|
for (j = 0; j < nmatches; j++)
|
|
LispFree(matches[j]);
|
|
LispFree(matches);
|
|
matches = NULL;
|
|
nmatches = 0;
|
|
}
|
|
}
|
|
else {
|
|
if (cannot_read == NOREAD_ERROR)
|
|
LispDestroy("%s: opendir(%s): %s",
|
|
STRFUN(builtin), dirs[i], strerror(errno));
|
|
else {
|
|
LispFree(dirs[i]);
|
|
if (i + 1 < ndirs)
|
|
memmove(dirs + i, dirs + i + 1,
|
|
sizeof(char*) * (ndirs - (i + 1)));
|
|
--ndirs;
|
|
--i; /* XXX playing with for loop */
|
|
}
|
|
}
|
|
}
|
|
if (sep)
|
|
*sep = PATH_SEP;
|
|
else
|
|
break;
|
|
}
|
|
|
|
for (i = 0; i < ndirs; i++) {
|
|
object = APPLY1(Oparse_namestring, STRING2(dirs[i]));
|
|
if (result == NIL) {
|
|
result = cons = CONS(object, NIL);
|
|
GC_PROTECT(result);
|
|
}
|
|
else {
|
|
RPLACD(cons, CONS(object, NIL));
|
|
cons = CDR(cons);
|
|
}
|
|
}
|
|
LispFree(dirs);
|
|
GC_LEAVE();
|
|
|
|
return (result);
|
|
}
|
|
|
|
LispObj *
|
|
Lisp_ParseNamestring(LispBuiltin *builtin)
|
|
/*
|
|
parse-namestring object &optional host defaults &key start end junk-allowed
|
|
*/
|
|
{
|
|
GC_ENTER();
|
|
LispObj *result;
|
|
|
|
LispObj *object, *host, *defaults, *ostart, *oend, *junk_allowed;
|
|
|
|
junk_allowed = ARGUMENT(5);
|
|
oend = ARGUMENT(4);
|
|
ostart = ARGUMENT(3);
|
|
defaults = ARGUMENT(2);
|
|
host = ARGUMENT(1);
|
|
object = ARGUMENT(0);
|
|
|
|
if (host == UNSPEC)
|
|
host = NIL;
|
|
if (defaults == UNSPEC)
|
|
defaults = NIL;
|
|
|
|
RETURN_COUNT = 1;
|
|
if (STREAMP(object)) {
|
|
if (object->data.stream.type == LispStreamFile)
|
|
object = object->data.stream.pathname;
|
|
/* else just check for JUNK-ALLOWED... */
|
|
}
|
|
if (PATHNAMEP(object)) {
|
|
RETURN(0) = FIXNUM(0);
|
|
return (object);
|
|
}
|
|
|
|
if (host != NIL) {
|
|
CHECK_STRING(host);
|
|
}
|
|
if (defaults != NIL) {
|
|
if (!PATHNAMEP(defaults)) {
|
|
defaults = APPLY1(Oparse_namestring, defaults);
|
|
GC_PROTECT(defaults);
|
|
}
|
|
}
|
|
|
|
result = NIL;
|
|
if (STRINGP(object)) {
|
|
LispObj *cons, *cdr;
|
|
char *name = THESTR(object), *ptr, *str, data[PATH_MAX + 1],
|
|
string[PATH_MAX + 1], *send;
|
|
const char *namestr, *typestr;
|
|
long start, end, length, alength, namelen, typelen;
|
|
|
|
LispCheckSequenceStartEnd(builtin, object, ostart, oend,
|
|
&start, &end, &length);
|
|
alength = end - start;
|
|
|
|
if (alength > sizeof(data) - 1)
|
|
LispDestroy("%s: string %s too large",
|
|
STRFUN(builtin), STROBJ(object));
|
|
memcpy(data, name + start, alength);
|
|
#ifndef KEEP_EXTRA_PATH_SEP
|
|
ptr = data;
|
|
send = ptr + alength;
|
|
while (ptr < send) {
|
|
if (*ptr++ == PATH_SEP) {
|
|
for (str = ptr; str < send && *str == PATH_SEP; str++)
|
|
;
|
|
if (str - ptr) {
|
|
memmove(ptr, str, alength - (str - data));
|
|
alength -= str - ptr;
|
|
send -= str - ptr;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
data[alength] = '\0';
|
|
memcpy(string, data, alength + 1);
|
|
|
|
if (PATHNAMEP(defaults))
|
|
defaults = defaults->data.pathname;
|
|
|
|
/* string name */
|
|
result = cons = CONS(NIL, NIL);
|
|
GC_PROTECT(result);
|
|
|
|
/* host */
|
|
if (defaults != NIL)
|
|
defaults = CDR(defaults);
|
|
cdr = defaults == NIL ? NIL : CAR(defaults);
|
|
RPLACD(cons, CONS(cdr, NIL));
|
|
cons = CDR(cons);
|
|
|
|
/* device */
|
|
if (defaults != NIL)
|
|
defaults = CDR(defaults);
|
|
cdr = defaults == NIL ? NIL : CAR(defaults);
|
|
RPLACD(cons, CONS(cdr, NIL));
|
|
cons = CDR(cons);
|
|
|
|
/* directory */
|
|
if (defaults != NIL)
|
|
defaults = CDR(defaults);
|
|
if (*data == PATH_SEP)
|
|
cdr = CONS(Kabsolute, NIL);
|
|
else
|
|
cdr = CONS(Krelative, NIL);
|
|
RPLACD(cons, CONS(cdr, NIL));
|
|
cons = CDR(cons);
|
|
/* directory components */
|
|
ptr = data;
|
|
send = data + alength;
|
|
if (*ptr == PATH_SEP)
|
|
++ptr;
|
|
for (str = ptr; str < send; str++) {
|
|
if (*str == PATH_SEP)
|
|
break;
|
|
}
|
|
while (str < send) {
|
|
*str++ = '\0';
|
|
if (str - ptr > NAME_MAX)
|
|
LispDestroy("%s: directory name too long %s",
|
|
STRFUN(builtin), ptr);
|
|
RPLACD(cdr, CONS(LSTRING(ptr, str - ptr - 1), NIL));
|
|
cdr = CDR(cdr);
|
|
for (ptr = str; str < send; str++) {
|
|
if (*str == PATH_SEP)
|
|
break;
|
|
}
|
|
}
|
|
if (str - ptr > NAME_MAX)
|
|
LispDestroy("%s: file name too long %s", STRFUN(builtin), ptr);
|
|
if (CAAR(cons) == Krelative &&
|
|
defaults != NIL && CAAR(defaults) == Kabsolute) {
|
|
/* defaults specify directory and pathname doesn't */
|
|
char *tstring;
|
|
long dlength, tlength;
|
|
LispObj *dir = CDAR(defaults);
|
|
|
|
for (dlength = 1; CONSP(dir); dir = CDR(dir))
|
|
dlength += STRLEN(CAR(dir)) + 1;
|
|
if (alength + dlength < PATH_MAX) {
|
|
memmove(data + dlength, data, alength + 1);
|
|
memmove(string + dlength, string, alength + 1);
|
|
alength += dlength;
|
|
ptr += dlength;
|
|
send += dlength;
|
|
CAAR(cons) = Kabsolute;
|
|
for (dir = CDAR(defaults), cdr = CAR(cons);
|
|
CONSP(dir);
|
|
dir = CDR(dir)) {
|
|
RPLACD(cdr, CONS(CAR(dir), CDR(cdr)));
|
|
cdr = CDR(cdr);
|
|
}
|
|
dir = CDAR(defaults);
|
|
data[0] = string[0] = PATH_SEP;
|
|
for (dlength = 1; CONSP(dir); dir = CDR(dir)) {
|
|
tstring = THESTR(CAR(dir));
|
|
tlength = STRLEN(CAR(dir));
|
|
memcpy(data + dlength, tstring, tlength);
|
|
memcpy(string + dlength, tstring, tlength);
|
|
dlength += tlength;
|
|
data[dlength] = string[dlength] = PATH_SEP;
|
|
++dlength;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* name */
|
|
if (defaults != NIL)
|
|
defaults = CDR(defaults);
|
|
cdr = defaults == NIL ? NIL : CAR(defaults);
|
|
for (typelen = 0, str = ptr; str < send; str++) {
|
|
if (*str == PATH_TYPESEP) {
|
|
typelen = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (*ptr)
|
|
cdr = LSTRING(ptr, str - ptr);
|
|
if (STRINGP(cdr)) {
|
|
namestr = THESTR(cdr);
|
|
namelen = STRLEN(cdr);
|
|
}
|
|
else {
|
|
namestr = "";
|
|
namelen = 0;
|
|
}
|
|
RPLACD(cons, CONS(cdr, NIL));
|
|
cons = CDR(cons);
|
|
|
|
/* type */
|
|
if (defaults != NIL)
|
|
defaults = CDR(defaults);
|
|
cdr = defaults == NIL ? NIL : CAR(defaults);
|
|
ptr = str + typelen;
|
|
if (*ptr)
|
|
cdr = LSTRING(ptr, send - ptr);
|
|
if (STRINGP(cdr)) {
|
|
typestr = THESTR(cdr);
|
|
typelen = STRLEN(cdr);
|
|
}
|
|
else {
|
|
typestr = "";
|
|
typelen = 0;
|
|
}
|
|
RPLACD(cons, CONS(cdr, NIL));
|
|
cons = CDR(cons);
|
|
|
|
/* version */
|
|
if (defaults != NIL)
|
|
defaults = CDR(defaults);
|
|
cdr = defaults == NIL ? NIL : CAR(defaults);
|
|
RPLACD(cons, CONS(cdr, NIL));
|
|
|
|
/* string representation, must be done here to use defaults */
|
|
for (ptr = string + alength; ptr >= string; ptr--) {
|
|
if (*ptr == PATH_SEP)
|
|
break;
|
|
}
|
|
if (ptr >= string)
|
|
++ptr;
|
|
else
|
|
ptr = string;
|
|
*ptr = '\0';
|
|
|
|
length = ptr - string;
|
|
|
|
alength = namelen;
|
|
if (alength) {
|
|
if (length + alength + 2 > sizeof(string))
|
|
alength = sizeof(string) - length - 2;
|
|
memcpy(string + length, namestr, alength);
|
|
length += alength;
|
|
}
|
|
|
|
alength = typelen;
|
|
if (alength) {
|
|
if (length + 2 < sizeof(string))
|
|
string[length++] = PATH_TYPESEP;
|
|
if (length + alength + 2 > sizeof(string))
|
|
alength = sizeof(string) - length - 2;
|
|
memcpy(string + length, typestr, alength);
|
|
length += alength;
|
|
}
|
|
string[length] = '\0';
|
|
|
|
RPLACA(result, LSTRING(string, length));
|
|
RETURN(0) = FIXNUM(end);
|
|
|
|
result = PATHNAME(result);
|
|
}
|
|
else if (junk_allowed == UNSPEC || junk_allowed == NIL)
|
|
LispDestroy("%s: bad argument %s", STRFUN(builtin), STROBJ(object));
|
|
else
|
|
RETURN(0) = NIL;
|
|
|
|
GC_LEAVE();
|
|
|
|
return (result);
|
|
}
|
|
|
|
LispObj *
|
|
Lisp_MakePathname(LispBuiltin *builtin)
|
|
/*
|
|
make-pathname &key host device directory name type version defaults
|
|
*/
|
|
{
|
|
GC_ENTER();
|
|
int length, alength;
|
|
char *string, pathname[PATH_MAX + 1];
|
|
LispObj *result, *cdr, *cons;
|
|
|
|
LispObj *host, *device, *directory, *name, *type, *version, *defaults;
|
|
|
|
defaults = ARGUMENT(6);
|
|
version = ARGUMENT(5);
|
|
type = ARGUMENT(4);
|
|
name = ARGUMENT(3);
|
|
directory = ARGUMENT(2);
|
|
device = ARGUMENT(1);
|
|
host = ARGUMENT(0);
|
|
|
|
if (host != UNSPEC) {
|
|
CHECK_STRING(host);
|
|
}
|
|
if (device != UNSPEC) {
|
|
CHECK_STRING(device);
|
|
}
|
|
|
|
if (directory != UNSPEC) {
|
|
LispObj *dir;
|
|
|
|
CHECK_CONS(directory);
|
|
dir = CAR(directory);
|
|
CHECK_KEYWORD(dir);
|
|
if (dir != Kabsolute && dir != Krelative)
|
|
LispDestroy("%s: directory type %s unknown",
|
|
STRFUN(builtin), STROBJ(dir));
|
|
}
|
|
|
|
if (name != UNSPEC) {
|
|
CHECK_STRING(name);
|
|
}
|
|
if (type != UNSPEC) {
|
|
CHECK_STRING(type);
|
|
}
|
|
|
|
if (version != UNSPEC && version != NIL) {
|
|
switch (OBJECT_TYPE(version)) {
|
|
case LispFixnum_t:
|
|
if (FIXNUM_VALUE(version) >= 0)
|
|
goto version_ok;
|
|
case LispInteger_t:
|
|
if (INT_VALUE(version) >= 0)
|
|
goto version_ok;
|
|
break;
|
|
case LispDFloat_t:
|
|
if (DFLOAT_VALUE(version) >= 0.0)
|
|
goto version_ok;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
LispDestroy("%s: %s is not a positive real number",
|
|
STRFUN(builtin), STROBJ(version));
|
|
}
|
|
version_ok:
|
|
|
|
if (defaults != UNSPEC && !PATHNAMEP(defaults) &&
|
|
(host == UNSPEC || device == UNSPEC || directory == UNSPEC ||
|
|
name == UNSPEC || type == UNSPEC || version == UNSPEC)) {
|
|
defaults = APPLY1(Oparse_namestring, defaults);
|
|
GC_PROTECT(defaults);
|
|
}
|
|
|
|
if (defaults != UNSPEC) {
|
|
defaults = defaults->data.pathname;
|
|
defaults = CDR(defaults); /* host */
|
|
if (host == UNSPEC)
|
|
host = CAR(defaults);
|
|
defaults = CDR(defaults); /* device */
|
|
if (device == UNSPEC)
|
|
device = CAR(defaults);
|
|
defaults = CDR(defaults); /* directory */
|
|
if (directory == UNSPEC)
|
|
directory = CAR(defaults);
|
|
defaults = CDR(defaults); /* name */
|
|
if (name == UNSPEC)
|
|
name = CAR(defaults);
|
|
defaults = CDR(defaults); /* type */
|
|
if (type == UNSPEC)
|
|
type = CAR(defaults);
|
|
defaults = CDR(defaults); /* version */
|
|
if (version == UNSPEC)
|
|
version = CAR(defaults);
|
|
}
|
|
|
|
/* string representation */
|
|
length = 0;
|
|
if (CONSP(directory)) {
|
|
if (CAR(directory) == Kabsolute)
|
|
pathname[length++] = PATH_SEP;
|
|
|
|
for (cdr = CDR(directory); CONSP(cdr); cdr = CDR(cdr)) {
|
|
CHECK_STRING(CAR(cdr));
|
|
string = THESTR(CAR(cdr));
|
|
alength = STRLEN(CAR(cdr));
|
|
if (alength > NAME_MAX)
|
|
LispDestroy("%s: directory name too long %s",
|
|
STRFUN(builtin), string);
|
|
if (length + alength + 2 > sizeof(pathname))
|
|
alength = sizeof(pathname) - length - 2;
|
|
memcpy(pathname + length, string, alength);
|
|
length += alength;
|
|
pathname[length++] = PATH_SEP;
|
|
}
|
|
}
|
|
if (STRINGP(name)) {
|
|
int xlength = 0;
|
|
|
|
if (STRINGP(type))
|
|
xlength = STRLEN(type) + 1;
|
|
|
|
string = THESTR(name);
|
|
alength = STRLEN(name);
|
|
if (alength + xlength > NAME_MAX)
|
|
LispDestroy("%s: file name too long %s",
|
|
STRFUN(builtin), string);
|
|
if (length + alength + 2 > sizeof(pathname))
|
|
alength = sizeof(pathname) - length - 2;
|
|
memcpy(pathname + length, string, alength);
|
|
length += alength;
|
|
}
|
|
if (STRINGP(type)) {
|
|
if (length + 2 < sizeof(pathname))
|
|
pathname[length++] = PATH_TYPESEP;
|
|
string = THESTR(type);
|
|
alength = STRLEN(type);
|
|
if (length + alength + 2 > sizeof(pathname))
|
|
alength = sizeof(pathname) - length - 2;
|
|
memcpy(pathname + length, string, alength);
|
|
length += alength;
|
|
}
|
|
pathname[length] = '\0';
|
|
result = cons = CONS(LSTRING(pathname, length), NIL);
|
|
GC_PROTECT(result);
|
|
|
|
/* host */
|
|
RPLACD(cons, CONS(host == UNSPEC ? NIL : host, NIL));
|
|
cons = CDR(cons);
|
|
|
|
/* device */
|
|
RPLACD(cons, CONS(device == UNSPEC ? NIL : device, NIL));
|
|
cons = CDR(cons);
|
|
|
|
/* directory */
|
|
if (directory == UNSPEC)
|
|
cdr = CONS(Krelative, NIL);
|
|
else
|
|
cdr = directory;
|
|
RPLACD(cons, CONS(cdr, NIL));
|
|
cons = CDR(cons);
|
|
|
|
/* name */
|
|
RPLACD(cons, CONS(name == UNSPEC ? NIL : name, NIL));
|
|
cons = CDR(cons);
|
|
|
|
/* type */
|
|
RPLACD(cons, CONS(type == UNSPEC ? NIL : type, NIL));
|
|
cons = CDR(cons);
|
|
|
|
/* version */
|
|
RPLACD(cons, CONS(version == UNSPEC ? NIL : version, NIL));
|
|
|
|
GC_LEAVE();
|
|
|
|
return (PATHNAME(result));
|
|
}
|
|
|
|
LispObj *
|
|
Lisp_PathnameHost(LispBuiltin *builtin)
|
|
/*
|
|
pathname-host pathname
|
|
*/
|
|
{
|
|
return (LispPathnameField(PATH_HOST, 0));
|
|
}
|
|
|
|
LispObj *
|
|
Lisp_PathnameDevice(LispBuiltin *builtin)
|
|
/*
|
|
pathname-device pathname
|
|
*/
|
|
{
|
|
return (LispPathnameField(PATH_DEVICE, 0));
|
|
}
|
|
|
|
LispObj *
|
|
Lisp_PathnameDirectory(LispBuiltin *builtin)
|
|
/*
|
|
pathname-device pathname
|
|
*/
|
|
{
|
|
return (LispPathnameField(PATH_DIRECTORY, 0));
|
|
}
|
|
|
|
LispObj *
|
|
Lisp_PathnameName(LispBuiltin *builtin)
|
|
/*
|
|
pathname-name pathname
|
|
*/
|
|
{
|
|
return (LispPathnameField(PATH_NAME, 0));
|
|
}
|
|
|
|
LispObj *
|
|
Lisp_PathnameType(LispBuiltin *builtin)
|
|
/*
|
|
pathname-type pathname
|
|
*/
|
|
{
|
|
return (LispPathnameField(PATH_TYPE, 0));
|
|
}
|
|
|
|
LispObj *
|
|
Lisp_PathnameVersion(LispBuiltin *builtin)
|
|
/*
|
|
pathname-version pathname
|
|
*/
|
|
{
|
|
return (LispPathnameField(PATH_VERSION, 0));
|
|
}
|
|
|
|
LispObj *
|
|
Lisp_FileNamestring(LispBuiltin *builtin)
|
|
/*
|
|
file-namestring pathname
|
|
*/
|
|
{
|
|
return (LispPathnameField(PATH_NAME, 1));
|
|
}
|
|
|
|
LispObj *
|
|
Lisp_DirectoryNamestring(LispBuiltin *builtin)
|
|
/*
|
|
directory-namestring pathname
|
|
*/
|
|
{
|
|
return (LispPathnameField(PATH_DIRECTORY, 1));
|
|
}
|
|
|
|
LispObj *
|
|
Lisp_EnoughNamestring(LispBuiltin *builtin)
|
|
/*
|
|
enough-pathname pathname &optional defaults
|
|
*/
|
|
{
|
|
LispObj *pathname, *defaults;
|
|
|
|
defaults = ARGUMENT(1);
|
|
pathname = ARGUMENT(0);
|
|
|
|
if (defaults != UNSPEC && defaults != NIL) {
|
|
char *ppathname, *pdefaults, *pp, *pd;
|
|
|
|
if (!STRINGP(pathname)) {
|
|
if (PATHNAMEP(pathname))
|
|
pathname = CAR(pathname->data.pathname);
|
|
else if (STREAMP(pathname) &&
|
|
pathname->data.stream.type == LispStreamFile)
|
|
pathname = CAR(pathname->data.stream.pathname->data.pathname);
|
|
else
|
|
LispDestroy("%s: bad PATHNAME %s",
|
|
STRFUN(builtin), STROBJ(pathname));
|
|
}
|
|
|
|
if (!STRINGP(defaults)) {
|
|
if (PATHNAMEP(defaults))
|
|
defaults = CAR(defaults->data.pathname);
|
|
else if (STREAMP(defaults) &&
|
|
defaults->data.stream.type == LispStreamFile)
|
|
defaults = CAR(defaults->data.stream.pathname->data.pathname);
|
|
else
|
|
LispDestroy("%s: bad DEFAULTS %s",
|
|
STRFUN(builtin), STROBJ(defaults));
|
|
}
|
|
|
|
ppathname = pp = THESTR(pathname);
|
|
pdefaults = pd = THESTR(defaults);
|
|
while (*ppathname && *pdefaults && *ppathname == *pdefaults) {
|
|
ppathname++;
|
|
pdefaults++;
|
|
}
|
|
if (*pdefaults == '\0' && pdefaults > pd)
|
|
--pdefaults;
|
|
if (*ppathname && *pdefaults && *pdefaults != PATH_SEP) {
|
|
--ppathname;
|
|
while (*ppathname != PATH_SEP && ppathname > pp)
|
|
--ppathname;
|
|
if (*ppathname == PATH_SEP)
|
|
++ppathname;
|
|
}
|
|
|
|
return (STRING(ppathname));
|
|
}
|
|
else {
|
|
if (STRINGP(pathname))
|
|
return (pathname);
|
|
else if (PATHNAMEP(pathname))
|
|
return (CAR(pathname->data.pathname));
|
|
else if (STREAMP(pathname)) {
|
|
if (pathname->data.stream.type == LispStreamFile)
|
|
return (CAR(pathname->data.stream.pathname->data.pathname));
|
|
}
|
|
}
|
|
LispDestroy("%s: bad PATHNAME %s", STRFUN(builtin), STROBJ(pathname));
|
|
|
|
return (NIL);
|
|
}
|
|
|
|
LispObj *
|
|
Lisp_Namestring(LispBuiltin *builtin)
|
|
/*
|
|
namestring pathname
|
|
*/
|
|
{
|
|
return (LispPathnameField(PATH_STRING, 1));
|
|
}
|
|
|
|
LispObj *
|
|
Lisp_HostNamestring(LispBuiltin *builtin)
|
|
/*
|
|
host-namestring pathname
|
|
*/
|
|
{
|
|
return (LispPathnameField(PATH_HOST, 1));
|
|
}
|
|
|
|
LispObj *
|
|
Lisp_Pathnamep(LispBuiltin *builtin)
|
|
/*
|
|
pathnamep object
|
|
*/
|
|
{
|
|
LispObj *object;
|
|
|
|
object = ARGUMENT(0);
|
|
|
|
return (PATHNAMEP(object) ? T : NIL);
|
|
}
|
|
|
|
/* XXX only checks if host is a string and only checks the HOME enviroment
|
|
* variable */
|
|
LispObj *
|
|
Lisp_UserHomedirPathname(LispBuiltin *builtin)
|
|
/*
|
|
user-homedir-pathname &optional host
|
|
*/
|
|
{
|
|
GC_ENTER();
|
|
int length;
|
|
char *home = getenv("HOME"), data[PATH_MAX + 1];
|
|
LispObj *result;
|
|
|
|
LispObj *host;
|
|
|
|
host = ARGUMENT(0);
|
|
|
|
if (host != UNSPEC && !STRINGP(host))
|
|
LispDestroy("%s: bad hostname %s", STRFUN(builtin), STROBJ(host));
|
|
|
|
length = 0;
|
|
if (home) {
|
|
length = strlen(home);
|
|
strncpy(data, home, length);
|
|
if (length && home[length - 1] != PATH_SEP)
|
|
data[length++] = PATH_SEP;
|
|
}
|
|
data[length] = '\0';
|
|
|
|
result = LSTRING(data, length);
|
|
GC_PROTECT(result);
|
|
result = APPLY1(Oparse_namestring, result);
|
|
GC_LEAVE();
|
|
|
|
return (result);
|
|
}
|
|
|
|
LispObj *
|
|
Lisp_Truename(LispBuiltin *builtin)
|
|
{
|
|
return (LispProbeFile(builtin, 0));
|
|
}
|
|
|
|
LispObj *
|
|
Lisp_ProbeFile(LispBuiltin *builtin)
|
|
{
|
|
return (LispProbeFile(builtin, 1));
|
|
}
|