15dfecf96Smrg/*
25dfecf96Smrg * Copyright (c) 2001 by The XFree86 Project, Inc.
35dfecf96Smrg *
45dfecf96Smrg * Permission is hereby granted, free of charge, to any person obtaining a
55dfecf96Smrg * copy of this software and associated documentation files (the "Software"),
65dfecf96Smrg * to deal in the Software without restriction, including without limitation
75dfecf96Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
85dfecf96Smrg * and/or sell copies of the Software, and to permit persons to whom the
95dfecf96Smrg * Software is furnished to do so, subject to the following conditions:
105dfecf96Smrg *
115dfecf96Smrg * The above copyright notice and this permission notice shall be included in
125dfecf96Smrg * all copies or substantial portions of the Software.
135dfecf96Smrg *
145dfecf96Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
155dfecf96Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
165dfecf96Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
175dfecf96Smrg * THE XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
185dfecf96Smrg * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
195dfecf96Smrg * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
205dfecf96Smrg * SOFTWARE.
215dfecf96Smrg *
225dfecf96Smrg * Except as contained in this notice, the name of the XFree86 Project shall
235dfecf96Smrg * not be used in advertising or otherwise to promote the sale, use or other
245dfecf96Smrg * dealings in this Software without prior written authorization from the
255dfecf96Smrg * XFree86 Project.
265dfecf96Smrg *
275dfecf96Smrg * Author: Paulo César Pereira de Andrade
285dfecf96Smrg */
295dfecf96Smrg
305dfecf96Smrg/* $XFree86: xc/programs/xedit/lisp/pathname.c,v 1.17tsi Exp $ */
315dfecf96Smrg
325dfecf96Smrg#include <stdio.h>	/* including dirent.h first may cause problems */
335dfecf96Smrg#include <sys/types.h>
345dfecf96Smrg#include <dirent.h>
355dfecf96Smrg#include <errno.h>
365dfecf96Smrg#include <sys/stat.h>
375dfecf96Smrg#include "lisp/pathname.h"
385dfecf96Smrg#include "lisp/private.h"
395dfecf96Smrg
405dfecf96Smrg#define NOREAD_SKIP	0
415dfecf96Smrg#define NOREAD_ERROR	1
425dfecf96Smrg
435dfecf96Smrg/*
445dfecf96Smrg * Initialization
455dfecf96Smrg */
465dfecf96SmrgLispObj *Oparse_namestring, *Kerror, *Kabsolute, *Krelative, *Kskip;
475dfecf96Smrg
485dfecf96Smrg/*
495dfecf96Smrg * Implementation
505dfecf96Smrg */
515dfecf96Smrgvoid
525dfecf96SmrgLispPathnameInit(void)
535dfecf96Smrg{
545dfecf96Smrg    Kerror		= KEYWORD("ERROR");
555dfecf96Smrg    Oparse_namestring	= STATIC_ATOM("PARSE-NAMESTRING");
565dfecf96Smrg    Kabsolute		= KEYWORD("ABSOLUTE");
575dfecf96Smrg    Krelative		= KEYWORD("RELATIVE");
585dfecf96Smrg}
595dfecf96Smrg
605dfecf96Smrgstatic int
615dfecf96Smrgglob_match(char *cmp1, char *cmp2)
625dfecf96Smrg/*
635dfecf96Smrg * Note: this code was written from scratch, and may generate incorrect
645dfecf96Smrg * results for very complex glob masks.
655dfecf96Smrg */
665dfecf96Smrg{
675dfecf96Smrg    for (;;) {
685dfecf96Smrg	while (*cmp1 && *cmp1 == *cmp2) {
695dfecf96Smrg	    ++cmp1;
705dfecf96Smrg	    ++cmp2;
715dfecf96Smrg	}
725dfecf96Smrg	if (*cmp2) {
735dfecf96Smrg	    if (*cmp1 == '*') {
745dfecf96Smrg		while (*cmp1 == '*')
755dfecf96Smrg		    ++cmp1;
765dfecf96Smrg		if (*cmp1) {
775dfecf96Smrg		    int count = 0, settmp = 1;
785dfecf96Smrg		    char *tmp = cmp2, *sav2;
795dfecf96Smrg
805dfecf96Smrg		    while (*cmp1 && *cmp1 == '?') {
815dfecf96Smrg			++cmp1;
825dfecf96Smrg			++count;
835dfecf96Smrg		    }
845dfecf96Smrg
855dfecf96Smrg		    /* need to recurse here to make sure
865dfecf96Smrg		     * all cases are tested.
875dfecf96Smrg		     */
885dfecf96Smrg		    while (*cmp2 && *cmp2 != *cmp1)
895dfecf96Smrg			++cmp2;
905dfecf96Smrg		    if (!*cmp1 && cmp2 - tmp < count)
915dfecf96Smrg			return (0);
925dfecf96Smrg		    sav2 = cmp2;
935dfecf96Smrg
945dfecf96Smrg		    /* if recursive calls fails, make sure all '?'
955dfecf96Smrg		     * following '*' are processed */
965dfecf96Smrg		    while (*sav2 && sav2 - tmp < count)
975dfecf96Smrg			++sav2;
985dfecf96Smrg
995dfecf96Smrg		    for (; *cmp2;) {
1005dfecf96Smrg			if (settmp) /* repeated letters: *?o? => boot, root */
1015dfecf96Smrg			    tmp = cmp2;
1025dfecf96Smrg			else
1035dfecf96Smrg			    settmp = 1;
1045dfecf96Smrg			while (*cmp2 && *cmp2 != *cmp1)
1055dfecf96Smrg			    ++cmp2;
1065dfecf96Smrg			if (cmp2 - tmp < count) {
1075dfecf96Smrg			    if (*cmp2)
1085dfecf96Smrg				++cmp2;
1095dfecf96Smrg			    settmp = 0;
1105dfecf96Smrg			    continue;
1115dfecf96Smrg			}
1125dfecf96Smrg			if (*cmp2) {
1135dfecf96Smrg			    if (glob_match(cmp1, cmp2))
1145dfecf96Smrg				return (1);
1155dfecf96Smrg			    ++cmp2;
1165dfecf96Smrg			}
1175dfecf96Smrg		    }
1185dfecf96Smrg		    cmp2 = sav2;
1195dfecf96Smrg		}
1205dfecf96Smrg		else {
1215dfecf96Smrg		    while (*cmp2)
1225dfecf96Smrg			++cmp2;
1235dfecf96Smrg		    break;
1245dfecf96Smrg		}
1255dfecf96Smrg	    }
1265dfecf96Smrg	    else if (*cmp1 == '?') {
1275dfecf96Smrg		while (*cmp1 == '?' && *cmp2) {
1285dfecf96Smrg		    ++cmp1;
1295dfecf96Smrg		    ++cmp2;
1305dfecf96Smrg		}
1315dfecf96Smrg		continue;
1325dfecf96Smrg	    }
1335dfecf96Smrg	    else
1345dfecf96Smrg		break;
1355dfecf96Smrg	}
1365dfecf96Smrg	else {
1375dfecf96Smrg	    while (*cmp1 == '*')
1385dfecf96Smrg		++cmp1;
1395dfecf96Smrg	    break;
1405dfecf96Smrg	}
1415dfecf96Smrg    }
1425dfecf96Smrg
1435dfecf96Smrg    return (*cmp1 == '\0' && *cmp2 == '\0');
1445dfecf96Smrg}
1455dfecf96Smrg
1465dfecf96Smrg/*
1475dfecf96Smrg * Since directory is a function to be extended by the implementation,
1485dfecf96Smrg * current extensions are:
1495dfecf96Smrg *	all		=> list files and directories
1505dfecf96Smrg *			   it is an error to call
1515dfecf96Smrg *			   (directory "<pathname-spec>/" :all t)
1525dfecf96Smrg *			   if non nil, it is like the shell command
1535dfecf96Smrg *			   echo <pathname-spec>, but normally, not in the
1545dfecf96Smrg *			   same order, as the code does not sort the result.
1555dfecf96Smrg *		!=nil	=> list files and directories
1565dfecf96Smrg * (default)	nil	=> list only files, or only directories if
1575dfecf96Smrg *			   <pathname-spec> ends with PATH_SEP char.
1585dfecf96Smrg *	if-cannot-read	=> if opendir fails on a directory
1595dfecf96Smrg *		:error	=> generate an error
1605dfecf96Smrg * (default)	:skip	=> skip search in this directory
1615dfecf96Smrg */
1625dfecf96SmrgLispObj *
1635dfecf96SmrgLisp_Directory(LispBuiltin *builtin)
1645dfecf96Smrg/*
1655dfecf96Smrg directory pathname &key all if-cannot-read
1665dfecf96Smrg */
1675dfecf96Smrg{
1685dfecf96Smrg    GC_ENTER();
1695dfecf96Smrg    DIR *dir;
1705dfecf96Smrg    struct stat st;
1715dfecf96Smrg    struct dirent *ent;
1725dfecf96Smrg    int length, listdirs, i, ndirs, nmatches;
1735dfecf96Smrg    char name[PATH_MAX + 1], path[PATH_MAX + 2], directory[PATH_MAX + 2];
1745dfecf96Smrg    char *sep, *base, *ptr, **dirs, **matches,
1755dfecf96Smrg	  dot[] = {'.', PATH_SEP, '\0'},
1765dfecf96Smrg	  dotdot[] = {'.', '.', PATH_SEP, '\0'};
1775dfecf96Smrg    int cannot_read;
1785dfecf96Smrg
1795dfecf96Smrg    LispObj *pathname, *all, *if_cannot_read, *result, *cons, *object;
1805dfecf96Smrg
1815dfecf96Smrg    if_cannot_read = ARGUMENT(2);
1825dfecf96Smrg    all = ARGUMENT(1);
1835dfecf96Smrg    pathname = ARGUMENT(0);
1845dfecf96Smrg    result = NIL;
1855dfecf96Smrg
1865dfecf96Smrg    cons = NIL;
1875dfecf96Smrg
1885dfecf96Smrg    if (if_cannot_read != UNSPEC) {
1895dfecf96Smrg	if (!KEYWORDP(if_cannot_read) ||
1905dfecf96Smrg	    (if_cannot_read != Kskip &&
1915dfecf96Smrg	     if_cannot_read != Kerror))
1925dfecf96Smrg	    LispDestroy("%s: bad :IF-CANNOT-READ %s",
1935dfecf96Smrg			STRFUN(builtin), STROBJ(if_cannot_read));
1945dfecf96Smrg	if (if_cannot_read != Kskip)
1955dfecf96Smrg	    cannot_read = NOREAD_SKIP;
1965dfecf96Smrg	else
1975dfecf96Smrg	    cannot_read = NOREAD_ERROR;
1985dfecf96Smrg    }
1995dfecf96Smrg    else
2005dfecf96Smrg	cannot_read = NOREAD_SKIP;
2015dfecf96Smrg
2025dfecf96Smrg    if (PATHNAMEP(pathname))
2035dfecf96Smrg	pathname = CAR(pathname->data.pathname);
2045dfecf96Smrg    else if (STREAMP(pathname) && pathname->data.stream.type == LispStreamFile)
2055dfecf96Smrg	pathname = CAR(pathname->data.stream.pathname->data.pathname);
2065dfecf96Smrg    else if (!STRINGP(pathname))
2075dfecf96Smrg	LispDestroy("%s: %s is not a pathname",
2085dfecf96Smrg		    STRFUN(builtin), STROBJ(pathname));
2095dfecf96Smrg
2105dfecf96Smrg    strncpy(name, THESTR(pathname), sizeof(name) - 1);
2115dfecf96Smrg    name[sizeof(name) - 1] = '\0';
2125dfecf96Smrg    length = strlen(name);
2135dfecf96Smrg    if (length < STRLEN(pathname))
2145dfecf96Smrg	LispDestroy("%s: pathname too long %s",
2155dfecf96Smrg		    STRFUN(builtin), name);
2165dfecf96Smrg
2175dfecf96Smrg    if (length == 0) {
2185dfecf96Smrg	if (getcwd(path, sizeof(path) - 2) == NULL)
2195dfecf96Smrg	    LispDestroy("%s: getcwd(): %s", STRFUN(builtin), strerror(errno));
2205dfecf96Smrg	length = strlen(path);
2215dfecf96Smrg	if (!length || path[length - 1] != PATH_SEP) {
2225dfecf96Smrg	    path[length++] = PATH_SEP;
2235dfecf96Smrg	    path[length] = '\0';
2245dfecf96Smrg	}
2255dfecf96Smrg	result = APPLY1(Oparse_namestring, LSTRING(path, length));
2265dfecf96Smrg	GC_LEAVE();
2275dfecf96Smrg
2285dfecf96Smrg	return (result);
2295dfecf96Smrg    }
2305dfecf96Smrg
2315dfecf96Smrg    if (name[length - 1] == PATH_SEP) {
2325dfecf96Smrg	listdirs = 1;
2335dfecf96Smrg	if (length > 1) {
2345dfecf96Smrg	    --length;
2355dfecf96Smrg	    name[length] = '\0';
2365dfecf96Smrg	}
2375dfecf96Smrg    }
2385dfecf96Smrg    else
2395dfecf96Smrg	listdirs = 0;
2405dfecf96Smrg
2415dfecf96Smrg    if (name[0] != PATH_SEP) {
2425dfecf96Smrg	if (getcwd(path, sizeof(path) - 2) == NULL)
2435dfecf96Smrg	    LispDestroy("%s: getcwd(): %s", STRFUN(builtin), strerror(errno));
2445dfecf96Smrg	length = strlen(path);
2455dfecf96Smrg	if (!length || path[length - 1] != PATH_SEP) {
2465dfecf96Smrg	    path[length++] = PATH_SEP;
2475dfecf96Smrg	    path[length] = '\0';
2485dfecf96Smrg	}
2495dfecf96Smrg    }
2505dfecf96Smrg    else
2515dfecf96Smrg	path[0] = '\0';
2525dfecf96Smrg
2535dfecf96Smrg    result = NIL;
2545dfecf96Smrg
2555dfecf96Smrg    /* list intermediate directories */
2565dfecf96Smrg    matches = NULL;
2575dfecf96Smrg    nmatches = 0;
2585dfecf96Smrg    dirs = LispMalloc(sizeof(char*));
2595dfecf96Smrg    ndirs = 1;
2605dfecf96Smrg    if (snprintf(directory, sizeof(directory), "%s%s%c",
2615dfecf96Smrg		 path, name, PATH_SEP) > PATH_MAX)
2625dfecf96Smrg	LispDestroy("%s: pathname too long %s", STRFUN(builtin), directory);
2635dfecf96Smrg
2645dfecf96Smrg    /* Remove ../ */
2655dfecf96Smrg    sep = directory;
2665dfecf96Smrg    for (sep = strstr(sep, dotdot); sep; sep = strstr(sep, dotdot)) {
2675dfecf96Smrg	if (sep <= directory + 1)
2685dfecf96Smrg	    strcpy(directory, sep + 2);
2695dfecf96Smrg	else if (sep[-1] == PATH_SEP) {
2705dfecf96Smrg	    for (base = sep - 2; base > directory; base--)
2715dfecf96Smrg		if (*base == PATH_SEP)
2725dfecf96Smrg		    break;
2735dfecf96Smrg	    strcpy(base, sep + 2);
2745dfecf96Smrg	    sep = base;
2755dfecf96Smrg	}
2765dfecf96Smrg	else
2775dfecf96Smrg	    ++sep;
2785dfecf96Smrg    }
2795dfecf96Smrg
2805dfecf96Smrg    /* Remove "./" */
2815dfecf96Smrg    sep = directory;
2825dfecf96Smrg    for (sep = strstr(sep, dot); sep; sep = strstr(sep, dot)) {
2835dfecf96Smrg	if (sep == directory || sep[-1] == PATH_SEP)
2845dfecf96Smrg	    strcpy(sep, sep + 2);
2855dfecf96Smrg	else
2865dfecf96Smrg	    ++sep;
2875dfecf96Smrg    }
2885dfecf96Smrg
2895dfecf96Smrg    /* This will happen when there are too many '../'  in the path */
2905dfecf96Smrg    if (directory[1] == '\0') {
2915dfecf96Smrg	directory[1] = PATH_SEP;
2925dfecf96Smrg	directory[2] = '\0';
2935dfecf96Smrg    }
2945dfecf96Smrg
2955dfecf96Smrg    base = directory;
2965dfecf96Smrg    sep = strchr(base + 1, PATH_SEP);
2975dfecf96Smrg    dirs[0] = LispMalloc(2);
2985dfecf96Smrg    dirs[0][0] = PATH_SEP;
2995dfecf96Smrg    dirs[0][1] = '\0';
3005dfecf96Smrg
3015dfecf96Smrg    for (base = directory + 1, sep = strchr(base, PATH_SEP); ;
3025dfecf96Smrg	 base = sep + 1, sep = strchr(base, PATH_SEP)) {
3035dfecf96Smrg	*sep = '\0';
3045dfecf96Smrg	if (sep[1] == '\0')
3055dfecf96Smrg	    sep = NULL;
3065dfecf96Smrg	length = strlen(base);
3075dfecf96Smrg	if (length == 0) {
3085dfecf96Smrg	    if (sep)
3095dfecf96Smrg		*sep = PATH_SEP;
3105dfecf96Smrg	    else
3115dfecf96Smrg		break;
3125dfecf96Smrg	    continue;
3135dfecf96Smrg	}
3145dfecf96Smrg
3155dfecf96Smrg	for (i = 0; i < ndirs; i++) {
3165dfecf96Smrg	    length = strlen(dirs[i]);
3175dfecf96Smrg	    if (length > 1)
3185dfecf96Smrg		dirs[i][length - 1] = '\0';		/* remove trailing / */
3195dfecf96Smrg	    if ((dir = opendir(dirs[i])) != NULL) {
3205dfecf96Smrg		(void)readdir(dir);	/* "." */
3215dfecf96Smrg		(void)readdir(dir);	/* ".." */
3225dfecf96Smrg		if (length > 1)
3235dfecf96Smrg		    dirs[i][length - 1] = PATH_SEP;	/* add trailing / again */
3245dfecf96Smrg
3255dfecf96Smrg		snprintf(path, sizeof(path), "%s", dirs[i]);
3265dfecf96Smrg		length = strlen(path);
3275dfecf96Smrg		ptr = path + length;
3285dfecf96Smrg
3295dfecf96Smrg		while ((ent = readdir(dir)) != NULL) {
3305dfecf96Smrg		    int isdir;
3315dfecf96Smrg		    unsigned d_namlen = strlen(ent->d_name);
3325dfecf96Smrg
3335dfecf96Smrg		    if (length + d_namlen + 2 < sizeof(path))
3345dfecf96Smrg			strcpy(ptr, ent->d_name);
3355dfecf96Smrg		    else {
3365dfecf96Smrg			closedir(dir);
3375dfecf96Smrg			LispDestroy("%s: pathname too long %s",
3385dfecf96Smrg				    STRFUN(builtin), dirs[i]);
3395dfecf96Smrg		    }
3405dfecf96Smrg
3415dfecf96Smrg		    if (stat(path, &st) != 0)
3425dfecf96Smrg			isdir = 0;
3435dfecf96Smrg		    else
3445dfecf96Smrg			isdir = S_ISDIR(st.st_mode);
3455dfecf96Smrg
3465dfecf96Smrg		    if (all != UNSPEC || ((isdir && (listdirs || sep)) ||
3475dfecf96Smrg					  (!listdirs && !sep && !isdir))) {
3485dfecf96Smrg			if (glob_match(base, ent->d_name)) {
3495dfecf96Smrg			    if (isdir) {
3505dfecf96Smrg				length = strlen(ptr);
3515dfecf96Smrg				ptr[length++] = PATH_SEP;
3525dfecf96Smrg				ptr[length] = '\0';
3535dfecf96Smrg			    }
3545dfecf96Smrg			    /* XXX won't closedir on memory allocation failure! */
3555dfecf96Smrg			    matches = LispRealloc(matches, sizeof(char*) *
3565dfecf96Smrg						  nmatches + 1);
3575dfecf96Smrg			    matches[nmatches++] = LispStrdup(ptr);
3585dfecf96Smrg			}
3595dfecf96Smrg		    }
3605dfecf96Smrg		}
3615dfecf96Smrg		closedir(dir);
3625dfecf96Smrg
3635dfecf96Smrg		if (nmatches == 0) {
3645dfecf96Smrg		    if (sep || !listdirs || *base) {
3655dfecf96Smrg			LispFree(dirs[i]);
3665dfecf96Smrg			if (i + 1 < ndirs)
3675dfecf96Smrg			    memmove(dirs + i, dirs + i + 1,
3685dfecf96Smrg				    sizeof(char*) * (ndirs - (i + 1)));
3695dfecf96Smrg			--ndirs;
3705dfecf96Smrg			--i;		    /* XXX playing with for loop */
3715dfecf96Smrg		    }
3725dfecf96Smrg		}
3735dfecf96Smrg		else {
3745dfecf96Smrg		    int j;
3755dfecf96Smrg
3765dfecf96Smrg		    length = strlen(dirs[i]);
3775dfecf96Smrg		    if (nmatches > 1) {
3785dfecf96Smrg			dirs = LispRealloc(dirs, sizeof(char*) *
3795dfecf96Smrg					   (ndirs + nmatches));
3805dfecf96Smrg			if (i + 1 < ndirs)
3815dfecf96Smrg			    memmove(dirs + i + nmatches, dirs + i + 1,
3825dfecf96Smrg				    sizeof(char*) * (ndirs - (i + 1)));
3835dfecf96Smrg		    }
3845dfecf96Smrg		    for (j = 1; j < nmatches; j++) {
3855dfecf96Smrg			dirs[i + j] = LispMalloc(length +
3865dfecf96Smrg						 strlen(matches[j]) + 1);
3875dfecf96Smrg			sprintf(dirs[i + j], "%s%s", dirs[i], matches[j]);
3885dfecf96Smrg		    }
3895dfecf96Smrg		    dirs[i] = LispRealloc(dirs[i],
3905dfecf96Smrg					  length + strlen(matches[0]) + 1);
3915dfecf96Smrg		    strcpy(dirs[i] + length, matches[0]);
3925dfecf96Smrg		    i += nmatches - 1;	/* XXX playing with for loop */
3935dfecf96Smrg		    ndirs += nmatches - 1;
3945dfecf96Smrg
3955dfecf96Smrg		    for (j = 0; j < nmatches; j++)
3965dfecf96Smrg			LispFree(matches[j]);
3975dfecf96Smrg		    LispFree(matches);
3985dfecf96Smrg		    matches = NULL;
3995dfecf96Smrg		    nmatches = 0;
4005dfecf96Smrg		}
4015dfecf96Smrg	    }
4025dfecf96Smrg	    else {
4035dfecf96Smrg		if (cannot_read == NOREAD_ERROR)
4045dfecf96Smrg		    LispDestroy("%s: opendir(%s): %s",
4055dfecf96Smrg				STRFUN(builtin), dirs[i], strerror(errno));
4065dfecf96Smrg		else {
4075dfecf96Smrg		    LispFree(dirs[i]);
4085dfecf96Smrg		    if (i + 1 < ndirs)
4095dfecf96Smrg			memmove(dirs + i, dirs + i + 1,
4105dfecf96Smrg				sizeof(char*) * (ndirs - (i + 1)));
4115dfecf96Smrg		    --ndirs;
4125dfecf96Smrg		    --i;	    /* XXX playing with for loop */
4135dfecf96Smrg		}
4145dfecf96Smrg	    }
4155dfecf96Smrg	}
4165dfecf96Smrg	if (sep)
4175dfecf96Smrg	    *sep = PATH_SEP;
4185dfecf96Smrg	else
4195dfecf96Smrg	    break;
4205dfecf96Smrg    }
4215dfecf96Smrg
4225dfecf96Smrg    for (i = 0; i < ndirs; i++) {
4235dfecf96Smrg	object = APPLY1(Oparse_namestring, STRING2(dirs[i]));
4245dfecf96Smrg	if (result == NIL) {
4255dfecf96Smrg	    result = cons = CONS(object, NIL);
4265dfecf96Smrg	    GC_PROTECT(result);
4275dfecf96Smrg	}
4285dfecf96Smrg	else {
4295dfecf96Smrg	    RPLACD(cons, CONS(object, NIL));
4305dfecf96Smrg	    cons = CDR(cons);
4315dfecf96Smrg	}
4325dfecf96Smrg    }
4335dfecf96Smrg    LispFree(dirs);
4345dfecf96Smrg    GC_LEAVE();
4355dfecf96Smrg
4365dfecf96Smrg    return (result);
4375dfecf96Smrg}
4385dfecf96Smrg
4395dfecf96SmrgLispObj *
4405dfecf96SmrgLisp_ParseNamestring(LispBuiltin *builtin)
4415dfecf96Smrg/*
4425dfecf96Smrg parse-namestring object &optional host defaults &key start end junk-allowed
4435dfecf96Smrg */
4445dfecf96Smrg{
4455dfecf96Smrg    GC_ENTER();
4465dfecf96Smrg    LispObj *result;
4475dfecf96Smrg
4485dfecf96Smrg    LispObj *object, *host, *defaults, *ostart, *oend, *junk_allowed;
4495dfecf96Smrg
4505dfecf96Smrg    junk_allowed = ARGUMENT(5);
4515dfecf96Smrg    oend = ARGUMENT(4);
4525dfecf96Smrg    ostart = ARGUMENT(3);
4535dfecf96Smrg    defaults = ARGUMENT(2);
4545dfecf96Smrg    host = ARGUMENT(1);
4555dfecf96Smrg    object = ARGUMENT(0);
4565dfecf96Smrg
4575dfecf96Smrg    if (host == UNSPEC)
4585dfecf96Smrg	host = NIL;
4595dfecf96Smrg    if (defaults == UNSPEC)
4605dfecf96Smrg	defaults = NIL;
4615dfecf96Smrg
4625dfecf96Smrg    RETURN_COUNT = 1;
4635dfecf96Smrg    if (STREAMP(object)) {
4645dfecf96Smrg	if (object->data.stream.type == LispStreamFile)
4655dfecf96Smrg	    object = object->data.stream.pathname;
4665dfecf96Smrg	/* else just check for JUNK-ALLOWED... */
4675dfecf96Smrg    }
4685dfecf96Smrg    if (PATHNAMEP(object)) {
4695dfecf96Smrg	RETURN(0) = FIXNUM(0);
4705dfecf96Smrg	return (object);
4715dfecf96Smrg    }
4725dfecf96Smrg
4735dfecf96Smrg    if (host != NIL) {
4745dfecf96Smrg	CHECK_STRING(host);
4755dfecf96Smrg    }
4765dfecf96Smrg    if (defaults != NIL) {
4775dfecf96Smrg	if (!PATHNAMEP(defaults)) {
4785dfecf96Smrg	    defaults = APPLY1(Oparse_namestring, defaults);
4795dfecf96Smrg	    GC_PROTECT(defaults);
4805dfecf96Smrg	}
4815dfecf96Smrg    }
4825dfecf96Smrg
4835dfecf96Smrg    result = NIL;
4845dfecf96Smrg    if (STRINGP(object)) {
4855dfecf96Smrg	LispObj *cons, *cdr;
4865dfecf96Smrg	char *name = THESTR(object), *ptr, *str, data[PATH_MAX + 1],
487f765521fSmrg	      string[PATH_MAX + 1], *send;
488f765521fSmrg	const char *namestr, *typestr;
4895dfecf96Smrg	long start, end, length, alength, namelen, typelen;
4905dfecf96Smrg
4915dfecf96Smrg	LispCheckSequenceStartEnd(builtin, object, ostart, oend,
4925dfecf96Smrg				  &start, &end, &length);
4935dfecf96Smrg	alength = end - start;
4945dfecf96Smrg
4955dfecf96Smrg	if (alength > sizeof(data) - 1)
4965dfecf96Smrg	    LispDestroy("%s: string %s too large",
4975dfecf96Smrg			STRFUN(builtin), STROBJ(object));
4985dfecf96Smrg	memcpy(data, name + start, alength);
4995dfecf96Smrg#ifndef KEEP_EXTRA_PATH_SEP
5005dfecf96Smrg	ptr = data;
5015dfecf96Smrg	send = ptr + alength;
5025dfecf96Smrg	while (ptr < send) {
5035dfecf96Smrg	    if (*ptr++ == PATH_SEP) {
5045dfecf96Smrg		for (str = ptr; str < send && *str == PATH_SEP; str++)
5055dfecf96Smrg		    ;
5065dfecf96Smrg		if (str - ptr) {
5075dfecf96Smrg		    memmove(ptr, str, alength - (str - data));
5085dfecf96Smrg		    alength -= str - ptr;
5095dfecf96Smrg		    send -= str - ptr;
5105dfecf96Smrg		}
5115dfecf96Smrg	    }
5125dfecf96Smrg	}
5135dfecf96Smrg#endif
5145dfecf96Smrg	data[alength] = '\0';
5155dfecf96Smrg	memcpy(string, data, alength + 1);
5165dfecf96Smrg
5175dfecf96Smrg	if (PATHNAMEP(defaults))
5185dfecf96Smrg	    defaults = defaults->data.pathname;
5195dfecf96Smrg
5205dfecf96Smrg	/* string name */
5215dfecf96Smrg	result = cons = CONS(NIL, NIL);
5225dfecf96Smrg	GC_PROTECT(result);
5235dfecf96Smrg
5245dfecf96Smrg	/* host */
5255dfecf96Smrg	if (defaults != NIL)
5265dfecf96Smrg	    defaults = CDR(defaults);
5275dfecf96Smrg	cdr = defaults == NIL ? NIL : CAR(defaults);
5285dfecf96Smrg	RPLACD(cons, CONS(cdr, NIL));
5295dfecf96Smrg	cons = CDR(cons);
5305dfecf96Smrg
5315dfecf96Smrg	/* device */
5325dfecf96Smrg	if (defaults != NIL)
5335dfecf96Smrg	    defaults = CDR(defaults);
5345dfecf96Smrg	cdr = defaults == NIL ? NIL : CAR(defaults);
5355dfecf96Smrg	RPLACD(cons, CONS(cdr, NIL));
5365dfecf96Smrg	cons = CDR(cons);
5375dfecf96Smrg
5385dfecf96Smrg	/* directory */
5395dfecf96Smrg	if (defaults != NIL)
5405dfecf96Smrg	    defaults = CDR(defaults);
5415dfecf96Smrg	if (*data == PATH_SEP)
5425dfecf96Smrg	    cdr = CONS(Kabsolute, NIL);
5435dfecf96Smrg	else
5445dfecf96Smrg	    cdr = CONS(Krelative, NIL);
5455dfecf96Smrg	RPLACD(cons, CONS(cdr, NIL));
5465dfecf96Smrg	cons = CDR(cons);
5475dfecf96Smrg	/* directory components */
5485dfecf96Smrg	ptr = data;
5495dfecf96Smrg	send = data + alength;
5505dfecf96Smrg	if (*ptr == PATH_SEP)
5515dfecf96Smrg	    ++ptr;
5525dfecf96Smrg	for (str = ptr; str < send; str++) {
5535dfecf96Smrg	    if (*str == PATH_SEP)
5545dfecf96Smrg		break;
5555dfecf96Smrg	}
5565dfecf96Smrg	while (str < send) {
5575dfecf96Smrg	    *str++ = '\0';
5585dfecf96Smrg	    if (str - ptr > NAME_MAX)
5595dfecf96Smrg		LispDestroy("%s: directory name too long %s",
5605dfecf96Smrg			    STRFUN(builtin), ptr);
5615dfecf96Smrg	    RPLACD(cdr, CONS(LSTRING(ptr, str - ptr - 1), NIL));
5625dfecf96Smrg	    cdr = CDR(cdr);
5635dfecf96Smrg	    for (ptr = str; str < send; str++) {
5645dfecf96Smrg		if (*str == PATH_SEP)
5655dfecf96Smrg		    break;
5665dfecf96Smrg	    }
5675dfecf96Smrg	}
5685dfecf96Smrg	if (str - ptr > NAME_MAX)
5695dfecf96Smrg	    LispDestroy("%s: file name too long %s", STRFUN(builtin), ptr);
5705dfecf96Smrg	if (CAAR(cons) == Krelative &&
5715dfecf96Smrg	    defaults != NIL && CAAR(defaults) == Kabsolute) {
5725dfecf96Smrg	    /* defaults specify directory and pathname doesn't */
5735dfecf96Smrg	    char *tstring;
5745dfecf96Smrg	    long dlength, tlength;
5755dfecf96Smrg	    LispObj *dir = CDAR(defaults);
5765dfecf96Smrg
5775dfecf96Smrg	    for (dlength = 1; CONSP(dir); dir = CDR(dir))
5785dfecf96Smrg		dlength += STRLEN(CAR(dir)) + 1;
5795dfecf96Smrg	    if (alength + dlength < PATH_MAX) {
5805dfecf96Smrg		memmove(data + dlength, data, alength + 1);
5815dfecf96Smrg		memmove(string + dlength, string, alength + 1);
5825dfecf96Smrg		alength += dlength;
5835dfecf96Smrg		ptr += dlength;
5845dfecf96Smrg		send += dlength;
5855dfecf96Smrg		CAAR(cons) = Kabsolute;
5865dfecf96Smrg		for (dir = CDAR(defaults), cdr = CAR(cons);
5875dfecf96Smrg		     CONSP(dir);
5885dfecf96Smrg		     dir = CDR(dir)) {
5895dfecf96Smrg		    RPLACD(cdr, CONS(CAR(dir), CDR(cdr)));
5905dfecf96Smrg		    cdr = CDR(cdr);
5915dfecf96Smrg		}
5925dfecf96Smrg		dir = CDAR(defaults);
5935dfecf96Smrg		data[0] = string[0] = PATH_SEP;
5945dfecf96Smrg		for (dlength = 1; CONSP(dir); dir = CDR(dir)) {
5955dfecf96Smrg		    tstring = THESTR(CAR(dir));
5965dfecf96Smrg		    tlength = STRLEN(CAR(dir));
5975dfecf96Smrg		    memcpy(data + dlength, tstring, tlength);
5985dfecf96Smrg		    memcpy(string + dlength, tstring, tlength);
5995dfecf96Smrg		    dlength += tlength;
6005dfecf96Smrg		    data[dlength] = string[dlength] = PATH_SEP;
6015dfecf96Smrg		    ++dlength;
6025dfecf96Smrg		}
6035dfecf96Smrg	    }
6045dfecf96Smrg	}
6055dfecf96Smrg
6065dfecf96Smrg	/* name */
6075dfecf96Smrg	if (defaults != NIL)
6085dfecf96Smrg	    defaults = CDR(defaults);
6095dfecf96Smrg	cdr = defaults == NIL ? NIL : CAR(defaults);
6105dfecf96Smrg	for (typelen = 0, str = ptr; str < send; str++) {
6115dfecf96Smrg	    if (*str == PATH_TYPESEP) {
6125dfecf96Smrg		typelen = 1;
6135dfecf96Smrg		break;
6145dfecf96Smrg	    }
6155dfecf96Smrg	}
6165dfecf96Smrg	if (*ptr)
6175dfecf96Smrg	    cdr = LSTRING(ptr, str - ptr);
6185dfecf96Smrg	if (STRINGP(cdr)) {
6195dfecf96Smrg	    namestr = THESTR(cdr);
6205dfecf96Smrg	    namelen = STRLEN(cdr);
6215dfecf96Smrg	}
6225dfecf96Smrg	else {
6235dfecf96Smrg	    namestr = "";
6245dfecf96Smrg	    namelen = 0;
6255dfecf96Smrg	}
6265dfecf96Smrg	RPLACD(cons, CONS(cdr, NIL));
6275dfecf96Smrg	cons = CDR(cons);
6285dfecf96Smrg
6295dfecf96Smrg	/* type */
6305dfecf96Smrg	if (defaults != NIL)
6315dfecf96Smrg	    defaults = CDR(defaults);
6325dfecf96Smrg	cdr = defaults == NIL ? NIL : CAR(defaults);
6335dfecf96Smrg	ptr = str + typelen;
6345dfecf96Smrg	if (*ptr)
6355dfecf96Smrg	    cdr = LSTRING(ptr, send - ptr);
6365dfecf96Smrg	if (STRINGP(cdr)) {
6375dfecf96Smrg	    typestr = THESTR(cdr);
6385dfecf96Smrg	    typelen = STRLEN(cdr);
6395dfecf96Smrg	}
6405dfecf96Smrg	else {
6415dfecf96Smrg	    typestr = "";
6425dfecf96Smrg	    typelen = 0;
6435dfecf96Smrg	}
6445dfecf96Smrg	RPLACD(cons, CONS(cdr, NIL));
6455dfecf96Smrg	cons = CDR(cons);
6465dfecf96Smrg
6475dfecf96Smrg	/* version */
6485dfecf96Smrg	if (defaults != NIL)
6495dfecf96Smrg	    defaults = CDR(defaults);
6505dfecf96Smrg	cdr = defaults == NIL ? NIL : CAR(defaults);
6515dfecf96Smrg	RPLACD(cons, CONS(cdr, NIL));
6525dfecf96Smrg
6535dfecf96Smrg	/* string representation, must be done here to use defaults */
6545dfecf96Smrg	for (ptr = string + alength; ptr >= string; ptr--) {
6555dfecf96Smrg	    if (*ptr == PATH_SEP)
6565dfecf96Smrg		break;
6575dfecf96Smrg	}
6585dfecf96Smrg	if (ptr >= string)
6595dfecf96Smrg	    ++ptr;
6605dfecf96Smrg	else
6615dfecf96Smrg	    ptr = string;
6625dfecf96Smrg	*ptr = '\0';
6635dfecf96Smrg
6645dfecf96Smrg	length = ptr - string;
6655dfecf96Smrg
6665dfecf96Smrg	alength = namelen;
6675dfecf96Smrg	if (alength) {
6685dfecf96Smrg	    if (length + alength + 2 > sizeof(string))
6695dfecf96Smrg		alength = sizeof(string) - length - 2;
6705dfecf96Smrg	    memcpy(string + length, namestr, alength);
6715dfecf96Smrg	    length += alength;
6725dfecf96Smrg	}
6735dfecf96Smrg
6745dfecf96Smrg	alength = typelen;
6755dfecf96Smrg	if (alength) {
6765dfecf96Smrg	    if (length + 2 < sizeof(string))
6775dfecf96Smrg		string[length++] = PATH_TYPESEP;
6785dfecf96Smrg	    if (length + alength + 2 > sizeof(string))
6795dfecf96Smrg		alength = sizeof(string) - length - 2;
6805dfecf96Smrg	    memcpy(string + length, typestr, alength);
6815dfecf96Smrg	    length += alength;
6825dfecf96Smrg	}
6835dfecf96Smrg	string[length] = '\0';
6845dfecf96Smrg
6855dfecf96Smrg	RPLACA(result,  LSTRING(string, length));
6865dfecf96Smrg	RETURN(0) = FIXNUM(end);
6875dfecf96Smrg
6885dfecf96Smrg	result = PATHNAME(result);
6895dfecf96Smrg    }
6905dfecf96Smrg    else if (junk_allowed == UNSPEC || junk_allowed == NIL)
6915dfecf96Smrg	LispDestroy("%s: bad argument %s", STRFUN(builtin), STROBJ(object));
6925dfecf96Smrg    else
6935dfecf96Smrg	RETURN(0) = NIL;
6945dfecf96Smrg
6955dfecf96Smrg    GC_LEAVE();
6965dfecf96Smrg
6975dfecf96Smrg    return (result);
6985dfecf96Smrg}
6995dfecf96Smrg
7005dfecf96SmrgLispObj *
7015dfecf96SmrgLisp_MakePathname(LispBuiltin *builtin)
7025dfecf96Smrg/*
7035dfecf96Smrg make-pathname &key host device directory name type version defaults
7045dfecf96Smrg */
7055dfecf96Smrg{
7065dfecf96Smrg    GC_ENTER();
7075dfecf96Smrg    int length, alength;
7085dfecf96Smrg    char *string, pathname[PATH_MAX + 1];
7095dfecf96Smrg    LispObj *result, *cdr, *cons;
7105dfecf96Smrg
7115dfecf96Smrg    LispObj *host, *device, *directory, *name, *type, *version, *defaults;
7125dfecf96Smrg
7135dfecf96Smrg    defaults = ARGUMENT(6);
7145dfecf96Smrg    version = ARGUMENT(5);
7155dfecf96Smrg    type = ARGUMENT(4);
7165dfecf96Smrg    name = ARGUMENT(3);
7175dfecf96Smrg    directory = ARGUMENT(2);
7185dfecf96Smrg    device = ARGUMENT(1);
7195dfecf96Smrg    host = ARGUMENT(0);
7205dfecf96Smrg
7215dfecf96Smrg    if (host != UNSPEC) {
7225dfecf96Smrg	CHECK_STRING(host);
7235dfecf96Smrg    }
7245dfecf96Smrg    if (device != UNSPEC) {
7255dfecf96Smrg	CHECK_STRING(device);
7265dfecf96Smrg    }
7275dfecf96Smrg
7285dfecf96Smrg    if (directory != UNSPEC) {
7295dfecf96Smrg	LispObj *dir;
7305dfecf96Smrg
7315dfecf96Smrg	CHECK_CONS(directory);
7325dfecf96Smrg	dir = CAR(directory);
7335dfecf96Smrg	CHECK_KEYWORD(dir);
7345dfecf96Smrg	if (dir != Kabsolute && dir != Krelative)
7355dfecf96Smrg	    LispDestroy("%s: directory type %s unknown",
7365dfecf96Smrg			STRFUN(builtin), STROBJ(dir));
7375dfecf96Smrg    }
7385dfecf96Smrg
7395dfecf96Smrg    if (name != UNSPEC) {
7405dfecf96Smrg	CHECK_STRING(name);
7415dfecf96Smrg    }
7425dfecf96Smrg    if (type != UNSPEC) {
7435dfecf96Smrg	CHECK_STRING(type);
7445dfecf96Smrg    }
7455dfecf96Smrg
7465dfecf96Smrg    if (version != UNSPEC && version != NIL) {
7475dfecf96Smrg	switch (OBJECT_TYPE(version)) {
7485dfecf96Smrg	    case LispFixnum_t:
7495dfecf96Smrg		if (FIXNUM_VALUE(version) >= 0)
7505dfecf96Smrg		    goto version_ok;
7515dfecf96Smrg	    case LispInteger_t:
7525dfecf96Smrg		if (INT_VALUE(version) >= 0)
7535dfecf96Smrg		    goto version_ok;
7545dfecf96Smrg		break;
7555dfecf96Smrg	    case LispDFloat_t:
7565dfecf96Smrg		if (DFLOAT_VALUE(version) >= 0.0)
7575dfecf96Smrg		    goto version_ok;
7585dfecf96Smrg		break;
7595dfecf96Smrg	    default:
7605dfecf96Smrg		break;
7615dfecf96Smrg	}
7625dfecf96Smrg	LispDestroy("%s: %s is not a positive real number",
7635dfecf96Smrg		    STRFUN(builtin), STROBJ(version));
7645dfecf96Smrg    }
7655dfecf96Smrgversion_ok:
7665dfecf96Smrg
7675dfecf96Smrg    if (defaults != UNSPEC && !PATHNAMEP(defaults) &&
7685dfecf96Smrg	(host == UNSPEC || device == UNSPEC || directory == UNSPEC ||
7695dfecf96Smrg	 name == UNSPEC || type == UNSPEC || version == UNSPEC)) {
7705dfecf96Smrg	defaults = APPLY1(Oparse_namestring, defaults);
7715dfecf96Smrg	GC_PROTECT(defaults);
7725dfecf96Smrg    }
7735dfecf96Smrg
7745dfecf96Smrg    if (defaults != UNSPEC) {
7755dfecf96Smrg	defaults = defaults->data.pathname;
7765dfecf96Smrg	defaults = CDR(defaults);	/* host */
7775dfecf96Smrg	if (host == UNSPEC)
7785dfecf96Smrg	    host = CAR(defaults);
7795dfecf96Smrg	defaults = CDR(defaults);	/* device */
7805dfecf96Smrg	if (device == UNSPEC)
7815dfecf96Smrg	    device = CAR(defaults);
7825dfecf96Smrg	defaults = CDR(defaults);	/* directory */
7835dfecf96Smrg	if (directory == UNSPEC)
7845dfecf96Smrg	    directory = CAR(defaults);
7855dfecf96Smrg	defaults = CDR(defaults);	/* name */
7865dfecf96Smrg	if (name == UNSPEC)
7875dfecf96Smrg	    name = CAR(defaults);
7885dfecf96Smrg	defaults = CDR(defaults);	/* type */
7895dfecf96Smrg	if (type == UNSPEC)
7905dfecf96Smrg	    type = CAR(defaults);
7915dfecf96Smrg	defaults = CDR(defaults);	/* version */
7925dfecf96Smrg	if (version == UNSPEC)
7935dfecf96Smrg	    version = CAR(defaults);
7945dfecf96Smrg    }
7955dfecf96Smrg
7965dfecf96Smrg    /* string representation */
7975dfecf96Smrg    length = 0;
7985dfecf96Smrg    if (CONSP(directory)) {
7995dfecf96Smrg	if (CAR(directory) == Kabsolute)
8005dfecf96Smrg	    pathname[length++] = PATH_SEP;
8015dfecf96Smrg
8025dfecf96Smrg	for (cdr = CDR(directory); CONSP(cdr); cdr = CDR(cdr)) {
8035dfecf96Smrg	    CHECK_STRING(CAR(cdr));
8045dfecf96Smrg	    string = THESTR(CAR(cdr));
8055dfecf96Smrg	    alength = STRLEN(CAR(cdr));
8065dfecf96Smrg	    if (alength > NAME_MAX)
8075dfecf96Smrg		LispDestroy("%s: directory name too long %s",
8085dfecf96Smrg			    STRFUN(builtin), string);
8095dfecf96Smrg	    if (length + alength + 2 > sizeof(pathname))
8105dfecf96Smrg		alength = sizeof(pathname) - length - 2;
8115dfecf96Smrg	    memcpy(pathname + length, string, alength);
8125dfecf96Smrg	    length += alength;
8135dfecf96Smrg	    pathname[length++] = PATH_SEP;
8145dfecf96Smrg	}
8155dfecf96Smrg    }
8165dfecf96Smrg    if (STRINGP(name)) {
8175dfecf96Smrg	int xlength = 0;
8185dfecf96Smrg
8195dfecf96Smrg	if (STRINGP(type))
8205dfecf96Smrg	    xlength = STRLEN(type) + 1;
8215dfecf96Smrg
8225dfecf96Smrg	string = THESTR(name);
8235dfecf96Smrg	alength = STRLEN(name);
8245dfecf96Smrg	if (alength + xlength > NAME_MAX)
8255dfecf96Smrg	    LispDestroy("%s: file name too long %s",
8265dfecf96Smrg			STRFUN(builtin), string);
8275dfecf96Smrg	if (length + alength + 2 > sizeof(pathname))
8285dfecf96Smrg	    alength = sizeof(pathname) - length - 2;
8295dfecf96Smrg	memcpy(pathname + length, string, alength);
8305dfecf96Smrg	length += alength;
8315dfecf96Smrg    }
8325dfecf96Smrg    if (STRINGP(type)) {
8335dfecf96Smrg	if (length + 2 < sizeof(pathname))
8345dfecf96Smrg	    pathname[length++] = PATH_TYPESEP;
8355dfecf96Smrg	string = THESTR(type);
8365dfecf96Smrg	alength = STRLEN(type);
8375dfecf96Smrg	if (length + alength + 2 > sizeof(pathname))
8385dfecf96Smrg	    alength = sizeof(pathname) - length - 2;
8395dfecf96Smrg	memcpy(pathname + length, string, alength);
8405dfecf96Smrg	length += alength;
8415dfecf96Smrg    }
8425dfecf96Smrg    pathname[length] = '\0';
8435dfecf96Smrg    result = cons = CONS(LSTRING(pathname, length), NIL);
8445dfecf96Smrg    GC_PROTECT(result);
8455dfecf96Smrg
8465dfecf96Smrg    /* host */
8475dfecf96Smrg    RPLACD(cons, CONS(host == UNSPEC ? NIL : host, NIL));
8485dfecf96Smrg    cons = CDR(cons);
8495dfecf96Smrg
8505dfecf96Smrg    /* device */
8515dfecf96Smrg    RPLACD(cons, CONS(device == UNSPEC ? NIL : device, NIL));
8525dfecf96Smrg    cons = CDR(cons);
8535dfecf96Smrg
8545dfecf96Smrg    /* directory */
8555dfecf96Smrg    if (directory == UNSPEC)
8565dfecf96Smrg	cdr = CONS(Krelative, NIL);
8575dfecf96Smrg    else
8585dfecf96Smrg	cdr = directory;
8595dfecf96Smrg    RPLACD(cons, CONS(cdr, NIL));
8605dfecf96Smrg    cons = CDR(cons);
8615dfecf96Smrg
8625dfecf96Smrg    /* name */
8635dfecf96Smrg    RPLACD(cons, CONS(name == UNSPEC ? NIL : name, NIL));
8645dfecf96Smrg    cons = CDR(cons);
8655dfecf96Smrg
8665dfecf96Smrg    /* type */
8675dfecf96Smrg    RPLACD(cons, CONS(type == UNSPEC ? NIL : type, NIL));
8685dfecf96Smrg    cons = CDR(cons);
8695dfecf96Smrg
8705dfecf96Smrg    /* version */
8715dfecf96Smrg    RPLACD(cons, CONS(version == UNSPEC ? NIL : version, NIL));
8725dfecf96Smrg
8735dfecf96Smrg    GC_LEAVE();
8745dfecf96Smrg
8755dfecf96Smrg    return (PATHNAME(result));
8765dfecf96Smrg}
8775dfecf96Smrg
8785dfecf96SmrgLispObj *
8795dfecf96SmrgLisp_PathnameHost(LispBuiltin *builtin)
8805dfecf96Smrg/*
8815dfecf96Smrg pathname-host pathname
8825dfecf96Smrg */
8835dfecf96Smrg{
8845dfecf96Smrg    return (LispPathnameField(PATH_HOST, 0));
8855dfecf96Smrg}
8865dfecf96Smrg
8875dfecf96SmrgLispObj *
8885dfecf96SmrgLisp_PathnameDevice(LispBuiltin *builtin)
8895dfecf96Smrg/*
8905dfecf96Smrg pathname-device pathname
8915dfecf96Smrg */
8925dfecf96Smrg{
8935dfecf96Smrg    return (LispPathnameField(PATH_DEVICE, 0));
8945dfecf96Smrg}
8955dfecf96Smrg
8965dfecf96SmrgLispObj *
8975dfecf96SmrgLisp_PathnameDirectory(LispBuiltin *builtin)
8985dfecf96Smrg/*
8995dfecf96Smrg pathname-device pathname
9005dfecf96Smrg */
9015dfecf96Smrg{
9025dfecf96Smrg    return (LispPathnameField(PATH_DIRECTORY, 0));
9035dfecf96Smrg}
9045dfecf96Smrg
9055dfecf96SmrgLispObj *
9065dfecf96SmrgLisp_PathnameName(LispBuiltin *builtin)
9075dfecf96Smrg/*
9085dfecf96Smrg pathname-name pathname
9095dfecf96Smrg */
9105dfecf96Smrg{
9115dfecf96Smrg    return (LispPathnameField(PATH_NAME, 0));
9125dfecf96Smrg}
9135dfecf96Smrg
9145dfecf96SmrgLispObj *
9155dfecf96SmrgLisp_PathnameType(LispBuiltin *builtin)
9165dfecf96Smrg/*
9175dfecf96Smrg pathname-type pathname
9185dfecf96Smrg */
9195dfecf96Smrg{
9205dfecf96Smrg    return (LispPathnameField(PATH_TYPE, 0));
9215dfecf96Smrg}
9225dfecf96Smrg
9235dfecf96SmrgLispObj *
9245dfecf96SmrgLisp_PathnameVersion(LispBuiltin *builtin)
9255dfecf96Smrg/*
9265dfecf96Smrg pathname-version pathname
9275dfecf96Smrg */
9285dfecf96Smrg{
9295dfecf96Smrg    return (LispPathnameField(PATH_VERSION, 0));
9305dfecf96Smrg}
9315dfecf96Smrg
9325dfecf96SmrgLispObj *
9335dfecf96SmrgLisp_FileNamestring(LispBuiltin *builtin)
9345dfecf96Smrg/*
9355dfecf96Smrg file-namestring pathname
9365dfecf96Smrg */
9375dfecf96Smrg{
9385dfecf96Smrg    return (LispPathnameField(PATH_NAME, 1));
9395dfecf96Smrg}
9405dfecf96Smrg
9415dfecf96SmrgLispObj *
9425dfecf96SmrgLisp_DirectoryNamestring(LispBuiltin *builtin)
9435dfecf96Smrg/*
9445dfecf96Smrg directory-namestring pathname
9455dfecf96Smrg */
9465dfecf96Smrg{
9475dfecf96Smrg    return (LispPathnameField(PATH_DIRECTORY, 1));
9485dfecf96Smrg}
9495dfecf96Smrg
9505dfecf96SmrgLispObj *
9515dfecf96SmrgLisp_EnoughNamestring(LispBuiltin *builtin)
9525dfecf96Smrg/*
9535dfecf96Smrg enough-pathname pathname &optional defaults
9545dfecf96Smrg */
9555dfecf96Smrg{
9565dfecf96Smrg    LispObj *pathname, *defaults;
9575dfecf96Smrg
9585dfecf96Smrg    defaults = ARGUMENT(1);
9595dfecf96Smrg    pathname = ARGUMENT(0);
9605dfecf96Smrg
9615dfecf96Smrg    if (defaults != UNSPEC && defaults != NIL) {
9625dfecf96Smrg	char *ppathname, *pdefaults, *pp, *pd;
9635dfecf96Smrg
9645dfecf96Smrg	if (!STRINGP(pathname)) {
9655dfecf96Smrg	    if (PATHNAMEP(pathname))
9665dfecf96Smrg		pathname  = CAR(pathname->data.pathname);
9675dfecf96Smrg	    else if (STREAMP(pathname) &&
9685dfecf96Smrg		     pathname->data.stream.type == LispStreamFile)
9695dfecf96Smrg		pathname  = CAR(pathname->data.stream.pathname->data.pathname);
9705dfecf96Smrg	    else
9715dfecf96Smrg		LispDestroy("%s: bad PATHNAME %s",
9725dfecf96Smrg			    STRFUN(builtin), STROBJ(pathname));
9735dfecf96Smrg	}
9745dfecf96Smrg
9755dfecf96Smrg	if (!STRINGP(defaults)) {
9765dfecf96Smrg	    if (PATHNAMEP(defaults))
9775dfecf96Smrg		defaults  = CAR(defaults->data.pathname);
9785dfecf96Smrg	    else if (STREAMP(defaults) &&
9795dfecf96Smrg		     defaults->data.stream.type == LispStreamFile)
9805dfecf96Smrg		defaults  = CAR(defaults->data.stream.pathname->data.pathname);
9815dfecf96Smrg	    else
9825dfecf96Smrg		LispDestroy("%s: bad DEFAULTS %s",
9835dfecf96Smrg			    STRFUN(builtin), STROBJ(defaults));
9845dfecf96Smrg	}
9855dfecf96Smrg
9865dfecf96Smrg	ppathname = pp = THESTR(pathname);
9875dfecf96Smrg	pdefaults = pd = THESTR(defaults);
9885dfecf96Smrg	while (*ppathname && *pdefaults && *ppathname == *pdefaults) {
9895dfecf96Smrg	    ppathname++;
9905dfecf96Smrg	    pdefaults++;
9915dfecf96Smrg	}
9925dfecf96Smrg	if (*pdefaults == '\0' && pdefaults > pd)
9935dfecf96Smrg	    --pdefaults;
9945dfecf96Smrg	if (*ppathname && *pdefaults && *pdefaults != PATH_SEP) {
9955dfecf96Smrg	    --ppathname;
9965dfecf96Smrg	    while (*ppathname != PATH_SEP && ppathname > pp)
9975dfecf96Smrg		--ppathname;
9985dfecf96Smrg	    if (*ppathname == PATH_SEP)
9995dfecf96Smrg		++ppathname;
10005dfecf96Smrg	}
10015dfecf96Smrg
10025dfecf96Smrg	return (STRING(ppathname));
10035dfecf96Smrg    }
10045dfecf96Smrg    else {
10055dfecf96Smrg	if (STRINGP(pathname))
10065dfecf96Smrg	    return (pathname);
10075dfecf96Smrg	else if (PATHNAMEP(pathname))
10085dfecf96Smrg	    return (CAR(pathname->data.pathname));
10095dfecf96Smrg	else if (STREAMP(pathname)) {
10105dfecf96Smrg	    if (pathname->data.stream.type == LispStreamFile)
10115dfecf96Smrg		return (CAR(pathname->data.stream.pathname->data.pathname));
10125dfecf96Smrg	}
10135dfecf96Smrg    }
10145dfecf96Smrg    LispDestroy("%s: bad PATHNAME %s", STRFUN(builtin), STROBJ(pathname));
10155dfecf96Smrg
10165dfecf96Smrg    return (NIL);
10175dfecf96Smrg}
10185dfecf96Smrg
10195dfecf96SmrgLispObj *
10205dfecf96SmrgLisp_Namestring(LispBuiltin *builtin)
10215dfecf96Smrg/*
10225dfecf96Smrg namestring pathname
10235dfecf96Smrg */
10245dfecf96Smrg{
10255dfecf96Smrg    return (LispPathnameField(PATH_STRING, 1));
10265dfecf96Smrg}
10275dfecf96Smrg
10285dfecf96SmrgLispObj *
10295dfecf96SmrgLisp_HostNamestring(LispBuiltin *builtin)
10305dfecf96Smrg/*
10315dfecf96Smrg host-namestring pathname
10325dfecf96Smrg */
10335dfecf96Smrg{
10345dfecf96Smrg    return (LispPathnameField(PATH_HOST, 1));
10355dfecf96Smrg}
10365dfecf96Smrg
10375dfecf96SmrgLispObj *
10385dfecf96SmrgLisp_Pathnamep(LispBuiltin *builtin)
10395dfecf96Smrg/*
10405dfecf96Smrg pathnamep object
10415dfecf96Smrg */
10425dfecf96Smrg{
10435dfecf96Smrg    LispObj *object;
10445dfecf96Smrg
10455dfecf96Smrg    object = ARGUMENT(0);
10465dfecf96Smrg
10475dfecf96Smrg    return (PATHNAMEP(object) ? T : NIL);
10485dfecf96Smrg}
10495dfecf96Smrg
10505dfecf96Smrg/* XXX only checks if host is a string and only checks the HOME enviroment
10515dfecf96Smrg * variable */
10525dfecf96SmrgLispObj *
10535dfecf96SmrgLisp_UserHomedirPathname(LispBuiltin *builtin)
10545dfecf96Smrg/*
10555dfecf96Smrg user-homedir-pathname &optional host
10565dfecf96Smrg */
10575dfecf96Smrg{
10585dfecf96Smrg    GC_ENTER();
10595dfecf96Smrg    char *home = getenv("HOME"), data[PATH_MAX + 1];
106097f927b5Skamil    char sepstr[] = {PATH_SEP, '\0'};
10615dfecf96Smrg    LispObj *result;
10625dfecf96Smrg
10635dfecf96Smrg    LispObj *host;
10645dfecf96Smrg
10655dfecf96Smrg    host = ARGUMENT(0);
10665dfecf96Smrg
10675dfecf96Smrg    if (host != UNSPEC && !STRINGP(host))
10685dfecf96Smrg	LispDestroy("%s: bad hostname %s", STRFUN(builtin), STROBJ(host));
10695dfecf96Smrg
10705dfecf96Smrg    if (home) {
107197f927b5Skamil	strlcpy(data, home, sizeof(data));
107297f927b5Skamil	if (data[0] != '\0' && data[strlen(data) - 1] != PATH_SEP)
107397f927b5Skamil		strlcat(data, sepstr, sizeof(data));
107497f927b5Skamil    } else {
107597f927b5Skamil	data[0] = '\0';
10765dfecf96Smrg    }
10775dfecf96Smrg
107897f927b5Skamil    result = STRING(data);
10795dfecf96Smrg    GC_PROTECT(result);
10805dfecf96Smrg    result = APPLY1(Oparse_namestring, result);
10815dfecf96Smrg    GC_LEAVE();
10825dfecf96Smrg
10835dfecf96Smrg    return (result);
10845dfecf96Smrg}
10855dfecf96Smrg
10865dfecf96SmrgLispObj *
10875dfecf96SmrgLisp_Truename(LispBuiltin *builtin)
10885dfecf96Smrg{
10895dfecf96Smrg    return (LispProbeFile(builtin, 0));
10905dfecf96Smrg}
10915dfecf96Smrg
10925dfecf96SmrgLispObj *
10935dfecf96SmrgLisp_ProbeFile(LispBuiltin *builtin)
10945dfecf96Smrg{
10955dfecf96Smrg    return (LispProbeFile(builtin, 1));
10965dfecf96Smrg}
1097