PageRenderTime 50ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 2ms

/src/pathname.d

https://github.com/ynd/clisp-branch--ynd-devel
D | 8884 lines | 7261 code | 225 blank | 1398 comment | 1122 complexity | 32029ffd33114f5b6eee77a056d54472 MD5 | raw file
Possible License(s): LGPL-2.0, GPL-2.0, BSD-3-Clause
  1. /*
  2. * Pathnames for CLISP
  3. * Bruno Haible 1990-2005
  4. * Logical Pathnames: Marcus Daniels 16.9.1994
  5. * ANSI compliance, bugs: Sam Steingold 1998-2008
  6. * German comments translated into English: Stefan Kain 2002-01-03
  7. */
  8. #include "lispbibl.c"
  9. #ifdef WIN32_NATIVE
  10. #include "w32shell.c"
  11. #endif
  12. #include <string.h> /* declares strlen() */
  13. /* enable the following #define to debug pathname translations
  14. setting DEBUG_TRANSLATE_PATHNAME to a larger value results in more output
  15. WARNING: PRIN1 can trigger GC! BEWARE!
  16. define DEBUG_TRANSLATE_PATHNAME 1 */
  17. #if DEBUG_TRANSLATE_PATHNAME
  18. #define string_concat(x) (printf("[%d]string_concat(%d)\n",__LINE__,x),(string_concat)(x))
  19. #define DOUT(label,obj) OBJECT_OUT(obj,label)
  20. #define SDOUT(label,obj) printf("%d %s %s",__LINE__,label,STRING(obj));nobject_out(stdout,obj)
  21. #else
  22. #define DOUT(l,o)
  23. #define SDOUT(l,o)
  24. #endif
  25. /* ========================================================================
  26. Low level functions */
  27. /* UP: Tests whether a pathname is possibly a symlink.
  28. possible_symlink(path) */
  29. #ifdef UNIX_LINUX
  30. local inline bool possible_symlink (const char* path) {
  31. /* In Linux 2.0.35, /proc/<pid>/{cwd,exe,root} and /proc/<pid>/fd/<n>
  32. are symlinks pointing to void. Treat them like non-symlinks, in order
  33. to avoid errors. */
  34. if (path[0]=='/'
  35. && path[1]=='p' && path[2]=='r' && path[3]=='o' && path[4]=='c'
  36. && path[5]=='/'
  37. && (path[6]>='0' && path[6]<='9'))
  38. return false;
  39. return true;
  40. }
  41. #else
  42. #define possible_symlink(path) true
  43. #endif
  44. #ifdef UNIX_LINUX
  45. /* The Linux /proc filesystem has some symlinks whose readlink value is
  46. zero-terminated: /proc/self in Linux 2.0.35, /proc/<pid>/fd/<n> in
  47. Linux 2.2.2. Remove this extraneous trailing zero byte. */
  48. local inline int my_readlink (const char* path, char* buf, size_t bufsiz) {
  49. var int linklen = readlink(path,buf,bufsiz);
  50. if (linklen > 0 && buf[linklen-1] == '\0')
  51. linklen--;
  52. return linklen;
  53. }
  54. #define readlink my_readlink
  55. #endif
  56. /* we need realpath() (declared in <stdlib.h>, included under STDC_HEADERS)
  57. http://www.opengroup.org/onlinepubs/009695399/functions/realpath.html
  58. which is alleged to be broken on some systems
  59. OTOH, on some other systems, notably on cygwin,
  60. we _do_ need the system implementation of realpath
  61. because otherwise we get screwed on /proc/self/exe -> lisp
  62. instead of lisp.exe and possibly other quirks */
  63. #if defined(UNIX) && !defined(HAVE_REALPATH)
  64. /* library-function realpath implementation:
  65. [Copyright: SUN Microsystems, B. Haible]
  66. TITLE
  67. REALPATH(3)
  68. SYNOPSIS
  69. char* realpath (const char* path, char resolved_path[MAXPATHLEN]);
  70. DESCRIPTION
  71. realpath() expands all symbolic links and resolves refer-
  72. ences to '/./', '/../' and extra '/' characters in the null
  73. terminated string named by path and stores the canonicalized
  74. absolute pathname in the buffer named by resolved_path. The
  75. resulting path will have no symbolic links components, nor
  76. any '/./' or '/../' components.
  77. RETURN VALUES
  78. realpath() returns a pointer to the resolved_path on suc-
  79. cess. On failure, it returns NULL, sets errno to indicate
  80. the error, and places in resolved_path the absolute pathname
  81. of the path component which could not be resolved. */
  82. #define realpath my_realpath /* avoid conflict with Consensys realpath declaration */
  83. local char* realpath (const char* path, char* resolved_path) {
  84. /* Method: use getwd and readlink. */
  85. var char mypath[MAXPATHLEN];
  86. var int symlinkcount = 0; /* the number of symbolic links so far */
  87. var char* resolved_limit = &resolved_path[MAXPATHLEN-1];
  88. /* Valid pointers are those with resolved_path <= ptr <= resolved_limit.
  89. in *resolved_limit at most one null byte.
  90. (similarly with mypath.) */
  91. var char* resolve_start;
  92. {
  93. var char* resolved_ptr = resolved_path; /* always <= resolved_limit */
  94. /* poss. use Working-Directory: */
  95. if (!(path[0]=='/')) { /* not an absolute pathname? */
  96. if (getwd(resolved_path) == NULL)
  97. return NULL;
  98. resolved_ptr = resolved_path;
  99. while (*resolved_ptr) {
  100. resolved_ptr++;
  101. }
  102. if (resolved_ptr < resolved_limit) {
  103. *resolved_ptr++ = '/';
  104. }
  105. resolve_start = resolved_ptr;
  106. } else {
  107. resolve_start = resolved_ptr = &resolved_path[0];
  108. }
  109. /* copy the path: */
  110. var const char* path_ptr = path;
  111. while ((resolved_ptr < resolved_limit) && *path_ptr) {
  112. *resolved_ptr++ = *path_ptr++;
  113. }
  114. /* finish with '/' and a null: */
  115. if (resolved_ptr < resolved_limit) {
  116. *resolved_ptr++ = '/';
  117. }
  118. *resolved_ptr = 0;
  119. }
  120. /* Now start in resolved_path at resolve_start. */
  121. var char* from_ptr = resolve_start;
  122. var char* to_ptr = resolve_start;
  123. while ((to_ptr < resolved_limit) && (*from_ptr)) {
  124. /* so far the path in resolved_path[0]...to_ptr[-1]
  125. has the shape '/subdir1/subdir2/.../txt',
  126. whereas 'txt' is poss. empty, but no subdir is empty. */
  127. var char next = *from_ptr++; *to_ptr++ = next;
  128. if ((next == '/') && (to_ptr > resolved_path+1)) {
  129. /* to_ptr[-1]='/' -> resolve Directory ...to_ptr[-2] : */
  130. var char* last_subdir_end = &to_ptr[-2];
  131. switch (*last_subdir_end) {
  132. case '/':
  133. #ifdef PATHNAME_UNIX_UNC
  134. if (to_ptr > resolved_path+2)
  135. #endif
  136. /* '//' is simplified to '/' : */
  137. to_ptr--;
  138. break;
  139. case '.':
  140. {
  141. var char* last_subdir_ptr = &last_subdir_end[-1];
  142. if (to_ptr > resolved_path+2) {
  143. if (*last_subdir_ptr == '.') {
  144. if ((to_ptr > resolved_path+4)
  145. && (*--last_subdir_ptr == '/')) {
  146. /* last subdir was '/../'
  147. Therefore remove the subdir in front of it: */
  148. while ((last_subdir_ptr > resolved_path)
  149. && !(*--last_subdir_ptr == '/'));
  150. to_ptr = last_subdir_ptr+1;
  151. }
  152. } else if (*last_subdir_ptr == '/') {
  153. /* last subdir was '/./'
  154. remove: */
  155. to_ptr = last_subdir_end;
  156. }
  157. }
  158. }
  159. break;
  160. default:
  161. /* after a normal subdir */
  162. #ifdef HAVE_READLINK
  163. if (possible_symlink(resolved_path)) {
  164. /* read symbolic link: */
  165. to_ptr[-1]=0; /* replace '/' with 0 */
  166. #ifdef UNIX_CYGWIN32
  167. /* readlink() does not work right on NFS mounted directories
  168. (it returns -1,ENOENT or -1,EIO).
  169. So check for a directory first. */
  170. var struct stat statbuf;
  171. if (lstat(resolved_path,&statbuf) < 0)
  172. return NULL; /* error */
  173. if (S_ISDIR(statbuf.st_mode)) {
  174. /* directory, not a symbolic link */
  175. to_ptr[-1] = '/'; /* insert the '/' again */
  176. } else if (!S_ISLNK(statbuf.st_mode)) {
  177. /* something else, but not a directory or symbolic link. */
  178. errno = ENOTDIR;
  179. return NULL;
  180. } else
  181. #endif
  182. {
  183. var int linklen =
  184. readlink(resolved_path,mypath,sizeof(mypath)-1);
  185. if (linklen >=0) { /* was a symbolic link */
  186. if (++symlinkcount > MAXSYMLINKS) {
  187. errno = ELOOP_VALUE; return NULL;
  188. }
  189. { /* append the still to be resolved part of path
  190. to the link-content: */
  191. var char* mypath_ptr = &mypath[linklen]; /* here is room */
  192. var char* mypath_limit = &mypath[MAXPATHLEN-1]; /* up to here */
  193. if (mypath_ptr < mypath_limit) { *mypath_ptr++ = '/'; } /* first, append a '/' */
  194. /* then the rest: */
  195. while ((mypath_ptr <= mypath_limit)
  196. && (*mypath_ptr = *from_ptr++))
  197. { mypath_ptr++; }
  198. *mypath_ptr = 0; /* and conclude wit 0 */
  199. }
  200. /* this replaces resp. completes the path: */
  201. if (mypath[0] == '/') { /* replaces the path: */
  202. from_ptr = &mypath[0]; to_ptr = resolved_path;
  203. while ((*to_ptr++ = *from_ptr++));
  204. from_ptr = resolved_path;
  205. } else { /* completes the path:
  206. disrcard link-name. Therefore search for the last '/': */
  207. {
  208. var char* ptr = &to_ptr[-1];
  209. while ((ptr > resolved_path) && !(ptr[-1] == '/')) { ptr--; }
  210. from_ptr = ptr;
  211. }
  212. {
  213. var char* mypath_ptr = &mypath[0]; to_ptr = from_ptr;
  214. while ((to_ptr <= resolved_limit) && (*to_ptr++ = *mypath_ptr++));
  215. }
  216. }
  217. to_ptr = from_ptr;
  218. } else {
  219. #if defined(UNIX_IRIX)
  220. if ((errno == EINVAL) || (errno == ENXIO))
  221. #elif defined(UNIX_CYGWIN32)
  222. if ((errno == EINVAL) || (errno == EACCES))
  223. #else
  224. if (errno == EINVAL)
  225. #endif
  226. /* no symbolic link */
  227. to_ptr[-1] = '/'; /* insert the '/' again */
  228. else
  229. return NULL; /* error */
  230. }
  231. }
  232. }
  233. #endif
  234. break;
  235. }
  236. }
  237. } /* go for the next subdir */
  238. /* discard a '/' at the tail: */
  239. if ((to_ptr[-1] == '/')
  240. #ifdef PATHNAME_UNIX_UNC
  241. && (to_ptr > resolved_path+2)
  242. #else
  243. && (to_ptr > resolved_path+1)
  244. #endif
  245. )
  246. to_ptr--;
  247. to_ptr[0] = 0; /* conclude with 0 */
  248. return resolved_path; /* finished */
  249. }
  250. #endif
  251. /* Creates a new subdirectory.
  252. make_directory(pathstring);
  253. > pathstring: result of shorter_directory(...)
  254. > STACK_0: pathname */
  255. local inline void make_directory (char* pathstring) {
  256. #ifdef UNIX
  257. begin_system_call();
  258. if (mkdir(pathstring,0777)) { /* create sub-directory */
  259. end_system_call(); OS_file_error(STACK_0);
  260. }
  261. end_system_call();
  262. #endif
  263. #ifdef WIN32_NATIVE
  264. begin_system_call();
  265. if (! CreateDirectory(pathstring,NULL) ) { /* create sub-directory */
  266. end_system_call(); OS_file_error(STACK_0);
  267. }
  268. end_system_call();
  269. #endif
  270. }
  271. /* Deletes a subdirectory.
  272. delete_directory(pathstring);
  273. > pathstring: result of shorter_directory(...)
  274. > STACK_0: pathname */
  275. local inline void delete_directory (char* pathstring) {
  276. #ifdef UNIX
  277. begin_system_call();
  278. if (rmdir(pathstring)) { /* delete sub-directory */
  279. end_system_call(); OS_file_error(STACK_0);
  280. }
  281. end_system_call();
  282. #endif
  283. #ifdef WIN32_NATIVE
  284. begin_system_call();
  285. if (! RemoveDirectory(pathstring) ) { /* delete sub-directory */
  286. end_system_call(); OS_file_error(STACK_0);
  287. }
  288. end_system_call();
  289. #endif
  290. }
  291. #ifdef WIN32_NATIVE
  292. /* Changes the operating system's current directory.
  293. change_directory(pathstring);
  294. > pathstring: directory, ASCIZ-String
  295. > STACK_0: pathname */
  296. local inline void change_current_directory (char* pathstring) {
  297. begin_system_call();
  298. if (!SetCurrentDirectory(pathstring)) {
  299. end_system_call(); OS_file_error(STACK_0);
  300. }
  301. end_system_call();
  302. }
  303. #endif
  304. /* Delete a file.
  305. delete_existing_file(pathstring);
  306. It is known that the file exists.
  307. > pathstring: file name, ASCIZ-String
  308. > STACK_0: pathname */
  309. local inline void delete_existing_file (char* pathstring) {
  310. #ifdef UNIX
  311. begin_system_call();
  312. if (!( unlink(pathstring) ==0)) {
  313. end_system_call(); OS_file_error(STACK_0);
  314. }
  315. end_system_call();
  316. #endif
  317. #ifdef WIN32_NATIVE
  318. begin_system_call();
  319. if (! DeleteFile(pathstring) ) {
  320. end_system_call(); OS_file_error(STACK_0);
  321. }
  322. end_system_call();
  323. #endif
  324. }
  325. #ifdef WIN32_NATIVE
  326. #define WIN32_ERROR_NOT_FOUND (GetLastError()==ERROR_FILE_NOT_FOUND || GetLastError()==ERROR_PATH_NOT_FOUND || GetLastError()==ERROR_BAD_NETPATH)
  327. #endif
  328. /* Delete a file.
  329. delete_file_if_exists(pathstring);
  330. No error is signaled if the file does not exist.
  331. > pathstring: file name, ASCIZ-String
  332. > STACK_0: pathname
  333. < result: whether the file existed */
  334. local inline bool delete_file_if_exists (char* pathstring) {
  335. var bool exists = true;
  336. #ifdef UNIX
  337. begin_system_call();
  338. if (!( unlink(pathstring) ==0)) {
  339. if (!(errno==ENOENT)) { /* not found -> OK */
  340. end_system_call(); OS_file_error(STACK_0); /* report other error */
  341. }
  342. exists = false;
  343. }
  344. end_system_call();
  345. #endif
  346. #ifdef WIN32_NATIVE
  347. begin_system_call();
  348. if (! DeleteFile(pathstring) ) {
  349. if (!WIN32_ERROR_NOT_FOUND) {
  350. end_system_call(); OS_file_error(STACK_0);
  351. }
  352. exists = false;
  353. }
  354. end_system_call();
  355. #endif
  356. return exists;
  357. }
  358. local bool delete_file_if_exists_obj (object namestring) {
  359. bool ret;
  360. with_sstring_0(namestring,O(pathname_encoding),namestring_asciz, {
  361. ret = delete_file_if_exists(namestring_asciz);
  362. });
  363. return ret;
  364. }
  365. /* Delete a file being the target of a subsequent rename.
  366. delete_file_before_rename(pathstring);
  367. No error is signaled if the file does not exist.
  368. > pathstring: file name, ASCIZ-String
  369. > STACK_0: pathname */
  370. local inline void delete_file_before_rename (char* pathstring) {
  371. #if !defined(UNIX) /* rename() on Unix does it automatically */
  372. delete_file_if_exists(pathstring);
  373. #endif
  374. }
  375. /* Rename a file.
  376. rename_existing_file(old_pathstring,new_pathstring);
  377. It is known that the old_pathstring exists.
  378. On platforms except UNIX, it is known that new_pathstring does not exist.
  379. > old_pathstring: old file name, ASCIZ-String
  380. > new_pathstring: new file name, ASCIZ-String
  381. > STACK_0: pathname */
  382. local inline void rename_existing_file (char* old_pathstring,
  383. char* new_pathstring) {
  384. #ifdef UNIX
  385. begin_system_call();
  386. if ( rename(old_pathstring,new_pathstring) <0) { /* rename file */
  387. end_system_call(); OS_file_error(STACK_0); /* report error */
  388. }
  389. end_system_call();
  390. #endif
  391. #ifdef WIN32_NATIVE
  392. begin_system_call();
  393. if (! MoveFile(old_pathstring,new_pathstring) ) {
  394. end_system_call(); OS_file_error(STACK_0);
  395. }
  396. end_system_call();
  397. #endif
  398. }
  399. /* ========================================================================
  400. P A T H N A M E S
  401. All simple-strings occurring in pathnames are in fact
  402. normal-simple-strings.
  403. #ifdef PATHNAME_UNIX
  404. Components:
  405. HOST always NIL
  406. DEVICE always NIL
  407. DIRECTORY (Startpoint . Subdirs) whereas
  408. Startpoint = :RELATIVE | :ABSOLUTE
  409. Subdirs = () | (subdir . Subdirs)
  410. subdir = :WILD-INFERIORS (means "**" or "...", all subdirectories) or
  411. subdir = Simple-String, poss. with wildcard-character ? and *
  412. NAME NIL or
  413. Simple-String, poss. with wildcard-character ? and *
  414. (also :WILD on input)
  415. TYPE NIL or
  416. Simple-String, poss. with wildcard-character ? and *
  417. (also :WILD on input)
  418. VERSION always NIL (also :WILD or :NEWEST on input)
  419. A UNIX-filename is split in Name and Type as follows:
  420. if there is no '.' in Filename: Name = everything, Type = NIL,
  421. if there is '.' in Filename: Name = everything in front of it, Type = everything behind the last '.' .
  422. If a pathname must be completely specified (no wildcards),
  423. :WILD, :WILD-INFERIORS are not allowed, no wildcard-characters in the
  424. Strings, at NAME poss. also not NIL.
  425. External Notation: server:/sub1.typ/sub2.typ/name.typ
  426. with Defaults: /sub1.typ/sub2.typ/name.typ
  427. or name.typ
  428. or /sub1.typ/ ** /sub3.typ/x*.lisp (without Spaces!)
  429. or similar.
  430. If NAME starts with a dot, (parse-namestring (namestring pathname)) will not
  431. be the same as pathname.
  432. #endif
  433. #ifdef PATHNAME_WIN32
  434. Components:
  435. HOST NIL or Simple-String (Wildcard-Characters are without meaning)
  436. DEVICE NIL or :WILD or "A"|...|"Z"
  437. DIRECTORY (Startpoint . Subdirs) whereas
  438. Startpoint = :RELATIVE | :ABSOLUTE
  439. Subdirs = () | (subdir . Subdirs)
  440. subdir = :WILD-INFERIORS (means "**" or "...", all Subdirectories) or
  441. subdir = Simple-String, poss. with Wildcard-Character ? and *
  442. NAME NIL or
  443. Simple-String, poss. with Wildcard-Character ? and *
  444. (also :WILD on input)
  445. TYPE NIL or
  446. Simple-String, poss. with Wildcard-Character ? and *
  447. (also :WILD on input)
  448. VERSION always NIL (also :WILD or :NEWEST on input)
  449. If HOST is non-NIL, DEVICE must be NIL.
  450. A WIN32-Filename is split into Name and Type as follows:
  451. if there is no '.' in Filename: Name = everything, Type = NIL,
  452. if there is a '.' in Filename: Name = everything in front of, Type = everything behind the last '.' .
  453. If a Pathname must be completely specified (no Wildcards),
  454. then :WILD, :WILD-INFERIORS are not allowed, no Wildcard-Characters in the
  455. Strings, at NAME poss. also not NIL.
  456. External notation: A:\sub1.typ\sub2.typ\name.typ
  457. with Defaults: \sub1.typ\sub2.typ\name.typ
  458. or name.typ
  459. or *:\sub1.typ\**\sub3.typ\x*.lisp
  460. or similar.
  461. Instead of '\' - traditionally on DOS - also '/' is allowed.
  462. If HOST is non-NIL and the DIRECTORY's Startpoint is not :ABSOLUTE,
  463. (parse-namestring (namestring pathname)) will not be the same as pathname.
  464. #endif
  465. #ifdef LOGICAL_PATHNAMES
  466. Components of Logical Pathnames:
  467. HOST Simple-String or NIL
  468. DEVICE always NIL
  469. DIRECTORY (Startpoint . Subdirs) whereas
  470. Startpoint = :RELATIVE | :ABSOLUTE
  471. Subdirs = () | (subdir . Subdirs)
  472. subdir = :WILD-INFERIORS (means "**", all Subdirectories) or
  473. subdir = :WILD (means "*") or
  474. subdir = Simple-String, poss. with Wildcard-Character *
  475. NAME NIL or
  476. :WILD (means "*") or
  477. Simple-String, poss. with Wildcard-Character *
  478. TYPE NIL or
  479. :WILD (means "*") or
  480. Simple-String, poss. with Wildcard-Character *
  481. VERSION NIL or :NEWEST or :WILD or Integer
  482. External Notation: see CLtl2 p. 628-629.
  483. #endif
  484. access functions without case transforms:
  485. xpathname_host(logical,pathname)
  486. xpathname_device(logical,pathname)
  487. xpathname_directory(logical,pathname)
  488. xpathname_name(logical,pathname)
  489. xpathname_type(logical,pathname)
  490. xpathname_version(logical,pathname)
  491. > pathname: pathname or logical pathname
  492. > logical: flag = logpathnamep(pathname)
  493. < result: the value of the requested component
  494. pathname_*_maybe return the appropriate slot seen from the point of view of the
  495. underlying physical file system, therefore, ever though pathname has the slot
  496. version (for ANSI compliance reasons), pathname_version_maybe() returns NIL */
  497. #if HAS_HOST
  498. #define pathname_host_maybe(obj) (object)ThePathname(obj)->pathname_host
  499. #else
  500. #define pathname_host_maybe(obj) (unused(obj), NIL)
  501. #endif
  502. #if HAS_DEVICE
  503. #define pathname_device_maybe(obj) (object)ThePathname(obj)->pathname_device
  504. #else
  505. #define pathname_device_maybe(obj) (unused(obj), NIL)
  506. #endif
  507. #if HAS_VERSION
  508. #define pathname_version_maybe(obj) (object)ThePathname(obj)->pathname_version
  509. #else
  510. #define pathname_version_maybe(obj) (unused(obj), NIL)
  511. #endif
  512. #ifdef LOGICAL_PATHNAMES
  513. #define xpathname_host(logical,pathname) \
  514. (logical ? (object)TheLogpathname(pathname)->pathname_host : \
  515. pathname_host_maybe(pathname))
  516. #define xpathname_device(logical,pathname) \
  517. (logical ? NIL : pathname_device_maybe(pathname))
  518. #define xpathname_directory(logical,pathname) \
  519. (logical ? (object)TheLogpathname(pathname)->pathname_directory : \
  520. (object)ThePathname(pathname)->pathname_directory)
  521. #define xpathname_name(logical,pathname) \
  522. (logical ? (object)TheLogpathname(pathname)->pathname_name : \
  523. (object)ThePathname(pathname)->pathname_name)
  524. #define xpathname_type(logical,pathname) \
  525. (logical ? (object)TheLogpathname(pathname)->pathname_type : \
  526. (object)ThePathname(pathname)->pathname_type)
  527. #define xpathname_version(logical,pathname) \
  528. (logical ? (object)TheLogpathname(pathname)->pathname_version : \
  529. (object)ThePathname(pathname)->pathname_version)
  530. #else /* no logical pathnames */
  531. #define xpathname_host(logical,pathname) \
  532. pathname_host_maybe(pathname)
  533. #define xpathname_device(logical,pathname) \
  534. pathname_device_maybe(pathname)
  535. #define xpathname_directory(logical,pathname) \
  536. ThePathname(pathname)->pathname_directory
  537. #define xpathname_name(logical,pathname) \
  538. ThePathname(pathname)->pathname_name
  539. #define xpathname_type(logical,pathname) \
  540. ThePathname(pathname)->pathname_type
  541. #define xpathname_version(logical,pathname) \
  542. ThePathname(pathname)->pathname_version
  543. #endif
  544. #define SUBST_RECURSE(atom_form,self_call) \
  545. if (atomp(obj)) return atom_form; \
  546. check_STACK(); check_SP(); \
  547. pushSTACK(obj); \
  548. { /* recursive call for CAR: */ \
  549. object new_car = self_call(Car(obj)); \
  550. pushSTACK(new_car); \
  551. } \
  552. { /* recursive call for CDR: */ \
  553. object new_cdr = self_call(Cdr(STACK_1)); \
  554. if (eq(new_cdr,Cdr(STACK_1)) && eq(STACK_0,Car(STACK_1))) { \
  555. obj = STACK_1; skipSTACK(2); return obj; \
  556. } else { /* (CONS new_car new_cdr) */ \
  557. STACK_1 = new_cdr; \
  558. {object new_cons = allocate_cons(); \
  559. Car(new_cons) = popSTACK(); Cdr(new_cons) = popSTACK(); \
  560. return new_cons; \
  561. }} \
  562. }
  563. /* Converts capital-/small letters between :LOCAL and :COMMON .
  564. common_case(string)
  565. > string: Normal-Simple-String or Symbol/Number
  566. < result: converted Normal-Simple-String or the same Symbol/Number
  567. can trigger GC
  568. Operating System with preference for small letters or Capitalize */
  569. local maygc object common_case (object string) {
  570. if (!simple_string_p(string))
  571. return string;
  572. var uintL len = Sstring_length(string);
  573. /* Search, if capital- or small letters (or both) occur: */
  574. var bool all_upper = true;
  575. var bool all_lower = true;
  576. if (len > 0) {
  577. var object storage = string; sstring_un_realloc(storage);
  578. SstringDispatch(storage,X, {
  579. var const cintX* ptr = &((SstringX)TheVarobject(storage))->data[0];
  580. var uintL count;
  581. dotimespL(count,len, {
  582. var chart ch = as_chart(*ptr++);
  583. if (!chareq(ch,up_case(ch)))
  584. all_upper = false;
  585. if (!chareq(ch,down_case(ch)))
  586. all_lower = false;
  587. if (!all_upper && !all_lower)
  588. break;
  589. });
  590. });
  591. }
  592. if (all_upper == all_lower)
  593. /* all_upper = all_lower = true: Nothing to convert.
  594. all_upper = all_lower = false: "Mixed case represents itself." */
  595. return string;
  596. if (all_upper)
  597. /* all_upper = true, all_lower = false: STRING-DOWNCASE */
  598. return string_downcase(string);
  599. else
  600. /* all_upper = false, all_lower = true: STRING-UPCASE */
  601. return string_upcase(string);
  602. }
  603. /* the same, recursive like with SUBST: */
  604. local object subst_common_case (object obj) {
  605. SUBST_RECURSE(common_case(obj),subst_common_case);
  606. }
  607. #ifdef LOGICAL_PATHNAMES
  608. local bool legal_logical_word_char (chart ch) {
  609. ch = up_case(ch);
  610. var cint c = as_cint(ch);
  611. return (((c >= 'A') && (c <= 'Z'))
  612. || ((c >= '0') && (c <= '9'))
  613. || (c == '-'));
  614. }
  615. #endif
  616. #if HAS_HOST
  617. /* UP: Determines, if a character is allowed as character in the host-part
  618. of a namestring.
  619. legal_hostchar(ch)
  620. > chart ch: Character-Code
  621. < result: true if allowed, else false
  622. NB: legal_logical_word_char(ch) implies legal_hostchar(ch). */
  623. local bool legal_hostchar (chart ch) {
  624. #if defined(PATHNAME_WIN32)
  625. { /* This is just a guess. I do not know which characters are allowed in
  626. Windows host names. */
  627. var cint c = as_cint(ch);
  628. return ((c >= ' ') && (c <= '~')
  629. && (c != '"') && (c != '/') && (c != ':')
  630. && (c != '<') && (c != '>') && (c != '\\'));
  631. }
  632. #else
  633. return alphanumericp(ch) || chareq(ch,ascii('-'));
  634. #endif
  635. }
  636. /* UP: check an optional HOST argument
  637. test_optional_host(host,convert)
  638. > host: Host-Argument
  639. > convert: Flag, if case-conversion is undesired
  640. < result: valid host-component
  641. can trigger GC */
  642. local maygc object test_optional_host (object host, bool convert) {
  643. if (!boundp(host) || eq(host,S(Kunspecific)))
  644. return NIL;
  645. if (nullp(host))
  646. goto OK; /* NIL is OK */
  647. /* Else, host must be a String, whose characters are alphanumeric: */
  648. if (!stringp(host)) {
  649. pushSTACK(host); /* TYPE-ERROR slot DATUM */
  650. pushSTACK(O(type_host)); /* TYPE-ERROR slot EXPECTED-TYPE */
  651. pushSTACK(host);
  652. pushSTACK(TheSubr(subr_self)->name);
  653. error(type_error,GETTEXT("~S: host should be NIL or a string, not ~S"));
  654. }
  655. host = coerce_normal_ss(host); /* as Normal-Simple-String */
  656. if (convert)
  657. host = common_case(host);
  658. {
  659. var uintL len = Sstring_length(host);
  660. if (len > 0) {
  661. var const chart* charptr = &TheSnstring(host)->data[0];
  662. dotimespL(len,len, {
  663. var chart ch = *charptr++;
  664. if (!legal_hostchar(ch))
  665. goto badhost;
  666. });
  667. }
  668. }
  669. OK: return host;
  670. badhost:
  671. pushSTACK(host);
  672. pushSTACK(TheSubr(subr_self)->name);
  673. error(parse_error,GETTEXT("~S: illegal hostname ~S"));
  674. }
  675. #else
  676. #ifdef LOGICAL_PATHNAMES
  677. /* UP: check an optional HOST argument
  678. test_optional_host(host)
  679. > host: Host-Argument
  680. < result: valid host-component
  681. can trigger GC */
  682. local maygc object test_optional_host (object host) {
  683. if (!boundp(host) || eq(host,S(Kunspecific)))
  684. return NIL; /* not specified -> NIL */
  685. if (nullp(host))
  686. goto OK; /* NIL is OK */
  687. /* Else, host must be a String, whose characters are alphanumeric: */
  688. if (!stringp(host)) {
  689. pushSTACK(host); /* TYPE-ERROR slot DATUM */
  690. pushSTACK(O(type_host)); /* TYPE-ERROR slot EXPECTED-TYPE */
  691. pushSTACK(host);
  692. pushSTACK(TheSubr(subr_self)->name);
  693. error(type_error,GETTEXT("~S: host should be NIL or a string, not ~S"));
  694. }
  695. host = coerce_normal_ss(host); /* as Normal-Simple-String */
  696. {
  697. var uintL len = Sstring_length(host);
  698. if (len > 0) {
  699. var object storage = host; sstring_un_realloc(storage);
  700. SstringDispatch(storage,X, {
  701. var const cintX* ptr = &((SstringX)TheVarobject(storage))->data[0];
  702. dotimespL(len,len, {
  703. var chart ch = as_chart(*ptr++);
  704. if (!legal_logical_word_char(ch))
  705. goto badhost;
  706. });
  707. });
  708. }
  709. }
  710. OK: return host;
  711. badhost:
  712. pushSTACK(host);
  713. pushSTACK(TheSubr(subr_self)->name);
  714. error(parse_error,GETTEXT("~S: illegal hostname ~S"));
  715. }
  716. #else
  717. /* UP: check an optional HOST argument
  718. test_optional_host(host);
  719. > host: Host-Argument
  720. < result: valid host-component */
  721. local object test_optional_host (object host) {
  722. if (boundp(host) /* not specified -> OK */
  723. && !nullp(host) /* specified -> should be NIL or :UNSPECIFIC */
  724. && !eq(host,S(Kunspecific))) {
  725. pushSTACK(host); /* TYPE-ERROR slot DATUM */
  726. pushSTACK(S(null)); /* TYPE-ERROR slot EXPECTED-TYPE */
  727. pushSTACK(host);
  728. pushSTACK(TheSubr(subr_self)->name);
  729. error(type_error,GETTEXT("~S: host should be NIL, not ~S"));
  730. }
  731. return NIL;
  732. }
  733. #endif
  734. #endif
  735. /* Determines, if two characters count as equal characters in pathnames.
  736. equal_pathchar(ch1,ch2)
  737. > chart ch1,ch2: Character-Codes
  738. < result: true if equal, else false */
  739. #if !defined(PATHNAME_WIN32)
  740. #define equal_pathchar(ch1,ch2) chareq(ch1,ch2)
  741. #else /* defined(PATHNAME_WIN32) */
  742. /* Case-insensitive, but normally without conversion */
  743. #define equal_pathchar(ch1,ch2) chareq(up_case(ch1),up_case(ch2))
  744. #endif
  745. /* UP: check whether a given byte is a valid element of NAME or TYPE
  746. component in a Namestring
  747. legal_namebyte(ch)
  748. > uintB: byte
  749. < return: true if valid, else false */
  750. local inline bool legal_namebyte (uintB ch) {
  751. #ifdef VALID_FILENAME_CHAR /* defined in config.h */
  752. return VALID_FILENAME_CHAR || (ch=='*') || (ch=='?');
  753. #else
  754. #ifdef PATHNAME_UNIX
  755. return ((ch>=' ') && (ch<='~') && !(ch=='/'));
  756. #endif
  757. #ifdef PATHNAME_WIN32
  758. return ((ch >= 1) && (ch <= 127)
  759. && (ch != '"') /*&& (ch != '*')*/
  760. && (ch != '/') && (ch != ':')
  761. && (ch != '<') && (ch != '>') /*&& (ch != '?')*/
  762. && (ch != '\\'))
  763. || (ch == 131)
  764. || (ch >= 160);
  765. #endif
  766. #endif
  767. }
  768. /* UP: check whether the character is a valid element of NAME or TYPE
  769. component in a Namestring
  770. legal_namechar(ch)
  771. > chart ch: character-code
  772. < return: true if valid, else false */
  773. local bool legal_namechar (chart ch) {
  774. #ifdef UNICODE
  775. var uintB buf[4]; /* are there characters longer than 4 bytes?! */
  776. var uintL char_len = cslen(O(pathname_encoding),&ch,1);
  777. cstombs(O(pathname_encoding),&ch,1,buf,char_len);
  778. while (char_len > 0) {
  779. char_len--;
  780. if (!legal_namebyte(buf[char_len])) return false;
  781. }
  782. return true;
  783. #else
  784. return legal_namebyte(as_cint(ch));
  785. #endif
  786. }
  787. /* Determines, if a character is a wildcard for a single
  788. character.
  789. singlewild_char_p(ch)
  790. > chart ch: Character-Code
  791. < result: true if yes, else false */
  792. #define singlewild_char_p(ch) chareq(ch,ascii('?'))
  793. #define multiwild_char_p(ch) chareq(ch,ascii('*'))
  794. #define wild_char_p(ch) (multiwild_char_p(ch) || singlewild_char_p(ch))
  795. /* Converts an object into a pathname. */
  796. local object coerce_xpathname (object obj); /* later */
  797. /* Converts an object into a non-logical pathname. */
  798. local object coerce_pathname (object obj); /* later */
  799. #if !defined(LOGICAL_PATHNAMES)
  800. #define coerce_pathname(obj) coerce_xpathname(obj)
  801. #endif
  802. /* Returns a default-pathname. */
  803. local object defaults_pathname (void); /* later */
  804. /* checks a default-pathname.
  805. test_default_pathname(defaults)
  806. > defaults: defaults-argument
  807. < result: value of the defaults-argument, a pathname
  808. can trigger GC */
  809. local maygc object test_default_pathname (object defaults) {
  810. if (missingp(defaults))
  811. /* not specified -> take value of *DEFAULT-PATHNAME-DEFAULTS* : */
  812. return defaults_pathname();
  813. else
  814. /* specified -> turn into a pathname: */
  815. return coerce_xpathname(defaults);
  816. }
  817. /* <http://www.lisp.org/HyperSpec/Body/sec_19-2-3.html>:
  818. "for functions that manipulate or inquire about files in the file system,
  819. the pathname argument to such a function is merged with
  820. *DEFAULT-PATHNAME-DEFAULTS* before accessing the file system"
  821. When pathname comes from a file stream, this is NOT done because
  822. that pathname has already been "transfered from the world of the abstract
  823. Lisp pathname algebra to the real world of computer file system"
  824. Another option is to ensure that all slots of *DEFAULT-PATHNAME-DEFAULTS*
  825. are non-NIL (use :UNSPECIFIC instead): then merge_defaults() becomes
  826. an idempotent operation -- assuming trivial directory or non-ANSI merging.
  827. merge_defaults(pathname)
  828. > pathname: a pathname
  829. < result: a pathname derived from it, with *DEFAULT-PATHNAME-DEFAULTS* merged
  830. in.
  831. can trigger GC */
  832. local maygc object merge_defaults (object pathname) {
  833. pushSTACK(pathname); pushSTACK(defaults_pathname());
  834. funcall(L(merge_pathnames),2);
  835. return value1;
  836. }
  837. /* error-message because of illegal pathname-argument.
  838. error_pathname_designator(thing); ( error_... )
  839. > thing: (erroneous) argument */
  840. nonreturning_function(local, error_pathname_designator, (object thing)) {
  841. pushSTACK(thing); /* TYPE-ERROR slot DATUM */
  842. pushSTACK(O(type_designator_pathname)); /* TYPE-ERROR slot EXPECTED-TYPE */
  843. pushSTACK(O(type_designator_pathname));
  844. pushSTACK(thing);
  845. pushSTACK(TheSubr(subr_self)->name);
  846. error(type_error,
  847. GETTEXT("~S: argument ~S should be a pathname designator ~S"));
  848. }
  849. /* Tracks a chain of Synonym-Streams, so long as a File-Stream
  850. is reached.
  851. as_file_stream(stream)
  852. > stream: Builtin-Stream
  853. < stream: File-Stream */
  854. local object as_file_stream (object stream) {
  855. var object s = stream;
  856. while (1) {
  857. if (TheStream(s)->strmtype == strmtype_file)
  858. return s;
  859. if (!(TheStream(s)->strmtype == strmtype_synonym))
  860. break;
  861. s = Symbol_value(TheStream(stream)->strm_synonym_symbol);
  862. if (!builtin_stream_p(s))
  863. break;
  864. }
  865. error_pathname_designator(stream);
  866. }
  867. /* Signal an error if a file-stream does not have
  868. a file-name associated with it.
  869. test_file_stream_named(stream)
  870. > stream: File-Stream */
  871. #define test_file_stream_named(stream) \
  872. do { if (nullp(TheStream(stream)->strm_file_truename)) \
  873. error_file_stream_unnamed(stream); \
  874. } while(0)
  875. nonreturning_function(local, error_file_stream_unnamed, (object stream)) {
  876. pushSTACK(stream); /* FILE-ERROR slot PATHNAME */
  877. pushSTACK(stream);
  878. pushSTACK(TheSubr(subr_self)->name);
  879. error(file_error,GETTEXT("~S: filename for ~S is unknown"));
  880. }
  881. #if defined(UNIX) || defined(WIN32_NATIVE)
  882. #ifdef UNIX
  883. #define slash '/'
  884. #endif
  885. #ifdef WIN32_NATIVE
  886. #define slash '\\'
  887. #endif
  888. /* physical slash */
  889. #ifdef PATHNAME_WIN32
  890. #define pslashp(c) (chareq(c,ascii('\\')) || chareq(c,ascii('/')))
  891. #define cpslashp(c) ((c) == '\\' || (c) == '/')
  892. #else /* PATHNAME_UNIX */
  893. #define pslashp(c) chareq(c,ascii(slash))
  894. #define cpslashp(c) ((c) == slash)
  895. #endif
  896. #define colonp(c) chareq(c,ascii(':'))
  897. #ifndef LOGICAL_PATHNAMES
  898. #define lslashp(c) pslashp(c)
  899. #endif
  900. #define dotp(c) chareq(c,ascii('.'))
  901. #define starp(c) chareq(c,ascii('*'))
  902. /* UP: add a character to an ASCII string and return as a Lisp string
  903. can trigger GC */
  904. #ifdef UNICODE
  905. local /*maygc*/ object asciz_add_char (const char* chars, uintL len, char ch,
  906. object encoding)
  907. #else
  908. #define asciz_add_char(chars,len,ch,encoding) asciz_add_char_(chars,len,ch)
  909. local /*maygc*/ object asciz_add_char_ (const char* chars, uintL len, char ch)
  910. #endif
  911. {
  912. #ifdef UNICODE
  913. GCTRIGGER1(encoding);
  914. #else
  915. GCTRIGGER();
  916. #endif
  917. var DYNAMIC_ARRAY(buf,char,len+1);
  918. begin_system_call(); memcpy(buf,chars,len); end_system_call();
  919. buf[len] = ch;
  920. var object s = n_char_to_string(buf,len+1,encoding);
  921. FREE_DYNAMIC_ARRAY(buf);
  922. return s;
  923. }
  924. /* UP: Converts a Unix-Directory-Specification into a pathname.
  925. asciz_dir_to_pathname(path,encoding)
  926. > const char* path: path as ASCIZ-String
  927. > encoding: Encoding
  928. < result: as a pathname without name and type
  929. can trigger GC */
  930. #ifdef UNICODE
  931. local /*maygc*/ object asciz_dir_to_pathname(const char* path, object encoding)
  932. #else
  933. #define asciz_dir_to_pathname(path,encoding) asciz_dir_to_pathname_(path)
  934. local /*maygc*/ object asciz_dir_to_pathname_(const char* path)
  935. #endif
  936. {
  937. #ifdef UNICODE
  938. GCTRIGGER1(encoding);
  939. #else
  940. GCTRIGGER();
  941. #endif
  942. var object pathname;
  943. var uintL len = asciz_length(path); /* string length */
  944. /* if the String does not end with a '/' already, a '/' is added: */
  945. if ((len>0) && cpslashp(path[len-1]))
  946. pathname = n_char_to_string(path,len,encoding);
  947. else
  948. pathname = asciz_add_char(path,len,slash,encoding);
  949. /* and convert into a pathname: */
  950. return coerce_pathname(pathname);
  951. }
  952. #endif
  953. /* Type for PARSE-NAMESTRING:
  954. State while the string is being parsed character by character. */
  955. typedef struct {
  956. uintL index; /* index (incl. offset) */
  957. object FNindex; /* index as a fixnum */
  958. uintL count; /* number of the remaining characters */
  959. } zustand; /* "state" */
  960. /* Skip s characters. */
  961. #define Z_SHIFT(z,s) \
  962. do { (z).index += (s); (z).FNindex = fixnum_inc((z).FNindex,(s)); (z).count -= (s); } while(0)
  963. /* Tests whether the current character at Z satisfies pred. */
  964. #define Z_AT_SLASH(z,pred,st) \
  965. (((z).count != 0) && pred(schar(st,(z).index)))
  966. /* Replace this string with a substring. */
  967. #define Z_SUB(z,s) ((s) = subsstring((s),(z).index,(z).index+(z).count), (z).index = 0)
  968. #ifdef LOGICAL_PATHNAMES
  969. /* Parsing of logical pathnames. */
  970. /* separator between subdirs */
  971. #define semicolonp(c) (chareq(c,ascii(';')))
  972. #define lslashp(c) semicolonp(c)
  973. /* Copy LEN characters in string ORIG starting at ORIG_OFFSET to string DEST,
  974. starting at DEST_OFFSET, up-casing all characters. LEN is > 0. */
  975. local void copy_upcase (object dest, uintL dest_offset,
  976. object orig, uintL orig_offset, uintL len) {
  977. sstring_un_realloc(orig);
  978. SstringDispatch(orig,X1, {
  979. var cintX1* ptr1 = &((SstringX1)TheVarobject(orig))->data[orig_offset];
  980. sstring_un_realloc(dest);
  981. SstringDispatch(dest,X2, {
  982. var cintX2* ptr2 = &((SstringX2)TheVarobject(dest))->data[dest_offset];
  983. dotimespL(len,len, { *ptr2++ = as_cint(up_case(as_chart(*ptr1++))); });
  984. });
  985. });
  986. }
  987. /* Parses the name/type/version part (if subdirp=false) or a subdir part
  988. (if subdirp=true) of a logical pathname.
  989. parse_logical_word(&z,subdirp)
  990. > STACK_2: storage vector, a normal-simple-string
  991. > zustand z: start state
  992. < zustand z: updated
  993. < result: a normal-simple-string or :WILD or :WILD-INFERIORS or NIL
  994. can trigger GC */
  995. local maygc object parse_logical_word (zustand* z, bool subdirp) {
  996. ASSERT(sstring_normal_p(STACK_2));
  997. var zustand startz = *z; /* start-state */
  998. var chart ch;
  999. /* Is there a sequence of alphanumeric characters or '*',
  1000. no two '*' adjacent (except "**", if subdirp),
  1001. and, if subdirp, a ';' ? */
  1002. var bool last_was_star = false;
  1003. var bool seen_starstar = false;
  1004. while (z->count) {
  1005. ch = schar(STACK_2,z->index); /* next character */
  1006. if (!legal_logical_word_char(ch)) {
  1007. if (starp(ch)) {
  1008. if (last_was_star) {
  1009. if (subdirp && (z->index - startz.index == 1))
  1010. seen_starstar = true;
  1011. else
  1012. break; /* adjacent '*' are forbidden */
  1013. } else
  1014. last_was_star = true;
  1015. } else
  1016. break;
  1017. }
  1018. /* skip character: */
  1019. Z_SHIFT(*z,1);
  1020. }
  1021. var uintL len = z->index - startz.index;
  1022. if (subdirp) {
  1023. if ((z->count == 0) || !lslashp(ch)) {
  1024. *z = startz; return NIL; /* no ';' -> no subdir */
  1025. }
  1026. /* skip character ';' : */
  1027. Z_SHIFT(*z,1);
  1028. }
  1029. if (len==0)
  1030. return NIL;
  1031. else if ((len==1) && starp(schar(STACK_2,startz.index)))
  1032. return S(Kwild);
  1033. else if ((len==2) && seen_starstar)
  1034. return S(Kwild_inferiors);
  1035. else {
  1036. var object result = allocate_string(len);
  1037. copy_upcase(result,0,STACK_2,startz.index,len);
  1038. return result;
  1039. }
  1040. }
  1041. /* Test whether a string is a digit sequence.
  1042. all_digits(string)
  1043. > string: a normal-simple-string
  1044. < true if the string consists entirely of digits, else false */
  1045. local bool all_digits (object string) {
  1046. var uintL len = Sstring_length(string);
  1047. if (len > 0) {
  1048. var object storage = string; sstring_un_realloc(storage);
  1049. SstringDispatch(storage,X, {
  1050. var const cintX* ptr = &((SstringX)TheVarobject(storage))->data[0];
  1051. dotimespL(len,len, {
  1052. var cintX c = *ptr++;
  1053. if (!((c >= '0') && (c <= '9')))
  1054. return false;
  1055. });
  1056. });
  1057. }
  1058. return true;
  1059. }
  1060. /* test whether the string contains semicolons (and the rest being valid!),
  1061. thus appearing to be a logical pathname
  1062. > string: storage vector, a normal-simple-string
  1063. < result: true if the string contains semicolons */
  1064. local bool looks_logical_p (object string) {
  1065. var uintL len = Sstring_length(string);
  1066. var bool logical_p = false;
  1067. if (len > 0) {
  1068. SstringDispatch(string,X, {
  1069. var const cintX* charptr = &((SstringX)TheVarobject(string))->data[0];
  1070. do {
  1071. var chart ch = up_case(as_chart(*charptr++));
  1072. if (!legal_logical_word_char(ch)) {
  1073. if (semicolonp(ch))
  1074. logical_p = true;
  1075. else if (!colonp(ch) && !dotp(ch) && !starp(ch))
  1076. return false; /* invalid logical pathname char */
  1077. }
  1078. } while (--len);
  1079. });
  1080. }
  1081. return logical_p;
  1082. }
  1083. /* Attempt to parse a logical host name string, starting at a given state.
  1084. parse_logical_host_prefix(&z,string)
  1085. > string: storage vector, a normal-simple-string
  1086. > state z: start state
  1087. < state z: updated to point past the colon after the logical host
  1088. < result: logical host, or NIL
  1089. can trigger GC */
  1090. local maygc object parse_logical_host_prefix (zustand* zp, object string) {
  1091. ASSERT(sstring_normal_p(string));
  1092. var object host;
  1093. var uintL startindex = zp->index;
  1094. var chart ch;
  1095. /* a sequence of alphanumeric characters and then ':' */
  1096. while (1) {
  1097. if (zp->count==0)
  1098. return NIL; /* string already ended -> no host */
  1099. ch = schar(string,zp->index); /* next character */
  1100. if (!legal_logical_word_char(ch))
  1101. break;
  1102. /* go past alphanumeric character: */
  1103. Z_SHIFT(*zp,1);
  1104. }
  1105. if (!colonp(ch))
  1106. return NIL; /* no ':' -> no host */
  1107. { /* make host-string: */
  1108. var uintL len = zp->index - startindex;
  1109. pushSTACK(string);
  1110. host = allocate_string(len);
  1111. string = popSTACK();
  1112. /* and fill it: */
  1113. if (len > 0)
  1114. copy_upcase(host,0,string,startindex,len);
  1115. }
  1116. /* skip ':' */
  1117. Z_SHIFT(*zp,1);
  1118. return host;
  1119. }
  1120. /* CLHS for MAKE-PATHNAME: "Whenever a pathname is constructed the
  1121. components may be canonicalized if appropriate."
  1122. simplify the subdirectory list
  1123. strings are coerced to normal simple strings
  1124. the list should start with a valid startpoint (not checked!)
  1125. > dir : pathname directory list
  1126. < dir : the same list, destructively modified:
  1127. ".." or :back ==> :up
  1128. ... x "foo" :up y ... ==> ... x y ...
  1129. ... x ""/"." y ... ==> ... x y ...
  1130. :absolute :up ==> error
  1131. :wild-inferiors :up ==> error
  1132. can trigger GC */
  1133. local maygc object simplify_directory (object dir) {
  1134. if (!consp(dir)) return dir;
  1135. DOUT("simplify_directory:< ",dir);
  1136. pushSTACK(dir);
  1137. { /* kill ".", ".."->:up, coerce to normal simple strings */
  1138. var object curr = dir;
  1139. while (consp(curr) && consp(Cdr(curr))) {
  1140. var object next = Cdr(curr);
  1141. var object here = Car(next);
  1142. if (stringp(here)) {
  1143. if (vector_length(here)==0 || string_equal(here,O(dot_string))) {
  1144. Cdr(curr) = Cdr(next); /* drop "." and "" */
  1145. continue;
  1146. } else if (string_equal(here,O(wild_string))) {
  1147. Car(next) = S(Kwild);
  1148. curr = next;
  1149. continue;
  1150. } else if (string_equal(here,O(wildwild_string))) {
  1151. Car(next) = S(Kwild_inferiors);
  1152. curr = next;
  1153. continue;
  1154. } else if (!consp(next))
  1155. break;
  1156. if (string_equal(here,O(dotdot_string)))
  1157. Car(next) = S(Kup); /* ".." --> :UP */
  1158. else { /* coerce to normal */
  1159. pushSTACK(next);
  1160. var object element = coerce_normal_ss(here);
  1161. next = popSTACK();
  1162. Car(next) = element;
  1163. }
  1164. } else if (eq(here,S(Kback)))
  1165. Car(next) = S(Kup); /* :BACK --> :UP (ANSI) */
  1166. curr = next;
  1167. }
  1168. }
  1169. dir = popSTACK();
  1170. /* collapse "foo/../" (quadratic algorithm) */
  1171. var bool changed_p;
  1172. do {
  1173. changed_p = false;
  1174. var object curr = dir;
  1175. while (consp(curr) && consp(Cdr(curr))) {
  1176. var object next = Cdr(curr);
  1177. var object here = Car(next);
  1178. var object next_next = Cdr(next);
  1179. if (consp(next_next)) {
  1180. var object next_here = Car(next_next);
  1181. /* :BACK has been converted to :UP */
  1182. if (!eq(here,S(Kup)) && eq(next_here,S(Kup))) {
  1183. if (eq(here,S(Kwild_inferiors)) || eq(here,S(Kabsolute))) {
  1184. goto error_absolute_up;
  1185. } else {
  1186. Cdr(curr) = Cdr(next_next); /* collapse ( "foo" :UP ) */
  1187. changed_p = true;
  1188. }
  1189. } else
  1190. curr = next;
  1191. } else
  1192. curr = next;
  1193. }
  1194. } while (changed_p);
  1195. if (eq(Car(dir),S(Kabsolute)) && consp(Cdr(dir)))
  1196. if (eq(Car(Cdr(dir)),S(Kup)))
  1197. goto error_absolute_up;
  1198. DOUT("simplify_directory:> ",dir);
  1199. return dir;
  1200. error_absolute_up:
  1201. /* <http://www.lisp.org/HyperSpec/Body/sec_19-2-2-4-3.html> */
  1202. pushSTACK(O(empty_string)); /* FILE-ERROR slot PATHNAME */
  1203. pushSTACK(dir); pushSTACK(S(Kdirectory));
  1204. pushSTACK(TheSubr(subr_self)->name);
  1205. error(file_error,GETTEXT("~S: illegal ~S argument ~S"));
  1206. }
  1207. /* Parses a logical pathname.
  1208. parse_logical_pathnamestring(z)
  1209. > STACK_1: storage vector, a normal-simple-string
  1210. > STACK_0: freshly allocated logical pathname
  1211. > state z: start state
  1212. < STACK_0: same logical pathname, filled
  1213. < result: number of remaining characters
  1214. can trigger GC */
  1215. local maygc uintL parse_logical_pathnamestring (zustand z) {
  1216. DOUT("parse_logical_pathnamestring:<0",STACK_0);
  1217. DOUT("parse_logical_pathnamestring:<1",STACK_1);
  1218. { /* parse Host-Specification: */
  1219. var zustand startz = z;
  1220. var object host = parse_logical_host_prefix(&z,STACK_1);
  1221. if (nullp(host)) {
  1222. z = startz; /* back to the start */
  1223. host = STACK_(3+2); /* Default-Host */
  1224. } else { /* enter host: */
  1225. TheLogpathname(STACK_0)->pathname_host = host;
  1226. }
  1227. }
  1228. { /* enter Directory-Start: */
  1229. var object new_cons = allocate_cons(); /* new Cons for Startpoint */
  1230. TheLogpathname(STACK_0)->pathname_directory = new_cons;
  1231. pushSTACK(new_cons); /* new (last (pathname-directory Pathname)) */
  1232. }
  1233. /* stack layout:
  1234. data-vector, pathname, (last (pathname-directory Pathname)).
  1235. parse subdirectories:
  1236. If ";" is the first char, it is turned into :RELATIVE
  1237. (otherwise :ABSOLUTE) as the first subdir
  1238. for a reason that escapes me, ANSI CL specifies that
  1239. "foo:;bar;baz.zot" is a :RELATIVE logical pathname while
  1240. "foo:/bar/baz.zot" is an :ABSOLUTE physical pathname.
  1241. see "19.3.1.1.3 The Directory part of a Logical Pathname Namestring"
  1242. http://www.lisp.org/HyperSpec/Body/sec_19-3-1-1-3.html */
  1243. if (Z_AT_SLASH(z,lslashp,STACK_2)) {
  1244. Z_SHIFT(z,1);
  1245. Car(STACK_0) = S(Krelative);
  1246. } else {
  1247. Car(STACK_0) = S(Kabsolute);
  1248. }
  1249. while (1) {
  1250. /* try to parse the next subdir */
  1251. var object subdir = parse_logical_word(&z,true);
  1252. if (nullp(subdir))
  1253. break;
  1254. /* lengthen (pathname-directory pathname) by Subdir: */
  1255. pushSTACK(subdir);
  1256. var object new_cons = allocate_cons(); /* new Cons */
  1257. Car(new_cons) = popSTACK(); /* = (cons subdir NIL) */
  1258. Cdr(STACK_0) = new_cons; /* lengthens (pathname-directory Pathname) */
  1259. STACK_0 = new_cons; /* new (last (pathname-directory Pathname)) */
  1260. }
  1261. { /* parse Name: */
  1262. var object name = parse_logical_word(&z,false);
  1263. TheLogpathname(STACK_1)->pathname_name = name;
  1264. if ((z.count > 0) && dotp(schar(STACK_2,z.index))) {
  1265. var zustand z_name = z;
  1266. /* skip Character '.' : */
  1267. Z_SHIFT(z,1);
  1268. /* parse Type: */
  1269. var object type = parse_logical_word(&z,false);
  1270. TheLogpathname(STACK_1)->pathname_type = type;
  1271. if (!nullp(type)) {
  1272. if ((z.count > 0) && dotp(schar(STACK_2,z.index))) {
  1273. var zustand z_type = z;
  1274. /* skip Character '.' : */
  1275. Z_SHIFT(z,1);
  1276. /* parse Version: */
  1277. var object version = parse_logical_word(&z,false);
  1278. if (eq(version,S(Kwild))) {
  1279. } else if (equal(version,Symbol_name(S(Knewest)))) {
  1280. version = S(Knewest);
  1281. } else if (stringp(version) && all_digits(version)) {
  1282. pushSTACK(version); funcall(L(parse_integer),1);
  1283. version = value1; /* version: string -> integer */
  1284. } else {
  1285. version = NIL;
  1286. }
  1287. TheLogpathname(STACK_1)->pathname_version = version;
  1288. if (nullp(version))
  1289. z = z_type; /* restore character '.' */
  1290. } else {
  1291. TheLogpathname(STACK_1)->pathname_version = NIL;
  1292. }
  1293. } else {
  1294. z = z_name; /* restore character '.' */
  1295. TheLogpathname(STACK_1)->pathname_version = NIL;
  1296. }
  1297. } else {
  1298. TheLogpathname(STACK_1)->pathname_type = NIL;
  1299. TheLogpathname(STACK_1)->pathname_version = NIL;
  1300. }
  1301. }
  1302. skipSTACK(1);
  1303. TheLogpathname(STACK_0)->pathname_directory =
  1304. simplify_directory(TheLogpathname(STACK_0)->pathname_directory);
  1305. DOUT("parse_logical_pathnamestring:>0",STACK_0);
  1306. DOUT("parse_logical_pathnamestring:>1",STACK_1);
  1307. return z.count;
  1308. }
  1309. /* recognition of a logical host, cf. CLtL2 p. 631
  1310. (defun logical-host-p (host)
  1311. (and (simple-string-p host)
  1312. (gethash host sys::*logical-pathname-translations*) ; :test #'equalp !
  1313. t)) */
  1314. local bool logical_host_p (object host) {
  1315. return (simple_string_p(host)
  1316. /* No need to string-upcase host, because it's tested via EQUALP. */
  1317. && !eq(gethash(host,Symbol_value(S(logpathname_translations)),false),
  1318. nullobj));
  1319. }
  1320. #endif
  1321. #define string2wild(str) (equal(str,O(wild_string)) ? S(Kwild) : (object)(str))
  1322. #define wild2string(obj) (eq(obj,S(Kwild)) ? (object)O(wild_string) : (obj))
  1323. #ifdef PATHNAME_NOEXT
  1324. /* can trigger GC */
  1325. local maygc void fix_parse_namestring_dot_file (void)
  1326. { /* make sure *PARSE-NAMESTRING-DOT-FILE* is valid */
  1327. Symbol_value(S(parse_namestring_dot_file)) = S(Ktype); /*CLISP default*/
  1328. pushSTACK(NIL);
  1329. pushSTACK(S(parse_namestring_dot_file));
  1330. pushSTACK(S(parse_namestring_dot_file));
  1331. pushSTACK(Symbol_value(S(parse_namestring_dot_file)));
  1332. STACK_3 = CLSTEXT("The variable ~S had an illegal value.\n"
  1333. "~S has been reset to ~S.");
  1334. funcall(S(warn),4);
  1335. }
  1336. /* auxiliary function for PARSE-NAMESTRING:
  1337. splits a string (at the last dot) into Name and Type.
  1338. split_name_type(skip);
  1339. > STACK_0: Normal-Simple-String
  1340. > skip: 1 if a dot at the beginning should not trigger the splitting, else 0
  1341. < STACK_1: Name
  1342. < STACK_0: Type
  1343. decrements STACK by 1
  1344. can trigger GC */
  1345. local maygc void split_name_type (uintL skip) {
  1346. if (skip == 0) {
  1347. if (eq(Symbol_value(S(parse_namestring_dot_file)),S(Ktype))) { /* OK */
  1348. } else if (eq(Symbol_value(S(parse_namestring_dot_file)),S(Kname))) {
  1349. skip = 1; /* always have a name! */
  1350. } else
  1351. fix_parse_namestring_dot_file();
  1352. }
  1353. var object string = STACK_0;
  1354. var uintL length = Sstring_length(string);
  1355. /* Search for the last dot: */
  1356. var uintL index = length;
  1357. if (index > skip) {
  1358. SstringDispatch(string,X, {
  1359. var const cintX* ptr = &((SstringX)TheVarobject(string))->data[index];
  1360. do {
  1361. if (*--ptr == '.') goto punkt;
  1362. index--;
  1363. } while (index > skip);
  1364. });
  1365. }
  1366. /* no dot found -> Type := NIL */
  1367. { pushSTACK(NIL); }
  1368. goto name_type_ok;
  1369. punkt: /* dot found at index */
  1370. /* type := (substring string index) */
  1371. pushSTACK(subsstring(string,index,length));
  1372. /* name := (substring string 0 (1- index)) */
  1373. STACK_1 = subsstring(STACK_1,0,index-1);
  1374. name_type_ok:
  1375. STACK_0 = string2wild(STACK_0);
  1376. STACK_1 = string2wild(STACK_1);
  1377. }
  1378. #endif
  1379. /* (PARSE-NAMESTRING thing [host [defaults [:start] [:end] [:junk-allowed]]]),
  1380. CLTL p. 414 */
  1381. LISPFUN(parse_namestring,seclass_read,1,2,norest,key,3,
  1382. (kw(start),kw(end),kw(junk_allowed)) ) {
  1383. /* stack layout: thing, host, defaults, start, end, junk-allowed. */
  1384. var bool junk_allowed;
  1385. var bool parse_logical = false;
  1386. DOUT("parse-namestring:[thng]",STACK_5);
  1387. DOUT("parse-namestring:[host]",STACK_4);
  1388. DOUT("parse-namestring:[dflt]",STACK_3);
  1389. DOUT("parse-namestring:[beg]",STACK_2);
  1390. DOUT("parse-namestring:[end]",STACK_1);
  1391. DOUT("parse-namestring:[junk]",STACK_0);
  1392. { /* 1. check junk-allowed: */
  1393. var object obj = popSTACK(); /* junk-allowed-Argument */
  1394. junk_allowed = !missingp(obj);
  1395. }
  1396. /* stack layout: thing, host, defaults, start, end.
  1397. 2. default-value for start is 0: */
  1398. if (!boundp(STACK_1))
  1399. STACK_1 = Fixnum_0;
  1400. /* 3. check host: */
  1401. #if HAS_HOST || defined(LOGICAL_PATHNAMES)
  1402. {
  1403. var object host = STACK_3;
  1404. #if HAS_HOST
  1405. host = test_optional_host(host,false);
  1406. #else
  1407. host = test_optional_host(host);
  1408. #endif
  1409. if (nullp(host)) {
  1410. /* host := (PATHNAME-HOST defaults) */
  1411. var object defaults = test_default_pathname(STACK_2);
  1412. #ifdef LOGICAL_PATHNAMES
  1413. if (logpathnamep(defaults))
  1414. parse_logical = true;
  1415. #endif
  1416. host = xpathname_host(parse_logical,defaults);
  1417. } else {
  1418. #ifdef LOGICAL_PATHNAMES
  1419. if (logical_host_p(host)) {
  1420. parse_logical = true; host = string_upcase(host);
  1421. }
  1422. #endif
  1423. }
  1424. STACK_3 = host;
  1425. }
  1426. #else
  1427. test_optional_host(STACK_3);
  1428. #endif
  1429. /* 4. thing must be a String: */
  1430. DOUT("parse-namestring:[thng]",STACK_4);
  1431. DOUT("parse-namestring:[host]",STACK_3);
  1432. DOUT("parse-namestring:[dflt]",STACK_2);
  1433. var object thing = STACK_4;
  1434. if (xpathnamep(thing)) { /* Pathname? */
  1435. value1 = thing; /* 1. value thing */
  1436. done:
  1437. DOUT("parse-namestring:[done]",value1);
  1438. value2 = STACK_1; mv_count=2; /* 2. value start */
  1439. skipSTACK(5); return;
  1440. }
  1441. if (builtin_stream_p(thing)) { /* Stream? */
  1442. thing = as_file_stream(thing);
  1443. test_file_stream_named(thing);
  1444. value1 = TheStream(thing)->strm_file_name; /* 1. value: Filename */
  1445. goto done; /* 2. value like above */
  1446. }
  1447. /* thing should now be at least a String or a Symbol: */
  1448. var bool thing_symbol = false;
  1449. if (!stringp(thing)) {
  1450. if (!symbolp(thing) || !nullpSv(parse_namestring_ansi))
  1451. error_pathname_designator(thing);
  1452. thing = Symbol_name(thing); /* Symbol -> use symbol name */
  1453. thing_symbol = true;
  1454. STACK_4 = thing; /* and write back into the Stack */
  1455. }
  1456. /* thing = STACK_4 is now a String.
  1457. it will be traversed. */
  1458. var zustand z; /* running state */
  1459. {
  1460. var object string; /* String thing */
  1461. { /* check boundaries, with thing, start, end as arguments: */
  1462. var stringarg arg;
  1463. pushSTACK(thing); pushSTACK(STACK_(1+1)); pushSTACK(STACK_(0+2));
  1464. test_string_limits_ro(&arg);
  1465. string = arg.string;
  1466. z.index = arg.offset+arg.index; /* z.index = start-argument, */
  1467. z.count = arg.len; /* z.count = number of characters. */
  1468. z.FNindex = fixnum(arg.index); /* z.FNindex = start-Index as Fixnum. */
  1469. }
  1470. #ifdef LOGICAL_PATHNAMES
  1471. if (!parse_logical) {
  1472. /* Check whether *PARSE-NAMESTRING-ANSI* is true and the string
  1473. starts with a logical hostname. */
  1474. if (!nullpSv(parse_namestring_ansi)) {
  1475. /* Coerce string to be a normal-simple-string. */
  1476. #ifdef HAVE_SMALL_SSTRING
  1477. SstringCase(string,{ Z_SUB(z,string); },{ Z_SUB(z,string); },{},{ Z_SUB(z,string); });
  1478. #endif
  1479. pushSTACK(string);
  1480. var zustand tmp = z;
  1481. var object host = parse_logical_host_prefix(&tmp,string);
  1482. string = popSTACK();
  1483. DOUT("parse-namestring:",string);
  1484. DOUT("parse-namestring:",host);
  1485. if (!nullp(host)
  1486. /* Test whether the given hostname is valid. This is not
  1487. strictly what ANSI specifies, but is better than giving
  1488. an error for Win32 pathnames like "C:\\FOOBAR". */
  1489. && logical_host_p(host))
  1490. parse_logical = true;
  1491. else
  1492. /* ANSI CL specifies that we should look at the entire string, using
  1493. parse_logical_pathnamestring, not only parse_logical_host_prefix. */
  1494. parse_logical = looks_logical_p(string);
  1495. }
  1496. }
  1497. #endif
  1498. if (thing_symbol && !parse_logical) {
  1499. #if defined(PATHNAME_UNIX) || defined(PATHNAME_WIN32)
  1500. /* operating system with preference for small letters */
  1501. Z_SUB(z,string); /* yes -> convert with STRING-DOWNCASE */
  1502. pushSTACK(string);
  1503. nstring_downcase(string,0,Sstring_length(string));
  1504. string = popSTACK();
  1505. sstring_un_realloc(string);
  1506. #endif
  1507. }
  1508. /* Coerce string to be a normal-simple-string. */
  1509. #ifdef HAVE_SMALL_SSTRING
  1510. SstringCase(string,{ Z_SUB(z,string); },{ Z_SUB(z,string); },{},{ Z_SUB(z,string); });
  1511. #endif
  1512. pushSTACK(string);
  1513. }
  1514. #ifdef LOGICAL_PATHNAMES
  1515. if (parse_logical) {
  1516. pushSTACK(allocate_logpathname());
  1517. /* stack layout: ..., data-vector, pathname. */
  1518. var uintL remaining = parse_logical_pathnamestring(z);
  1519. z.index += z.count-remaining; z.FNindex = fixnum_inc(z.FNindex,z.count-remaining); z.count = remaining;
  1520. } else
  1521. #endif
  1522. {
  1523. pushSTACK(allocate_pathname());
  1524. /* stack layout: ..., data-vector, pathname.
  1525. separator between subdirs is on WIN32 both '\' and '/': */
  1526. #if HAS_HOST
  1527. { /* parse Host-Specification: */
  1528. var object host;
  1529. {
  1530. var zustand startz = z; /* start-state */
  1531. var chart ch;
  1532. #if defined(PATHNAME_WIN32)
  1533. /* Look for two slashes, then a sequence of characters. */
  1534. if (z.count==0) goto no_hostspec;
  1535. ch = TheSnstring(STACK_1)->data[z.index];
  1536. if (!pslashp(ch)) goto no_hostspec;
  1537. Z_SHIFT(z,1);
  1538. if (z.count==0) goto no_hostspec;
  1539. ch = TheSnstring(STACK_1)->data[z.index];
  1540. if (!pslashp(ch)) goto no_hostspec;
  1541. Z_SHIFT(z,1);
  1542. while (z.count) {
  1543. ch = TheSnstring(STACK_1)->data[z.index];
  1544. if (!legal_hostchar(ch))
  1545. break;
  1546. /* Skip past valid host char. */
  1547. Z_SHIFT(z,1);
  1548. }
  1549. /* Create host string. */
  1550. if (z.index - startz.index - 2 == 0)
  1551. goto no_hostspec;
  1552. host = subsstring(STACK_1,startz.index+2,z.index);
  1553. /* Note: The next character in the string is not a letter or '*';
  1554. therefore the device of the resulting pathname will be NIL. */
  1555. goto hostspec_ok;
  1556. #else
  1557. /* is it a sequence of alphanumeric characters and then a ':' resp. '::' ? */
  1558. while (1) {
  1559. if (z.count==0)
  1560. goto no_hostspec; /* string already through -> no Host */
  1561. ch = TheSnstring(STACK_1)->data[z.index]; /* next character */
  1562. if (!alphanumericp(ch))
  1563. break;
  1564. /* skip alphanumeric character: */
  1565. Z_SHIFT(z,1);
  1566. }
  1567. if (!colonp(ch))
  1568. goto no_hostspec; /* no ':' -> no host */
  1569. /* build host-string: */
  1570. host = subsstring(STACK_1,startz.index,z.index);
  1571. /* skip character ':' : */
  1572. Z_SHIFT(z,1);
  1573. goto hostspec_ok;
  1574. #endif
  1575. no_hostspec: /* no host-specification */
  1576. z = startz; /* back to start */
  1577. host = STACK_(3+2); /* Default-Host */
  1578. }
  1579. hostspec_ok: /* enter host: */
  1580. ThePathname(STACK_0)->pathname_host = host;
  1581. }
  1582. #endif /* HAS_HOST */
  1583. #if HAS_DEVICE
  1584. #ifdef PATHNAME_WIN32
  1585. { /* parse one-letter Device-Specification: */
  1586. var object device = NIL; /* Device := NIL */
  1587. /* parse Drive-Specification:
  1588. Is there a letter ('*','A'-'Z','a'-'z') and then a ':' ? */
  1589. {
  1590. var zustand startz = z; /* start-state */
  1591. var chart ch;
  1592. if (z.count==0)
  1593. goto no_drivespec; /* string already through ? */
  1594. ch = TheSnstring(STACK_1)->data[z.index]; /* next character */
  1595. ch = up_case(ch); /* as capital letter */
  1596. if (starp(ch)) {
  1597. /* ch = '*' -> Device := :WILD */
  1598. device = S(Kwild);
  1599. } else if ((as_cint(ch) >= 'A') && (as_cint(ch) <= 'Z')) {
  1600. /* 'A' <= ch <= 'Z' -> Device := "ch" */
  1601. var object string = allocate_string(1); /* String of length 1 */
  1602. TheSnstring(string)->data[0] = ch; /* with ch as sole letter */
  1603. device = string;
  1604. } else
  1605. goto no_device;
  1606. /* Device OK, skip character: */
  1607. Z_SHIFT(z,1);
  1608. if (z.count==0)
  1609. goto no_drivespec; /* string already through ? */
  1610. ch = TheSnstring(STACK_1)->data[z.index]; /* next character */
  1611. ch = up_case(ch); /* as capital letter */
  1612. no_device:
  1613. /* concluded with colon? */
  1614. if (!colonp(ch))
  1615. goto no_drivespec;
  1616. /* skip character: */
  1617. Z_SHIFT(z,1);
  1618. goto drivespec_ok;
  1619. no_drivespec:
  1620. /* parsing a Drive-Specification did not succeed. */
  1621. z = startz; /* restore start-state */
  1622. device = NIL; /* Device := NIL */
  1623. }
  1624. drivespec_ok: /* enter Device */
  1625. ThePathname(STACK_0)->pathname_device = device;
  1626. }
  1627. #endif /* PATHNAME_WIN32 */
  1628. #endif /* HAS_DEVICE */
  1629. /* enter Directory-Start: */
  1630. ThePathname(STACK_0)->pathname_directory = NIL;
  1631. pushSTACK(NIL); /* new (last (pathname-directory Pathname)) */
  1632. /* stack layout:
  1633. ..., Datenvektor, Pathname, (last (pathname-directory Pathname)).
  1634. parse subdirectories: */
  1635. {
  1636. #if defined(USER_HOMEDIR) && defined(PATHNAME_UNIX)
  1637. /* if there is a '~' immediately, a username is read up to the next '/'
  1638. or string-end and the Home-Directory of this user is inserted: */
  1639. if ((z.count != 0) && chareq(schar(STACK_2,z.index),ascii('~'))) {
  1640. /* there is a '~' immediately.
  1641. skip character: */
  1642. Z_SHIFT(z,1);
  1643. var object userhomedir; /* Pathname of the User-Homedir */
  1644. /* search next '/' : */
  1645. var uintL charcount = 0;
  1646. if (z.count > 0) {
  1647. SstringDispatch(STACK_2,X, {
  1648. var const cintX* charptr =
  1649. &((SstringX)TheVarobject(STACK_2))->data[z.index];
  1650. var uintL count;
  1651. dotimespL(count,z.count, {
  1652. if (*charptr++ == '/') break;
  1653. charcount++;
  1654. });
  1655. });
  1656. }
  1657. /* Username has charcount characters */
  1658. if (charcount==0) {
  1659. userhomedir = O(user_homedir); /* only '~' -> User-Homedir */
  1660. } else { /* build username: */
  1661. var object username =
  1662. subsstring(STACK_2,z.index,z.index+charcount);
  1663. /* fetch his/her Home-Directory from the password-file: */
  1664. with_sstring_0(username,O(misc_encoding),username_asciz, {
  1665. begin_system_call();
  1666. errno = 0;
  1667. var struct passwd * userpasswd = getpwnam(username_asciz);
  1668. if (userpasswd == (struct passwd *)NULL) { /* unsuccessful? */
  1669. if (!(errno==0)) { OS_error(); } /* report error */
  1670. end_system_call();
  1671. /* else: error */
  1672. pushSTACK(username);
  1673. pushSTACK(S(parse_namestring));
  1674. error(parse_error,GETTEXT("~S: there is no user named ~S"));
  1675. }
  1676. end_system_call();
  1677. userhomedir = /* homedir as pathname */
  1678. asciz_dir_to_pathname(userpasswd->pw_dir,O(misc_encoding));
  1679. });
  1680. }
  1681. /* copy directory from the pathname userhomedir:
  1682. (copy-list dir) = (nreconc (reverse dir) nil),
  1683. after it memorize its last Cons. */
  1684. userhomedir = reverse(ThePathname(userhomedir)->pathname_directory);
  1685. userhomedir = nreconc(userhomedir,NIL);
  1686. ThePathname(STACK_1)->pathname_directory = userhomedir;
  1687. while (mconsp(Cdr(userhomedir))) { userhomedir = Cdr(userhomedir); }
  1688. STACK_0 = userhomedir;
  1689. /* skip username-characters: */
  1690. Z_SHIFT(z,charcount);
  1691. /* if the string is through: finished,
  1692. otherwise a '/' follows immediately , it will be skipped: */
  1693. if (z.count==0) { /* Name and Type := NIL */
  1694. pushSTACK(NIL); pushSTACK(NIL); goto after_name_type;
  1695. }
  1696. /* skip character: */
  1697. Z_SHIFT(z,1);
  1698. } else
  1699. #endif /* USER_HOMEDIR & PATHNAME_UNIX */
  1700. #if defined(PATHNAME_UNIX) && 0
  1701. /* What is this needed for, except for $HOME ?
  1702. If a '$' follows immediately, an Environment-Variable is read up
  1703. to the next '/' or string-end and its value is inserted: */
  1704. if ((z.count != 0)
  1705. && chareq(TheSnstring(STACK_2)->data[z.index],ascii('$'))) {
  1706. /* A '$' follows immediately.
  1707. skip character: */
  1708. Z_SHIFT(z,1);
  1709. var object envval_dir;
  1710. /* search next '/' : */
  1711. var uintL charcount = 0;
  1712. {
  1713. var const chart* charptr = &TheSnstring(STACK_2)->data[z.index];
  1714. var uintL count;
  1715. dotimesL(count,z.count, {
  1716. if (chareq(*charptr++,ascii('/')))
  1717. break;
  1718. charcount++;
  1719. });
  1720. }
  1721. { /* Environment-Variable has charcount characters. */
  1722. var object envvar =
  1723. subsstring(STACK_2,z.index,z.index+charcount);
  1724. /* fetch its value: */
  1725. with_sstring_0(envvar,O(misc_encoding),envvar_asciz, {
  1726. begin_system_call();
  1727. var const char* envval = getenv(envvar_asciz);
  1728. end_system_call();
  1729. if (envval==NULL) {
  1730. pushSTACK(envvar);
  1731. pushSTACK(S(parse_namestring));
  1732. error(parse_error,
  1733. GETTEXT("~S: there is no environment variable ~S"));
  1734. }
  1735. envval_dir = /* value of the variable as pathname */
  1736. asciz_dir_to_pathname(envval,O(misc_encoding));
  1737. });
  1738. }
  1739. /* copy directory from the pathname envval_dir:
  1740. (copy-list dir) = (nreconc (reverse dir) nil),
  1741. afterwards memorize its last Cons. */
  1742. envval_dir = reverse(ThePathname(envval_dir)->pathname_directory);
  1743. envval_dir = nreconc(envval_dir,NIL);
  1744. ThePathname(STACK_1)->pathname_directory = envval_dir;
  1745. while (mconsp(Cdr(envval_dir))) { envval_dir = Cdr(envval_dir); }
  1746. STACK_0 = envval_dir;
  1747. /* skip envvar-characters: */
  1748. Z_SHIFT(z,charcount);
  1749. /* if the string is through: finished,
  1750. otherwise a '/' follows immediately , it will be skipped: */
  1751. if (z.count==0) { /* Name and Type := NIL */
  1752. pushSTACK(NIL); pushSTACK(NIL); goto after_name_type;
  1753. }
  1754. /* skip character: */
  1755. Z_SHIFT(z,1);
  1756. } else
  1757. #endif /* PATHNAME_UNIX & 0 */
  1758. #if defined(PATHNAME_UNIX) || defined(PATHNAME_WIN32)
  1759. #if defined(UNIX_CYGWIN32)
  1760. if (z.count > 1 && !nullpSv(device_prefix)
  1761. && colonp(schar(STACK_2,z.index+1))) {
  1762. /* if string starts with 'x:', treat it as a device */
  1763. var chart ch = down_case(schar(STACK_2,z.index));
  1764. if ((as_cint(ch) >= 'a') && (as_cint(ch) <= 'z')) {
  1765. pushSTACK(allocate_string(1)); /* drive */
  1766. TheSnstring(STACK_0)->data[0] = ch;
  1767. var object new_cons = allocate_cons();
  1768. Car(new_cons) = popSTACK(); /* drive */
  1769. ThePathname(STACK_1)->pathname_directory = new_cons;
  1770. STACK_0 = new_cons;
  1771. Z_SHIFT(z,2);
  1772. if (Z_AT_SLASH(z,pslashp,STACK_2)) Z_SHIFT(z,1);
  1773. } else goto continue_parsing_despite_colon;
  1774. } else
  1775. continue_parsing_despite_colon:
  1776. #endif
  1777. /* if 1st char is a slash, start with :ABSOLUTE (otherwise :RELATIVE): */
  1778. if (Z_AT_SLASH(z,pslashp,STACK_2)) {
  1779. Z_SHIFT(z,1);
  1780. var object new_cons = allocate_cons();
  1781. Car(new_cons) = S(Kabsolute);
  1782. ThePathname(STACK_1)->pathname_directory = new_cons;
  1783. STACK_0 = new_cons;
  1784. }
  1785. #endif
  1786. while (1) {
  1787. /* try to parse another subdirectory. */
  1788. #ifdef PATHNAME_NOEXT
  1789. {
  1790. var uintL z_start_index = z.index; /* index at the start */
  1791. while (1) {
  1792. var chart ch;
  1793. if (z.count == 0)
  1794. break;
  1795. ch = schar(STACK_2,z.index); /* next character */
  1796. if (!legal_namechar(ch)) /* valid character ? */
  1797. break;
  1798. /* yes -> part of the name
  1799. skip character: */
  1800. Z_SHIFT(z,1);
  1801. }
  1802. /* reached end of the name.
  1803. Name := substring of STACK_2 from z_start_index (inclusive)
  1804. to z.index (exclusive). */
  1805. var object string = subsstring(STACK_2,z_start_index,z.index);
  1806. /* name finished. */
  1807. pushSTACK(string);
  1808. }
  1809. /* if a '/' resp. '\' follows immediately, then it was a subdirectory,
  1810. else the pathname is finished: */
  1811. if (!Z_AT_SLASH(z,pslashp,STACK_3))
  1812. /* no -> it was the name and no subdir. */
  1813. break;
  1814. /* a '/' resp. '\' follows. skip character: */
  1815. Z_SHIFT(z,1);
  1816. /* stack layout: ...,
  1817. data-vector, pathname, (last (pathname-directory Pathname)),
  1818. subdir. */
  1819. /* was it '**' or '...' ? */
  1820. if (equal(STACK_0,O(wildwild_string))
  1821. || equal(STACK_0,O(dotdotdot_string))) {
  1822. STACK_0 = S(Kwild_inferiors); /* replace with :WILD-INFERIORS */
  1823. }
  1824. #endif /* PATHNAME_NOEXT */
  1825. if (nullp(STACK_1)) {
  1826. var object new_cons = allocate_cons();
  1827. Car(new_cons) = S(Krelative);
  1828. ThePathname(STACK_2)->pathname_directory = new_cons;
  1829. STACK_1 = new_cons;
  1830. }
  1831. /* lengthen (pathname-directory pathname) by subdir STACK_0: */
  1832. var object new_cons = allocate_cons(); /* new Cons */
  1833. Car(new_cons) = popSTACK(); /* = (cons subdir NIL) */
  1834. Cdr(STACK_0) = new_cons; /* lengthened (pathname-directory Pathname) */
  1835. STACK_0 = new_cons; /* new (last (pathname-directory Pathname)) */
  1836. }
  1837. #ifdef PATHNAME_NOEXT
  1838. /* stack layout: ..., data-vector, pathname,
  1839. (last (pathname-directory Pathname)), string. */
  1840. split_name_type(0); /* split string STACK_0 in name and type */
  1841. after_name_type:
  1842. /* stack layout: ..., data-vector, pathname,
  1843. (last (pathname-directory Pathname)), name, type. */
  1844. { /* enter name and type in pathname: */
  1845. var object type = popSTACK();
  1846. var object name = popSTACK();
  1847. skipSTACK(1); /* directory is already entered */
  1848. /* replace name="" with name=NIL: */
  1849. if (equal(name,O(empty_string)))
  1850. name = NIL;
  1851. var object pathname = STACK_0;
  1852. ThePathname(pathname)->pathname_name = name;
  1853. ThePathname(pathname)->pathname_type = type;
  1854. }
  1855. #endif
  1856. #ifdef WIN32_NATIVE
  1857. var object pathname = STACK_0;
  1858. var object dir = ThePathname(pathname)->pathname_directory;
  1859. var object dev = Symbol_value(S(device_prefix));
  1860. if (nullp(ThePathname(pathname)->pathname_device)
  1861. /* actually, we already know that dir is a cons */
  1862. && consp(dir) && eq(Car(dir),S(Kabsolute))
  1863. /* Cdr(dir) might not be a cons, e.g., "/foo" ==
  1864. #S(pathname :directory (:absolute) :name "foo") */
  1865. && consp(Cdr(dir)) && consp(Cdr(Cdr(dir)))
  1866. && stringp(dev) && stringp(Car(Cdr(dir)))
  1867. && string_eqcomp_ci(Car(Cdr(dir)),0,dev,0,vector_length(dev))) {
  1868. /* path = (:ABSOLUTE "cygdrive" "drive" "dir1" ...) ===>
  1869. path = (:ABSOLUTE "dir1" ...); device = "DRIVE" */
  1870. var object device = Car(Cdr(Cdr(dir)));
  1871. Cdr(dir) = Cdr(Cdr(Cdr(dir)));
  1872. device = string_upcase(device);
  1873. ThePathname(STACK_0)->pathname_device = device;
  1874. }
  1875. #endif
  1876. #ifdef UNIX_CYGWIN32
  1877. var object dir = ThePathname(STACK_0)->pathname_directory;
  1878. if (consp(dir) && stringp(Car(dir))) {
  1879. /* dir = ("c" ...) --> (:absolute *device-prefix* "c" ...)*/
  1880. pushSTACK(S(Kabsolute));
  1881. pushSTACK(Symbol_value(S(device_prefix)));
  1882. dir = listof(2);
  1883. Cdr(Cdr(dir)) = ThePathname(STACK_0)->pathname_directory;
  1884. ThePathname(STACK_0)->pathname_directory = dir;
  1885. }
  1886. #endif
  1887. ThePathname(STACK_0)->pathname_directory =
  1888. simplify_directory(ThePathname(STACK_0)->pathname_directory);
  1889. }
  1890. }
  1891. /* Pathname is finished.
  1892. stack layout: ..., data-vector, pathname. */
  1893. if (!junk_allowed)
  1894. /* Check whether no more characters remain */
  1895. if (!(z.count == 0)) {
  1896. pushSTACK(z.FNindex); /* last index */
  1897. pushSTACK(STACK_(4+2+1)); /* thing */
  1898. pushSTACK(S(parse_namestring));
  1899. error(parse_error,
  1900. GETTEXT("~S: syntax error in filename ~S at position ~S"));
  1901. }
  1902. #if HAS_HOST || defined(LOGICAL_PATHNAMES)
  1903. /* Check that if a :host argument (or :host component of the :defaults
  1904. argument) was present and the parsed pathname has a host component,
  1905. they agree; and set the :host component of the result otherwise */
  1906. if (!missingp(STACK_(3+2))) {
  1907. #ifdef LOGICAL_PATHNAMES
  1908. if (parse_logical) {
  1909. var object parsed_host = TheLogpathname(STACK_0)->pathname_host;
  1910. if (!nullp(parsed_host)) {
  1911. if (!equal(STACK_(3+2),parsed_host)) {
  1912. pushSTACK(STACK_0);
  1913. pushSTACK(parsed_host);
  1914. pushSTACK(STACK_(3+2+2));
  1915. pushSTACK(S(parse_namestring));
  1916. error(error_condition,GETTEXT("~S: hosts ~S and ~S of ~S should coincide"));
  1917. }
  1918. } else
  1919. TheLogpathname(STACK_0)->pathname_host = STACK_(3+2);
  1920. } else
  1921. #endif
  1922. {
  1923. #if HAS_HOST
  1924. var object parsed_host = ThePathname(STACK_0)->pathname_host;
  1925. if (!nullp(parsed_host)) {
  1926. if (!equal(STACK_(3+2),parsed_host)) {
  1927. pushSTACK(STACK_0);
  1928. pushSTACK(parsed_host);
  1929. pushSTACK(STACK_(3+2+2));
  1930. pushSTACK(S(parse_namestring));
  1931. error(error_condition,GETTEXT("~S: hosts ~S and ~S of ~S should coincide"));
  1932. }
  1933. } else
  1934. ThePathname(STACK_0)->pathname_host = STACK_(3+2);
  1935. #endif
  1936. }
  1937. }
  1938. #endif /* HAS_HOST || LOGICAL_PATHNAMES */
  1939. value1 = STACK_0; /* pathname as 1st value */
  1940. value2 = z.FNindex; /* index as 2nd value */
  1941. mv_count=2; /* 2 values */
  1942. DOUT("parse-namestring:[end ret]",value1);
  1943. skipSTACK(5+2); return;
  1944. }
  1945. #undef colonp
  1946. #undef Z_SUB
  1947. #undef Z_AT_SLASH
  1948. #undef Z_SHIFT
  1949. /* UP: Converts an object into a pathname.
  1950. coerce_xpathname(object)
  1951. > object: object
  1952. < result: (PATHNAME Objekt)
  1953. can trigger GC */
  1954. local maygc object coerce_xpathname (object obj) {
  1955. if (xpathnamep(obj)) {
  1956. /* nothing to do for pathnames. */
  1957. return obj;
  1958. } else {
  1959. /* else: call PARSE-NAMESTRING: */
  1960. pushSTACK(obj); funcall(L(parse_namestring),1);
  1961. return value1;
  1962. }
  1963. }
  1964. LISPFUNNR(pathname,1) { /* (PATHNAME pathname), CLTL p. 413 */
  1965. VALUES1(coerce_xpathname(popSTACK()));
  1966. }
  1967. /* (PATHNAME-HOST pathname [:case]), CLTL p. 417, CLtL2 p. 644 */
  1968. LISPFUN(pathnamehost,seclass_read,1,0,norest,key,1, (kw(case))) {
  1969. var object pathname = coerce_xpathname(STACK_1);
  1970. #ifdef LOGICAL_PATHNAMES
  1971. if (logpathnamep(pathname)) {
  1972. VALUES1(TheLogpathname(pathname)->pathname_host);
  1973. } else
  1974. #endif
  1975. {
  1976. #if HAS_HOST
  1977. var object erg = ThePathname(pathname)->pathname_host;
  1978. VALUES1(eq(STACK_0,S(Kcommon)) ? common_case(erg) : erg); /* host as value */
  1979. #else
  1980. VALUES1(NIL);
  1981. #endif
  1982. }
  1983. skipSTACK(2);
  1984. }
  1985. /* (PATHNAME-DEVICE pathname [:case]), CLTL p. 417, CLtL2 p. 644 */
  1986. LISPFUN(pathnamedevice,seclass_read,1,0,norest,key,1, (kw(case))) {
  1987. var object pathname = coerce_xpathname(STACK_1);
  1988. #ifdef LOGICAL_PATHNAMES
  1989. if (logpathnamep(pathname)) {
  1990. /* http://www.lisp.org/HyperSpec/Body/sec_19-3-2-1.html */
  1991. VALUES1(S(Kunspecific));
  1992. } else
  1993. #endif
  1994. {
  1995. #if HAS_DEVICE
  1996. var object erg = ThePathname(pathname)->pathname_device; /* device as value */
  1997. VALUES1(eq(STACK_0,S(Kcommon)) ? common_case(erg) : erg);
  1998. #else
  1999. VALUES1(NIL);
  2000. #endif
  2001. }
  2002. skipSTACK(2);
  2003. }
  2004. /* (PATHNAME-DIRECTORY pathname [:case]), CLTL p. 417, CLtL2 p. 644 */
  2005. LISPFUN(pathnamedirectory,seclass_read,1,0,norest,key,1, (kw(case))) {
  2006. var object pathname = coerce_xpathname(STACK_1);
  2007. #ifdef LOGICAL_PATHNAMES
  2008. if (logpathnamep(pathname)) {
  2009. VALUES1(TheLogpathname(pathname)->pathname_directory);
  2010. } else
  2011. #endif
  2012. {
  2013. var object erg = ThePathname(pathname)->pathname_directory;
  2014. VALUES1(eq(STACK_0,S(Kcommon)) ? subst_common_case(erg) : erg);
  2015. }
  2016. skipSTACK(2);
  2017. }
  2018. /* (PATHNAME-NAME pathname [:case]), CLTL p. 417, CLtL2 p. 644 */
  2019. LISPFUN(pathnamename,seclass_read,1,0,norest,key,1, (kw(case))) {
  2020. var object pathname = coerce_xpathname(STACK_1);
  2021. #ifdef LOGICAL_PATHNAMES
  2022. if (logpathnamep(pathname)) {
  2023. value1 = TheLogpathname(pathname)->pathname_name;
  2024. } else
  2025. #endif
  2026. {
  2027. var object erg = ThePathname(pathname)->pathname_name;
  2028. value1 = (eq(STACK_0,S(Kcommon)) ? common_case(erg) : erg);
  2029. }
  2030. mv_count=1; /* name as value */
  2031. skipSTACK(2);
  2032. }
  2033. /* (PATHNAME-TYPE pathname [:case]), CLTL p. 417, CLtL2 p. 644 */
  2034. LISPFUN(pathnametype,seclass_read,1,0,norest,key,1, (kw(case))) {
  2035. var object pathname = coerce_xpathname(STACK_1);
  2036. #ifdef LOGICAL_PATHNAMES
  2037. if (logpathnamep(pathname)) {
  2038. value1 = TheLogpathname(pathname)->pathname_type;
  2039. } else
  2040. #endif
  2041. {
  2042. var object erg = ThePathname(pathname)->pathname_type;
  2043. value1 = (eq(STACK_0,S(Kcommon)) ? common_case(erg) : erg);
  2044. }
  2045. mv_count=1; /* type as value */
  2046. skipSTACK(2);
  2047. }
  2048. /* (PATHNAME-VERSION pathname), CLTL p. 417, CLtL2 p. 644 */
  2049. LISPFUNNR(pathnameversion,1) {
  2050. var object pathname = coerce_xpathname(popSTACK());
  2051. VALUES1(xpathname_version(logpathnamep(pathname),pathname));
  2052. }
  2053. #ifdef LOGICAL_PATHNAMES
  2054. /* Converts obj to a pathname. If obj is a string, it is even converted to a
  2055. logical pathname.
  2056. can trigger GC */
  2057. local maygc object parse_as_logical (object obj) {
  2058. /* The value of (PARSE-NAMESTRING obj nil empty-logical-pathname) is always
  2059. a logical pathname, if obj is a string. (But not if it is a stream!) */
  2060. pushSTACK(obj); pushSTACK(NIL);
  2061. pushSTACK(O(empty_logical_pathname));
  2062. funcall(L(parse_namestring),3);
  2063. return value1;
  2064. }
  2065. /* Handler: Signals a TYPE-ERROR with the same error message as the current
  2066. condition. */
  2067. local void signal_type_error (void* sp, gcv_object_t* frame, object label,
  2068. object condition) {
  2069. var gcv_object_t* thing_ = (gcv_object_t*)sp;
  2070. /* (SYS::ERROR-OF-TYPE 'TYPE-ERROR
  2071. :DATUM thing
  2072. :EXPECTED-TYPE '(AND STRING (SATISFIES SYSTEM::VALID-LOGICAL-PATHNAME-STRING-P))
  2073. "~A" condition) */
  2074. pushSTACK(S(type_error));
  2075. pushSTACK(S(Kdatum)); pushSTACK(*thing_);
  2076. pushSTACK(S(Kexpected_type)); pushSTACK(O(type_logical_pathname_string));
  2077. pushSTACK(O(tildeA)); pushSTACK(condition);
  2078. funcall(L(error_of_type),7);
  2079. }
  2080. LISPFUNNR(logical_pathname,1)
  2081. { /* (LOGICAL-PATHNAME thing), CLtL2 p. 631 */
  2082. var object thing = STACK_0;
  2083. if (logpathnamep(thing)) {
  2084. /* nothing to do for logical pathnames. */
  2085. VALUES1(thing);
  2086. } else if (pathnamep(thing)) {
  2087. /* normal pathnames cannot be converted into logical pathnames. */
  2088. pushSTACK(thing); /* TYPE-ERROR slot DATUM */
  2089. pushSTACK(O(type_logical_pathname)); /* TYPE-ERROR slot EXPECTED-TYPE */
  2090. pushSTACK(thing);
  2091. pushSTACK(S(logical_pathname));
  2092. error(type_error,GETTEXT("~S: argument ~S is not a logical pathname, string, stream or symbol"));
  2093. } else if (builtin_stream_p(thing)) { /* Stream? */
  2094. thing = as_file_stream(thing);
  2095. test_file_stream_named(thing);
  2096. var object pathname = TheStream(thing)->strm_file_name;
  2097. if (!logpathnamep(pathname)) {
  2098. /* Normal pathnames cannot be converted into logical pathnames. */
  2099. pushSTACK(pathname); /* TYPE-ERROR slot DATUM */
  2100. pushSTACK(O(type_logical_pathname)); /* TYPE-ERROR slot EXPECTED-TYPE */
  2101. pushSTACK(thing); pushSTACK(S(logical_pathname));
  2102. error(type_error,GETTEXT("~S: the stream ~S was not opened with a logical pathname"));
  2103. }
  2104. VALUES1(pathname);
  2105. } else {
  2106. /* ANSI CL requires that we transform PARSE-ERROR into TYPE-ERROR. */
  2107. var gcv_object_t* thing_ = &STACK_0;
  2108. make_HANDLER_frame(O(handler_for_parse_error), &signal_type_error,thing_);
  2109. var object pathname = parse_as_logical(thing);
  2110. unwind_HANDLER_frame();
  2111. /* Check that a host was given. This makes it hard to create relative
  2112. logical pathnames, but it is what ANSI CL specifies. */
  2113. if (nullp(TheLogpathname(pathname)->pathname_host)) {
  2114. pushSTACK(TheLogpathname(pathname)->pathname_host); /* TYPE-ERROR slot DATUM */
  2115. pushSTACK(S(string)); /* TYPE-ERROR slot EXPECTED-TYPE */
  2116. pushSTACK(STACK_(0+2)); pushSTACK(S(logical_pathname));
  2117. error(type_error,GETTEXT("~S: argument ~S does not contain a host specification"));
  2118. }
  2119. VALUES1(pathname);
  2120. }
  2121. skipSTACK(1);
  2122. }
  2123. /* forward declaration */
  2124. local object use_default_dir (object pathname);
  2125. /* (TRANSLATE-LOGICAL-PATHNAME pathname &key [:absolute]), CLtL2 p. 631 */
  2126. LISPFUN(translate_logical_pathname,seclass_default,1,0,norest,key,1,
  2127. (kw(absolute))) {
  2128. var bool absolute_p = !missingp(STACK_0);
  2129. var object pathname;
  2130. skipSTACK(1); /* drop :ABSOLUTE */
  2131. /* It is not clear from the ANSI CL spec how the argument shall be coerced
  2132. to a pathname. But the examples in the spec indicate that if the
  2133. argument is a string, it should be converted to a logical pathname,
  2134. by calling LOGICAL-PATHNAME, not by calling PATHNAME. */
  2135. if (stringp(STACK_0)) {
  2136. funcall(L(logical_pathname),1); pathname = value1;
  2137. } else {
  2138. pathname = coerce_xpathname(popSTACK());
  2139. }
  2140. if (logpathnamep(pathname)) {
  2141. /* Conversion of a logical into a normal pathname:
  2142. (let ((ht (make-hash-table :key-type 'logical-pathname :value-type '(eql t)
  2143. :test #'equal)))
  2144. (loop
  2145. (when (gethash pathname ht) (error "Translation loop"))
  2146. (setf (gethash pathname ht) t)
  2147. (let ((host (or (pathname-host pathname) "SYS")))
  2148. (unless (logical-host-p host) (error "No translation for host"))
  2149. (let* ((translations
  2150. (gethash host sys::*logical-pathname-translations*))
  2151. (translation
  2152. (assoc pathname translations :test #'pathname-match-p)))
  2153. (unless (and translation (consp translation)
  2154. (consp (cdr translation)))
  2155. (error "No translation for pathname"))
  2156. (setq pathname (translate-pathname pathname (first translation)
  2157. (second translation)))))
  2158. (unless (sys::logical-pathname-p pathname) (return)))
  2159. pathname) */
  2160. pushSTACK(pathname);
  2161. DOUT("translate-logical-pathname: <",pathname);
  2162. pushSTACK(S(Ktest)); pushSTACK(L(equal)); funcall(L(make_hash_table),2);
  2163. pushSTACK(value1);
  2164. /* stack layout: pathname, ht. */
  2165. while (1) {
  2166. if (!nullp(shifthash(STACK_0,STACK_1,T,true))) {
  2167. /* STACK_1 = pathname; -- FILE-ERROR slot PATHNAME */
  2168. STACK_0 = STACK_1;
  2169. pushSTACK(S(translate_logical_pathname));
  2170. error(file_error,GETTEXT("~S: endless loop while resolving ~S"));
  2171. }
  2172. if (nullp(TheLogpathname(STACK_1)->pathname_host)) {
  2173. /* replace host NIL with default-host: */
  2174. var object newp = allocate_logpathname();
  2175. var object oldp = STACK_1;
  2176. TheLogpathname(newp)->pathname_host
  2177. = O(default_logical_pathname_host); /* Default "SYS" */
  2178. TheLogpathname(newp)->pathname_directory
  2179. = TheLogpathname(oldp)->pathname_directory;
  2180. TheLogpathname(newp)->pathname_name
  2181. = TheLogpathname(oldp)->pathname_name;
  2182. TheLogpathname(newp)->pathname_type
  2183. = TheLogpathname(oldp)->pathname_type;
  2184. TheLogpathname(newp)->pathname_version
  2185. = TheLogpathname(oldp)->pathname_version;
  2186. STACK_1 = newp;
  2187. }
  2188. var object host = TheLogpathname(STACK_1)->pathname_host;
  2189. DOUT("translate-logical-pathname:",host);
  2190. var object translations =
  2191. gethash(host,Symbol_value(S(logpathname_translations)),false);
  2192. if (eq(translations,nullobj)) {
  2193. /* STACK_1 = pathname; -- FILE-ERROR slot PATHNAME */
  2194. STACK_0 = STACK_1;
  2195. pushSTACK(host);
  2196. pushSTACK(S(translate_logical_pathname));
  2197. error(file_error,GETTEXT("~S: unknown logical host ~S in ~S"));
  2198. }
  2199. /* (ASSOC pathname translations :test #'pathname-match-p): */
  2200. pushSTACK(STACK_1); pushSTACK(translations);
  2201. DOUT("translate-logical-pathname:[path_name_s1]",STACK_1);
  2202. DOUT("translate-logical-pathname:",translations);
  2203. pushSTACK(S(Ktest)); pushSTACK(L(pathname_match_p));
  2204. funcall(L(assoc),4);
  2205. if (atomp(value1) || matomp(Cdr(value1))) {
  2206. /* STACK_1 = pathname; -- FILE-ERROR slot PATHNAME */
  2207. STACK_0 = STACK_1;
  2208. pushSTACK(S(translate_logical_pathname));
  2209. error(file_error,GETTEXT("~S: No replacement rule for ~S is known."));
  2210. }
  2211. /* (TRANSLATE-PATHNAME pathname (first rule) (second rule) :MERGE NIL):*/
  2212. pushSTACK(STACK_1); pushSTACK(Car(value1)); pushSTACK(Car(Cdr(value1)));
  2213. pushSTACK(S(Kmerge)); pushSTACK(NIL);
  2214. funcall(L(translate_pathname),5);
  2215. STACK_1 = pathname = value1;
  2216. DOUT("translate-logical-pathname:",pathname);
  2217. if (!logpathnamep(pathname))
  2218. break;
  2219. }
  2220. DOUT("translate-logical-pathname: >",pathname);
  2221. skipSTACK(2);
  2222. }
  2223. if (absolute_p)
  2224. pathname = use_default_dir(pathname); /* insert default-directory */
  2225. VALUES1(pathname);
  2226. }
  2227. /* UP: Change an object into a non-logical pathname.
  2228. coerce_pathname(object)
  2229. > object: object
  2230. < return: (TRANSLATE-LOGICAL-PATHNAME (PATHNAME Objekt))
  2231. can trigger GC */
  2232. local maygc object coerce_pathname (object obj) {
  2233. obj = coerce_xpathname(obj);
  2234. if (pathnamep(obj)) {
  2235. return obj;
  2236. } else if (logpathnamep(obj)) {
  2237. /* call TRANSLATE-LOGICAL-PATHNAME: */
  2238. pushSTACK(obj); funcall(L(translate_logical_pathname),1);
  2239. return value1;
  2240. } else
  2241. NOTREACHED;
  2242. }
  2243. #endif
  2244. /* UP: Pushes substrings for STRING_CONCAT on the STACK, that together yield
  2245. the string for a subdirectory (car path) .
  2246. subdir_namestring_parts(path,logicalp)
  2247. > path: a Cons
  2248. > logicalp: boolean
  2249. < result: number of strings pushed on the stack
  2250. changes STACK */
  2251. #define SUBDIR_PUSHSTACK(subdir) \
  2252. do { if (eq(subdir,S(Kwild_inferiors))) pushSTACK(O(wildwild_string)); \
  2253. else if (eq(subdir,S(Kwild))) pushSTACK(O(wild_string)); \
  2254. else if (eq(subdir,S(Kup)) || eq(subdir,S(Kback))) \
  2255. pushSTACK(O(dotdot_string)); \
  2256. else if (stringp(subdir)) pushSTACK(subdir); \
  2257. else NOTREACHED; \
  2258. } while(0)
  2259. local uintC subdir_namestring_parts (object path,bool logp) {
  2260. var object subdir = Car(path);
  2261. #if defined(PATHNAME_UNIX) || defined(PATHNAME_WIN32)
  2262. SUBDIR_PUSHSTACK(subdir); return 1;
  2263. #endif
  2264. }
  2265. /* UP: Pushes substrings for STRING_CONCAT on the STACK, that together yield
  2266. the String for the host of the Pathname pathname.
  2267. host_namestring_parts(pathname)
  2268. > pathname: non-logical pathname
  2269. < result: number of strings pushed on the stack
  2270. changes STACK */
  2271. #if HAS_HOST || defined(LOGICAL_PATHNAMES)
  2272. local uintC host_namestring_parts (object pathname) {
  2273. var bool logp = logpathnamep(pathname);
  2274. var object host = xpathname_host(logp,pathname);
  2275. if (nullp(host)) {
  2276. return 0; /* no String */
  2277. } else {
  2278. #ifdef PATHNAME_WIN32
  2279. if (!logp) {
  2280. pushSTACK(O(backslashbackslash_string));
  2281. pushSTACK(host);
  2282. return 2;
  2283. }
  2284. #endif
  2285. pushSTACK(host);
  2286. pushSTACK(O(colon_string)); /* ":" */
  2287. return 2;
  2288. }
  2289. }
  2290. #else
  2291. #define host_namestring_parts(pathname) (unused (pathname), 0) /* no strings */
  2292. #endif
  2293. /* UP: Pushes substrings for STRING_CONCAT on the STACK, that together
  2294. yield the String for the Device and Directory of the Pathname pathname.
  2295. directory_namestring_parts(pathname)
  2296. > pathname: non-logical pathname
  2297. < result: number of strings pushed on the stack
  2298. changes STACK */
  2299. local uintC directory_namestring_parts (object pathname) {
  2300. var uintC stringcount = 0; /* number of strings so far = 0 */
  2301. var bool logp = logpathnamep(pathname);
  2302. #if defined(PATHNAME_WIN32)
  2303. { /* Device: */
  2304. var object device = xpathname_device(logp,pathname);
  2305. if (!(nullp(device))) { /* NIL -> no string */
  2306. var object string = wild2string(device);
  2307. pushSTACK(string);
  2308. stringcount++; /* and count */
  2309. pushSTACK(O(colon_string));
  2310. stringcount++; /* ":" */
  2311. }
  2312. }
  2313. #endif
  2314. #if defined(PATHNAME_WIN32) || defined(PATHNAME_UNIX)
  2315. if (stringcount == 0) /* only if there's no device already */
  2316. /* no check for both host and device being present:
  2317. this can never happen in CLISP */
  2318. stringcount += host_namestring_parts(pathname);
  2319. #endif
  2320. { /* Directory: */
  2321. var object directory = xpathname_directory(logp,pathname);
  2322. #if defined(LOGICAL_PATHNAMES)
  2323. if (logp) {
  2324. if (consp(directory) && eq(Car(directory),S(Krelative))) {
  2325. pushSTACK(O(semicolon_string)); stringcount++; /* ";" on the Stack */
  2326. }
  2327. } else
  2328. #endif
  2329. #if defined(PATHNAME_WIN32)
  2330. #define push_slash pushSTACK(O(backslash_string))
  2331. #elif defined(PATHNAME_UNIX)
  2332. #define push_slash pushSTACK(O(slash_string))
  2333. #else
  2334. #error "what is the directory separator on your platform?"
  2335. #endif
  2336. {
  2337. if (!mconsp(directory)) return stringcount; /* no directory */
  2338. /* is the first subdir = :ABSOLUTE or = :RELATIVE ? */
  2339. if (eq(Car(directory),S(Kabsolute))) {
  2340. push_slash; stringcount++; /* "/" */
  2341. } else if (nullp(Cdr(directory))) { /* (:RELATIVE) ==> "./" */
  2342. pushSTACK(O(dot_string)); stringcount++; /* "." */
  2343. push_slash; stringcount++; /* "/" */
  2344. return stringcount;
  2345. }}
  2346. directory = Cdr(directory); /* skip */
  2347. /* other subdirs on the stack: */
  2348. while (consp(directory)) {
  2349. stringcount += subdir_namestring_parts(directory,logp);
  2350. #if defined(LOGICAL_PATHNAMES)
  2351. if (logp) {
  2352. pushSTACK(O(semicolon_string)); stringcount++; /* ";" */
  2353. } else
  2354. #endif
  2355. {
  2356. #ifdef PATHNAME_WIN32
  2357. pushSTACK(O(backslash_string)); stringcount++; /* "\\" */
  2358. #endif
  2359. #ifdef PATHNAME_UNIX
  2360. pushSTACK(O(slash_string)); stringcount++; /* "/" */
  2361. #endif
  2362. }
  2363. directory = Cdr(directory);
  2364. }
  2365. }
  2366. #undef push_slash
  2367. return stringcount;
  2368. }
  2369. /* UP: Pushes substrings for STRING_CONCAT on the STACK, that together yield
  2370. the string for Name and Type of the pathname.
  2371. nametype_namestring_parts(name,type,version)
  2372. > name, type, poss. version: components of the pathname
  2373. < result: number of the strings pushed on the stack
  2374. can trigger GC
  2375. changes STACK */
  2376. local maygc uintC nametype_namestring_parts (object name, object type, object version)
  2377. {
  2378. var uintC stringcount = 0;
  2379. /* Name: */
  2380. if (!nullp(name)) { /* name=NIL -> do not print */
  2381. var object string = wild2string(name);
  2382. pushSTACK(string);
  2383. stringcount++; /* and count */
  2384. }
  2385. /* Type: */
  2386. if (!nullp(type)) { /* type=NIL -> do not print */
  2387. pushSTACK(O(dot_string)); /* "." */
  2388. stringcount++; /* and count */
  2389. var object string = wild2string(type);
  2390. pushSTACK(string);
  2391. stringcount++; /* and count */
  2392. }
  2393. if (!nullp(version)) { /* version=NIL -> do not print */
  2394. pushSTACK(O(dot_string)); /* "." */
  2395. stringcount++; /* and count */
  2396. if (eq(version,S(Knewest)))
  2397. /* http://www.lisp.org/HyperSpec/Body/sec_19-3-1.html */
  2398. pushSTACK(Symbol_name(S(Knewest))); /* :NEWEST -> "NEWEST" */
  2399. else if (eq(version,S(Kwild)))
  2400. pushSTACK(O(wild_string));
  2401. else
  2402. /* version (integer >0) ==> string: (sys::decimal-string version) */
  2403. pushSTACK(decimal_string(version));
  2404. stringcount++; /* and count */
  2405. }
  2406. return stringcount;
  2407. }
  2408. /* UP: Pushes substrings for STRING_CONCAT on the STACK, that together yield
  2409. the string for name and type of the pathname.
  2410. file_namestring_parts(pathname)
  2411. > pathname: non-logical pathname
  2412. < result: number of the strings pushed on the stack
  2413. can trigger GC
  2414. changes STACK */
  2415. local maygc uintC file_namestring_parts (object pathname) {
  2416. #if defined(LOGICAL_PATHNAMES)
  2417. if (logpathnamep(pathname))
  2418. return nametype_namestring_parts
  2419. (TheLogpathname(pathname)->pathname_name,
  2420. TheLogpathname(pathname)->pathname_type,
  2421. TheLogpathname(pathname)->pathname_version);
  2422. else
  2423. #endif
  2424. /* do not print version when the underlying physical file system
  2425. does not support it */
  2426. return nametype_namestring_parts(ThePathname(pathname)->pathname_name,
  2427. ThePathname(pathname)->pathname_type,
  2428. pathname_version_maybe(pathname));
  2429. }
  2430. /* UP: Converts pathname into string.
  2431. whole_namestring(pathname)
  2432. > pathname: non-logical pathname
  2433. < result: Normal-Simple-String
  2434. can trigger GC */
  2435. local maygc object whole_namestring (object pathname) {
  2436. var uintC stringcount = 0;
  2437. stringcount += directory_namestring_parts(pathname);
  2438. stringcount += file_namestring_parts(pathname);
  2439. return string_concat(stringcount);
  2440. }
  2441. /* UP: Returns the string for the directory of a pathname.
  2442. directory_namestring(pathname)
  2443. > pathname: non-logical pathname
  2444. < result: Normal-Simple-String
  2445. can trigger GC */
  2446. local maygc object directory_namestring (object pathname) {
  2447. /* The function DIRECTORY-NAMESTRING is totally underspecified.
  2448. It could return
  2449. a. just the string for the directory portion,
  2450. b. the string for the device + directory portions,
  2451. c. the string for the host + device + directory portions.
  2452. Before we used hosts, we have traditionally returned (b).
  2453. Now, with hosts, we still return (b) since HOST-NAMESTRING returns
  2454. the host part, while there is no way to return just the device
  2455. This makes most sense, given that CLHS says that programs
  2456. should not attempt to concatenate the resulting string with anything. */
  2457. return string_concat(directory_namestring_parts(pathname));
  2458. }
  2459. /* UP: Returns the string identifying a file in its directory.
  2460. file_namestring(pathname)
  2461. > pathname: non-logical pathname
  2462. < result: normal-simple-string
  2463. can trigger GC */
  2464. local maygc inline object file_namestring (object pathname) {
  2465. return string_concat(file_namestring_parts(pathname));
  2466. }
  2467. LISPFUNNR(file_namestring,1)
  2468. { /* (FILE-NAMESTRING pathname), CLTL p. 417 */
  2469. var object pathname = coerce_xpathname(popSTACK());
  2470. VALUES1(file_namestring(pathname));
  2471. }
  2472. LISPFUNNR(directory_namestring,1)
  2473. { /* (DIRECTORY-NAMESTRING pathname), CLTL p. 417 */
  2474. var object pathname = coerce_xpathname(popSTACK());
  2475. VALUES1(directory_namestring(pathname));
  2476. }
  2477. LISPFUNNR(host_namestring,1)
  2478. { /* (HOST-NAMESTRING pathname), CLTL p. 417 */
  2479. var object pathname = coerce_xpathname(popSTACK());
  2480. VALUES1(xpathname_host(logpathnamep(pathname),pathname));
  2481. }
  2482. /* UP: check an optional VERSION argument.
  2483. test_optional_version(def);
  2484. > STACK_0: VERSION-Argument
  2485. > def: default value for it
  2486. < result: valid version-component */
  2487. local object test_optional_version (object def) {
  2488. var object version = STACK_0;
  2489. if (!boundp(version)) {
  2490. STACK_0 = def; /* not specified -> Default */
  2491. } else if (nullp(version)) { /* NIL is OK */
  2492. } else if (eq(version,S(Kwild))) { /* :WILD is OK */
  2493. } else if (eq(version,S(Knewest))) { /* :NEWEST is OK */
  2494. } else if (posfixnump(version) && !eq(version,Fixnum_0)) {/*Fixnum>0 is OK*/
  2495. } else if (pathnamep(version)) { /* Pathname -> its Version */
  2496. STACK_0 = ThePathname(version)->pathname_version;
  2497. }
  2498. #ifdef LOGICAL_PATHNAMES
  2499. else if (logpathnamep(version)) { /* Logical Pathname -> its Version */
  2500. STACK_0 = TheLogpathname(version)->pathname_version;
  2501. }
  2502. #endif
  2503. else { /* None of the desired cases -> error: */
  2504. pushSTACK(version); /* TYPE-ERROR slot DATUM */
  2505. pushSTACK(O(type_version)); /* TYPE-ERROR slot EXPECTED-TYPE */
  2506. pushSTACK(version);
  2507. pushSTACK(TheSubr(subr_self)->name);
  2508. error(type_error,GETTEXT("~S: :VERSION-argument should be NIL or a positive fixnum or :WILD or :NEWEST, not ~S"));
  2509. }
  2510. return STACK_0;
  2511. }
  2512. #ifdef PATHNAME_WIN32
  2513. /* the operating system manages a default-drive.
  2514. the operating system manages a default-directory on each drive. This
  2515. can change, if another floppy disk is inserted. */
  2516. /* a default-drive is kept: DEFAULT_DRIVE = O(default_drive). */
  2517. /* the variable *DEFAULT-PATHNAME-DEFAULTS* contains (as pathname) the
  2518. default value for each MERGE-operation. It is the one, which the system
  2519. "interpretes into" the pathnames entered by the user.
  2520. It is kept up to date with the DEFAULT_DRIVE: On
  2521. initialization the current device (in terms of DOS), on
  2522. change of DEFAULT_DRIVE via CD. */
  2523. #endif /* PATHNAME_WIN32 */
  2524. #ifdef PATHNAME_UNIX
  2525. /* The variable *DEFAULT-PATHNAME-DEFAULTS* contains (as pathname) the
  2526. default value for each MERGE-operation. It is the one, which the system
  2527. "interpretes into" the pathnames entered by the user. */
  2528. #endif
  2529. #ifdef UNIX
  2530. /* the operating system manages a default-directory ("working directory")
  2531. for this process. It can be changed with chdir and queried with getwd.
  2532. See CHDIR(2) and GETWD(3). */
  2533. #endif
  2534. /* UP: Re-calculation of *DEFAULT-PATHNAME-DEFAULTS* */
  2535. #ifdef PATHNAME_WIN32
  2536. /* from DEFAULT_DRIVE */
  2537. #endif
  2538. /* recalc_defaults_pathname();
  2539. < result: value of *DEFAULT-PATHNAME-DEFAULTS*, a pathname
  2540. can trigger GC */
  2541. local maygc object recalc_defaults_pathname (void) {
  2542. #ifdef PATHNAME_WIN32
  2543. /* execute (MAKE-PATHNAME :DEVICE default-drive) : */
  2544. pushSTACK(S(Kdevice)); pushSTACK(O(default_drive));
  2545. funcall(L(make_pathname),2);
  2546. #endif
  2547. #ifdef PATHNAME_UNIX
  2548. /* execute (MAKE-PATHNAME) : */
  2549. funcall(L(make_pathname),0);
  2550. #endif
  2551. /* and assign *DEFAULT-PATHNAME-DEFAULTS* : */
  2552. return Symbol_value(S(default_pathname_defaults)) = value1;
  2553. }
  2554. /* UP: Returns the default-pathname.
  2555. defaults_pathname()
  2556. < result: value of *DEFAULT-PATHNAME-DEFAULTS*, a pathname
  2557. can trigger GC */
  2558. local maygc object defaults_pathname (void) {
  2559. var object pathname = Symbol_value(S(default_pathname_defaults)); /* value of *DEFAULT-PATHNAME-DEFAULTS* */
  2560. if (xpathnamep(pathname)) { /* is a pathname -> OK */
  2561. return pathname;
  2562. } else { /* else warning: */
  2563. pushSTACK(CLSTEXT("The value of ~S was not a pathname. ~:*~S is being reset."));
  2564. pushSTACK(S(default_pathname_defaults));
  2565. funcall(S(warn),2);
  2566. /* and re-calculate: */
  2567. return recalc_defaults_pathname();
  2568. }
  2569. }
  2570. /* merge two directories
  2571. > p_directory: pathname directory list
  2572. > d_directory: defaults directory list
  2573. > p_log: flag, whether pathname is logical
  2574. > wildp: flag, from MERGE-PATHNAMES
  2575. > called_from_make_pathname: flag, from MERGE-PATHNAMES
  2576. < result: merges directory list
  2577. can trigger GC */
  2578. local maygc object merge_dirs (object p_directory, object d_directory, bool p_log,
  2579. bool wildp, bool called_from_make_pathname) {
  2580. var object new_subdirs = p_directory;
  2581. #if DEBUG_TRANSLATE_PATHNAME
  2582. printf("[%d] merge_dirs: log: %d; wild: %d; cfmp: %d\n",
  2583. __LINE__,p_log,wildp,called_from_make_pathname);
  2584. #endif
  2585. SDOUT("merge_dirs:",p_directory);
  2586. SDOUT("merge_dirs:",d_directory);
  2587. if (called_from_make_pathname) {
  2588. if (!boundp(p_directory)) /* pathname-subdirs not given? */
  2589. new_subdirs = d_directory; /* use defaults-subdirs */
  2590. } else if (!wildp) {
  2591. if (nullp(p_directory) /* is pathname-subdirs trivial? */
  2592. || (eq(Car(p_directory),p_log ? S(Kabsolute) : S(Krelative))
  2593. && matomp(Cdr(p_directory)))) {
  2594. new_subdirs = d_directory; /* use defaults-subdirs */
  2595. } else if (eq(Car(p_directory),S(Krelative))
  2596. /* PATHNAME = :ABSOLUTE ==> merge is not needed */
  2597. && consp(d_directory) /* DEFAULT = NIL ==> nothing to merge */
  2598. && (eq(Car(d_directory),S(Kabsolute))
  2599. || !nullpSv(merge_pathnames_ansi))) {
  2600. /* (append defaults-subdirs (cdr pathname-subdirs)) =
  2601. (nreconc (reverse defaults-subdirs) (cdr pathname-subdirs)) : */
  2602. pushSTACK(Cdr(p_directory));
  2603. var object temp = reverse(d_directory);
  2604. new_subdirs = simplify_directory(nreconc(temp,popSTACK()));
  2605. }
  2606. }
  2607. return new_subdirs;
  2608. }
  2609. /* (MERGE-PATHNAMES pathname [defaults [default-version]] [:wild]), CLTL p. 415
  2610. Definition assuming that HAS_HOST and HAS_DEVICE are exclusive:
  2611. (defun merge-pathnames (pathname &optional (defaults *default-pathname-defaults*) default-version)
  2612. (setq pathname (pathname pathname))
  2613. (setq defaults (pathname defaults))
  2614. (multiple-value-call #'make-pathname
  2615. #if HAS_HOST
  2616. (if (or (equal (pathname-host pathname) (pathname-host defaults))
  2617. (null (pathname-host pathname)))
  2618. (values
  2619. :host (or (pathname-host pathname) (pathname-host defaults))
  2620. #endif
  2621. #if HAS_DEVICE
  2622. (if (or (equal (pathname-device pathname) (pathname-device defaults))
  2623. (null (pathname-device pathname)))
  2624. (values
  2625. :device (or (pathname-device pathname) (pathname-device defaults))
  2626. #endif
  2627. :directory
  2628. (let ((pathname-dir (pathname-directory pathname))
  2629. (defaults-dir (pathname-directory defaults)))
  2630. (if (eq (car pathname-dir) ':RELATIVE)
  2631. (cond ((null (cdr pathname-dir)) defaults-dir)
  2632. ((or *merge-pathnames-ansi*
  2633. (not (eq (car defaults-dir) ':RELATIVE))) ; <----
  2634. (append defaults-dir (cdr pathname-dir)))
  2635. (t pathname-dir))
  2636. pathname-dir)))
  2637. (values
  2638. #if HAS_HOST
  2639. :host (pathname-host pathname)
  2640. #endif
  2641. #if HAS_DEVICE
  2642. :device (pathname-device pathname)
  2643. #endif
  2644. :directory (pathname-directory pathname)))
  2645. :name (or (pathname-name pathname) (pathname-name defaults))
  2646. :type (or (pathname-type pathname) (pathname-type defaults))))
  2647. If HAS_HOST and HAS_DEVICE are both true, the semantics are more
  2648. complicated; see CLHS for details.
  2649. If the :WILD argument is specified, :WILD components are replaced,
  2650. instead of missing components.
  2651. Explanation of the "<----" line:
  2652. Roger Kehr <kehr@iti.informatik.th-darmstadt.de> asks why in CLISP
  2653. (merge-pathnames (make-pathname :directory '(:relative "x"))
  2654. (make-pathname :directory '(:relative "y")))
  2655. => #"x/"
  2656. where he expects to get #"y/x/".
  2657. Bruno: There are two reasons for this behaviour:
  2658. 1. An informal one: I found the latter behaviour confusing and changed
  2659. CLISP to do it the former way. It seems to work better this way.
  2660. 2. A formal one: MERGE-PATHNAMES is used to specify default components
  2661. for pathnames, so there is some analogy between (MERGE-PATHNAMES a b)
  2662. and (or a b). Obviously putting in the same default a second time
  2663. should do the same as putting it in once:
  2664. (or a b b) is the same as (or a b), so
  2665. (MERGE-PATHNAMES (MERGE-PATHNAMES a b) b) should be the same as
  2666. (MERGE-PATHNAMES a b).
  2667. (This question actually matters because in Common Lisp there is
  2668. no distinction between "pathnames with defaults merged-in" and
  2669. "pathnames with defaults not yet applied". For example, you do not
  2670. know whether COMPILE-FILE will merge in some defaults.)
  2671. Now, (MERGE-PATHNAMES (MERGE-PATHNAMES '#"x/" '#"y/") '#"y/")
  2672. and (MERGE-PATHNAMES '#"x/" '#"y/")
  2673. are equal in CLISP's implementation, but not in implementations
  2674. that strictly follow the Common Lisp spec. In fact, the above
  2675. twice-default = once-default rule holds for all pathnames in CLISP. */
  2676. LISPFUN(merge_pathnames,seclass_read,1,2,norest,key,1, (kw(wild))) {
  2677. /* :wild #'make-pathname causes NIL components to be considered specified,
  2678. only #<unbound> components are considered unspecified. */
  2679. var bool called_from_make_pathname = eq(STACK_0,L(make_pathname));
  2680. /* :wild t causes only wild components to be considered unspecified. */
  2681. var bool wildp = !missingp(STACK_0);
  2682. skipSTACK(1);
  2683. #define SPECIFIED(obj) \
  2684. !(called_from_make_pathname ? !boundp(obj) : \
  2685. (wildp ? eq(obj,S(Kwild)) : nullp(obj)))
  2686. #define NAMETYPE_MATCH(acc,slot) \
  2687. { var object tmp = x##slot(p_log,p); \
  2688. acc(newp)->slot = (SPECIFIED(tmp) ? tmp : (object)x##slot(d_log,d)); \
  2689. }
  2690. /* check pathname (STACK_2) and defaults (STACK_1):
  2691. (coerce defaults 'pathname): */
  2692. STACK_1 = test_default_pathname(STACK_1);
  2693. /* (coerce pathname 'pathname): */
  2694. #ifdef LOGICAL_PATHNAMES
  2695. if (logpathnamep(STACK_1)) {
  2696. if (!xpathnamep(STACK_2)) { /* pathname */
  2697. STACK_2 = parse_as_logical(STACK_2);
  2698. DOUT("merge-pathnames:[log_pathname]",STACK_2);
  2699. }
  2700. } else
  2701. #endif
  2702. STACK_2 = coerce_xpathname(STACK_2); /* pathname */
  2703. var bool d_log = logpathnamep(STACK_1);
  2704. var bool p_log = logpathnamep(STACK_2);
  2705. { /* check default-version (STACK_0): */
  2706. var object v = test_optional_version(unbound);
  2707. var object p_version = xpathname_version(p_log,STACK_2);
  2708. var object d_version = xpathname_version(d_log,STACK_1);
  2709. var object p_name = xpathname_name(p_log,STACK_2);
  2710. if (SPECIFIED(p_version))
  2711. v = p_version;
  2712. if (missingp(v) && !SPECIFIED(p_name) && SPECIFIED(d_version))
  2713. v = d_version;
  2714. if (!boundp(v)) v = S(Knewest);
  2715. STACK_0 = STACK_1; STACK_1 = STACK_2; STACK_2 = v;
  2716. DOUT("merge-pathnames:",v);
  2717. }
  2718. /* stack layout: default-version, pathname, defaults. */
  2719. /* do the merge */
  2720. #ifdef LOGICAL_PATHNAMES
  2721. DOUT("merge-pathnames:[defaults]",STACK_0);
  2722. DOUT("merge-pathnames:[pathname]",STACK_1);
  2723. if (d_log || p_log) {
  2724. /* MERGE-PATHNAMES for logical pathnames */
  2725. var object newp = allocate_logpathname(); /* fetch new pathname */
  2726. var object d = popSTACK(); /* defaults */
  2727. var object p = popSTACK(); /* pathname */
  2728. { /* match hosts: */
  2729. var object p_host = xpathname_host(p_log,p);
  2730. var object d_host = xpathname_host(d_log,d);
  2731. TheLogpathname(newp)->pathname_host = p_host; /* initially, new-host := pathname-host */
  2732. if (equal(p_host,d_host))
  2733. goto lmatch_directories;
  2734. if (wildp ? !boundp(p_host) : nullp(p_host)) {
  2735. /* pathname-host not specified, but defaults-host specified: */
  2736. TheLogpathname(newp)->pathname_host = d_host; /* new-host := defaults-host */
  2737. goto lmatch_directories;
  2738. }
  2739. }
  2740. { /* directories do not match: new-directory := pathname-directory */
  2741. var object dir = xpathname_directory(p_log,p);
  2742. TheLogpathname(newp)->pathname_directory =
  2743. (!SPECIFIED(dir) ? xpathname_directory(d_log,d) : dir);
  2744. goto ldirectories_OK;
  2745. }
  2746. lmatch_directories:
  2747. { /* match directories: */
  2748. pushSTACK(p); pushSTACK(d); pushSTACK(newp);
  2749. TheLogpathname(STACK_0)->pathname_directory =
  2750. merge_dirs(xpathname_directory(p_log,p),
  2751. xpathname_directory(d_log,d),
  2752. p_log,wildp,called_from_make_pathname);
  2753. newp = popSTACK(); d = popSTACK(); p = popSTACK();
  2754. }
  2755. ldirectories_OK:
  2756. /* the directories are OK now */
  2757. NAMETYPE_MATCH(TheLogpathname,pathname_name);
  2758. NAMETYPE_MATCH(TheLogpathname,pathname_type);
  2759. TheLogpathname(newp)->pathname_version = popSTACK();
  2760. DOUT("merge-pathnames:[ret]",newp);
  2761. VALUES1(newp);
  2762. return;
  2763. }
  2764. /* not both are logical pathnames -> first, convert into normal pathnames: */
  2765. STACK_1 = coerce_pathname(STACK_1);
  2766. STACK_0 = coerce_pathname(STACK_0);
  2767. #endif
  2768. var object newp = allocate_pathname(); /* fetch new pathname */
  2769. var object d = popSTACK(); /* defaults */
  2770. var object p = popSTACK(); /* pathname */
  2771. #if HAS_HOST
  2772. { /* match hosts: */
  2773. var object p_host = ThePathname(p)->pathname_host;
  2774. var object d_host = ThePathname(d)->pathname_host;
  2775. ThePathname(newp)->pathname_host = p_host; /* initially, new-host := pathname-host */
  2776. /* both hosts equal -> match devices: */
  2777. if (equal(p_host,d_host))
  2778. goto match_devices;
  2779. if (!(wildp ? false : nullp(p_host)))
  2780. goto notmatch_devices;
  2781. #ifdef PATHNAME_WIN32
  2782. var object p_device = ThePathname(p)->pathname_device;
  2783. /* On Win32, a non-null p_device implicitly designates p_host as the
  2784. local machine. It must not be overridden by d_host. */
  2785. if (SPECIFIED(p_device))
  2786. goto notmatch_devices;
  2787. #endif
  2788. /* pathname-host not specified, but defaults-host specified: */
  2789. ThePathname(newp)->pathname_host = d_host; /* new-host := defaults-host */
  2790. goto match_devices;
  2791. }
  2792. #endif /* HAS_HOST */
  2793. match_devices:
  2794. #if HAS_DEVICE
  2795. { /* match devices: */
  2796. var object p_device = ThePathname(p)->pathname_device;
  2797. var object d_device = ThePathname(d)->pathname_device;
  2798. ThePathname(newp)->pathname_device = p_device; /* initially, new-device := pathname-device */
  2799. /* both devices equal -> match directories: */
  2800. if (equal(p_device,d_device))
  2801. goto match_directories;
  2802. if (!SPECIFIED(p_device)) {
  2803. /* pathname-device not given, but defaults-device is given: */
  2804. ThePathname(newp)->pathname_device = d_device; /* new-device := defaults-device */
  2805. goto match_directories;
  2806. }
  2807. goto notmatch_directories;
  2808. }
  2809. #endif /* HAS_DEVICE */
  2810. match_directories: { /* match directories: */
  2811. var object tmp;
  2812. pushSTACK(p); pushSTACK(d); pushSTACK(newp);
  2813. tmp = merge_dirs(ThePathname(p)->pathname_directory,
  2814. ThePathname(d)->pathname_directory,
  2815. false,wildp,called_from_make_pathname);
  2816. newp = popSTACK(); d = popSTACK(); p = popSTACK();
  2817. ThePathname(newp)->pathname_directory = tmp;
  2818. }
  2819. goto directories_OK;
  2820. /* do not match devices: */
  2821. notmatch_devices:
  2822. #if HAS_DEVICE
  2823. { /* new-device := pathname-device : */
  2824. ThePathname(newp)->pathname_device = ThePathname(p)->pathname_device;
  2825. }
  2826. #endif
  2827. notmatch_directories:
  2828. { /* directories do not match: new-directory := pathname-directory */
  2829. var object dir = xpathname_directory(p_log,p);
  2830. ThePathname(newp)->pathname_directory =
  2831. (missingp(dir) ? xpathname_directory(d_log,d) : dir);
  2832. }
  2833. directories_OK:
  2834. /* the directories are OK now */
  2835. NAMETYPE_MATCH(ThePathname,pathname_name);
  2836. NAMETYPE_MATCH(ThePathname,pathname_type);
  2837. ThePathname(newp)->pathname_version = popSTACK();
  2838. DOUT("merge-pathnames:[ret]",newp);
  2839. VALUES1(newp);
  2840. }
  2841. #undef SPECIFIED
  2842. #undef NAMETYPE_MATCH
  2843. /* (ENOUGH-NAMESTRING pathname [defaults]), CLTL p. 417
  2844. Definition assuming that HAS_HOST and HAS_DEVICE are exclusive:
  2845. (defun enough-namestring (pathname &optional (defaults *default-pathname-defaults*))
  2846. (setq pathname (pathname pathname))
  2847. (setq defaults (pathname defaults))
  2848. (namestring
  2849. (multiple-value-call #'make-pathname
  2850. #if HAS_HOST
  2851. (if (equal (pathname-host pathname) (pathname-host defaults))
  2852. (values
  2853. :host nil
  2854. #endif
  2855. #if HAS_DEVICE
  2856. (if (equal (pathname-device pathname) (pathname-device defaults))
  2857. (values
  2858. :device nil
  2859. #endif
  2860. :directory
  2861. (let ((pathname-dir (pathname-directory pathname))
  2862. (defaults-dir (pathname-directory defaults)))
  2863. (if (equal pathname-dir defaults-dir)
  2864. (list ':RELATIVE)
  2865. (if (and (not (eq (car pathname-dir) ':RELATIVE))
  2866. (not (eq (car defaults-dir) ':RELATIVE))
  2867. (equal (subseq pathname-dir 0 (min (length pathname-dir) (length defaults-dir)))
  2868. defaults-dir
  2869. ) )
  2870. (cons ':RELATIVE (nthcdr (length defaults-dir) pathname-dir))
  2871. pathname-dir
  2872. ) ) )
  2873. )
  2874. (values
  2875. #if HAS_HOST
  2876. :host (pathname-host pathname)
  2877. #endif
  2878. #if HAS_DEVICE
  2879. :device (pathname-device pathname)
  2880. #endif
  2881. :directory (pathname-directory pathname)))
  2882. :name (if (equal (pathname-name pathname) (pathname-name defaults))
  2883. nil
  2884. (pathname-name pathname))
  2885. :type (if (equal (pathname-type pathname) (pathname-type defaults))
  2886. nil
  2887. (pathname-type pathname)))))
  2888. If HAS_HOST and HAS_DEVICE are both true, the semantics are more
  2889. complicated; see CLHS for details. */
  2890. #define SET_NEWP(slot,value) \
  2891. if (log2) TheLogpathname(newp)->slot = value; \
  2892. else ThePathname(newp)->slot = value;
  2893. LISPFUN(enough_namestring,seclass_read,1,1,norest,nokey,0,NIL) {
  2894. /* check pathname and defaults:
  2895. turn pathname into a Pathname: */
  2896. STACK_1 = coerce_xpathname(STACK_1);
  2897. var bool log2 = logpathnamep(STACK_1);
  2898. /* turn defaults into a Pathname: */
  2899. STACK_0 = test_default_pathname(STACK_0);
  2900. var bool log1 = logpathnamep(STACK_0);
  2901. /* fetch new Pathname: */
  2902. var object newp = (log2 ? allocate_logpathname() : allocate_pathname());
  2903. pushSTACK(newp);
  2904. /* stack layout: pathname, defaults, new. */
  2905. #if HAS_HOST
  2906. { /* compare hosts: */
  2907. var object p_host = xpathname_host(log2,STACK_2); /* pathname-host */
  2908. var object d_host = xpathname_host(log1,STACK_1); /* defaults-host */
  2909. if (equal(p_host,d_host)) { /* both hosts equal? */
  2910. SET_NEWP(pathname_host,NIL); /* new-host := NIL */
  2911. #endif
  2912. #if HAS_DEVICE
  2913. { /* compare devices: */
  2914. var object p_device = xpathname_device(log2,STACK_2);
  2915. var object d_device = xpathname_device(log1,STACK_1);
  2916. if (equal(p_device,d_device)) { /* both devices equal? */
  2917. if (!log2) ThePathname(newp)->pathname_device = NIL;
  2918. #endif
  2919. {
  2920. var object p_directory = xpathname_directory(log2,STACK_2);
  2921. var object d_directory = xpathname_directory(log1,STACK_1);
  2922. var object new_subdirs;
  2923. /* compare pathname-subdirs and defaults-subdirs: */
  2924. if (equal(p_directory,d_directory)) { /* ==> use NIL : */
  2925. new_subdirs = NIL;
  2926. } else {
  2927. /* Does neither pathname-subdirs nor defaults-subdirs
  2928. start with :RELATIVE ? */
  2929. if ( consp(p_directory) && (eq(Car(p_directory),S(Kabsolute)))
  2930. && consp(d_directory) && (eq(Car(d_directory),S(Kabsolute)))) {
  2931. /* yes -> test, if defaults-subdirs is a starting piece
  2932. of the list pathname-subdirs: */
  2933. var object Lp = p_directory;
  2934. var object Ld = d_directory;
  2935. /* Is Ld a starting piece of Lp ? */
  2936. while (1) {
  2937. if (atomp(Ld)) { /* Ld finished -> yes */
  2938. new_subdirs = Lp;
  2939. /* new-subdirs := (cons :RELATIVE new-subdirs) : */
  2940. pushSTACK(new_subdirs);
  2941. new_subdirs = allocate_cons();
  2942. Cdr(new_subdirs) = popSTACK();
  2943. Car(new_subdirs) = S(Krelative);
  2944. goto subdirs_ok;
  2945. }
  2946. if (atomp(Lp))
  2947. break; /* Lp finished -> no */
  2948. if (!equal(Car(Ld),Car(Lp))) /* different list-elements? */
  2949. break; /* -> no */
  2950. Ld = Cdr(Ld); Lp = Cdr(Lp); /* advance lists */
  2951. }
  2952. }
  2953. new_subdirs = p_directory; /* new-subdirs := pathname-subdirs */
  2954. }
  2955. subdirs_ok: /* new-subdirs is the new subdir-list. */
  2956. /* new-directory := new-subdirs : */
  2957. newp = STACK_0;
  2958. SET_NEWP(pathname_directory,new_subdirs);
  2959. }
  2960. #if HAS_DEVICE
  2961. } else {
  2962. /* different devices
  2963. (Note for PATHNAME_WIN32: If we have different devices, the common
  2964. host must have been NIL.)
  2965. new-device := pathname-device
  2966. new-directory := pathname-directory */
  2967. if (log2) {
  2968. TheLogpathname(newp)->pathname_directory =
  2969. TheLogpathname(STACK_2)->pathname_directory;
  2970. } else {
  2971. ThePathname(newp)->pathname_device = p_device;
  2972. ThePathname(newp)->pathname_directory =
  2973. ThePathname(STACK_2)->pathname_directory;
  2974. }
  2975. }
  2976. }
  2977. #endif
  2978. #if HAS_HOST
  2979. } else { /* different hosts */
  2980. /* new-host := pathname-host
  2981. new-device := pathname-device
  2982. new-directory := pathname-directory */
  2983. if (log2) {
  2984. TheLogpathname(newp)->pathname_host = p_host;
  2985. TheLogpathname(newp)->pathname_directory =
  2986. TheLogpathname(STACK_2)->pathname_directory;
  2987. } else {
  2988. ThePathname(newp)->pathname_host = p_host;
  2989. #if HAS_DEVICE
  2990. ThePathname(newp)->pathname_device =
  2991. ThePathname(STACK_2)->pathname_device;
  2992. #endif
  2993. ThePathname(newp)->pathname_directory =
  2994. ThePathname(STACK_2)->pathname_directory;
  2995. }
  2996. }
  2997. }
  2998. #endif
  2999. { /* fill in name: */
  3000. var object p_name = xpathname_name(log2,STACK_2); /* pathname-name */
  3001. var object d_name = xpathname_name(log1,STACK_1); /* defaults-name */
  3002. var object r_name = (equal(p_name,d_name) ? NIL : p_name);
  3003. SET_NEWP(pathname_name,r_name);
  3004. }
  3005. { /* fill in type: */
  3006. var object p_type = xpathname_type(log2,STACK_2); /* pathname-type */
  3007. var object d_type = xpathname_type(log1,STACK_1); /* defaults-type */
  3008. var object r_type = (equal(p_type,d_type) ? NIL : p_type);
  3009. SET_NEWP(pathname_type,r_type);
  3010. }
  3011. skipSTACK(3);
  3012. /* build (namestring new) : */
  3013. with_saved_back_trace_subr(L(namestring),STACK STACKop -1,-1,
  3014. VALUES1(whole_namestring(newp)); );
  3015. }
  3016. #undef SET_NEWP
  3017. #ifdef LOGICAL_PATHNAMES
  3018. /* UP: checks, if object is an admissible name:
  3019. :WILD or a Simple-String made of valid characters, without adjacent '*'.
  3020. legal_logical_word(object)
  3021. > object: if a simple-string, a normal-simple-string */
  3022. local bool legal_logical_word (object obj) {
  3023. if (eq(obj,S(Kwild)))
  3024. return true;
  3025. if (!simple_string_p(obj))
  3026. return false;
  3027. ASSERT(sstring_normal_p(obj));
  3028. var uintL len = Sstring_length(obj);
  3029. if (len==0)
  3030. return false; /* empty word is forbidden */
  3031. SstringDispatch(obj,X, {
  3032. var const cintX* charptr = &((SstringX)TheVarobject(obj))->data[0];
  3033. var bool last_was_star = false;
  3034. dotimespL(len,len, {
  3035. var chart cc = as_chart(*charptr++);
  3036. if (!(legal_logical_word_char(cc) || starp(cc)))
  3037. return false;
  3038. if (starp(cc)) {
  3039. if (last_was_star)
  3040. return false; /* adjacent '*' are forbidden */
  3041. last_was_star = true;
  3042. } else {
  3043. last_was_star = false;
  3044. }
  3045. });
  3046. });
  3047. return true;
  3048. }
  3049. #endif
  3050. #ifdef PATHNAME_NOEXT
  3051. /* UP: checks, if object is an admissible name:
  3052. a Simple-String made of valid characters
  3053. legal_name(object)
  3054. > object: any object */
  3055. #define legal_name(obj) check_name(obj,NULL)
  3056. /* also, return the _BASE ONE_ index of the first dot in the string */
  3057. local bool check_name (object obj, uintL *dot_pos_) {
  3058. if (dot_pos_) *dot_pos_ = 0;
  3059. if (!stringp(obj)) return false;
  3060. var uintL len, offset;
  3061. obj = unpack_string_ro(obj,&len,&offset);
  3062. if (len > 0) {
  3063. SstringDispatch(obj,X, {
  3064. var const cintX* start = ((SstringX)TheVarobject(obj))->data + offset;
  3065. var const cintX* charptr = start;
  3066. do { var chart cc = as_chart(*charptr++);
  3067. if (!legal_namechar(cc)) return false;
  3068. if (dot_pos_ && *dot_pos_==0 && dotp(cc))
  3069. *dot_pos_ = charptr - start;
  3070. } while(--len);
  3071. });
  3072. }
  3073. return true;
  3074. }
  3075. /* UP: checks, if object is an admissible name:
  3076. a Simple-String made of valid characters, without '.'
  3077. legal_type(object)
  3078. > object: if a simple-string, a normal-simple-string */
  3079. local bool legal_type (object obj);
  3080. #ifdef PATHNAME_NOEXT
  3081. local bool legal_type (object obj) {
  3082. if (!simple_string_p(obj))
  3083. return false;
  3084. ASSERT(sstring_normal_p(obj));
  3085. var uintL len = Sstring_length(obj);
  3086. if (len > 0) {
  3087. SstringDispatch(obj,X, {
  3088. var const cintX* charptr = &((SstringX)TheVarobject(obj))->data[0];
  3089. dotimespL(len,len, {
  3090. var chart cc = as_chart(*charptr++);
  3091. if (dotp(cc) || !legal_namechar(cc))
  3092. return false;
  3093. });
  3094. });
  3095. }
  3096. return true;
  3097. }
  3098. /* Check that the namestring for path will be parsed into a similar object
  3099. used by pr_orecord() in io.d
  3100. can trigger GC */
  3101. global maygc bool namestring_correctly_parseable_p (gcv_object_t *path_)
  3102. {
  3103. /* #p".foo" can be either :name ".foo" or :type "foo" */
  3104. var object name = ThePathname(*path_)->pathname_name;
  3105. var object type = ThePathname(*path_)->pathname_type;
  3106. var uintL dot_position;
  3107. check_name(name,&dot_position); /* we know it's valid! */
  3108. if (eq(Symbol_value(S(parse_namestring_dot_file)),S(Ktype))) {
  3109. parse_namestring_dot_file_type: /* ".foo" ==> :type "foo" */
  3110. if (nullp(type) && dot_position>0) return false; /* name has '.' => bad */
  3111. } else if (eq(Symbol_value(S(parse_namestring_dot_file)),S(Kname))) {
  3112. /* ".foo" ==> :name ".foo" */
  3113. if (nullp(name) && !nullp(type)) return false;
  3114. /* has dots _inside_ the name, and type=nil */
  3115. if (nullp(type) && dot_position>1) return false;
  3116. } else {
  3117. fix_parse_namestring_dot_file(); /* set to :TYPE */
  3118. name = ThePathname(*path_)->pathname_name; /* restore after posible GC */
  3119. type = ThePathname(*path_)->pathname_type;
  3120. goto parse_namestring_dot_file_type;
  3121. }
  3122. /* name cannot be "": it is replaced with NIL by MAKE-PATHNAME; */
  3123. #if HAS_VERSION
  3124. /* when the underlying physical file system DOES support version,
  3125. we are confident - for no good reason so far! -
  3126. that we will be able to print the pathname properly */
  3127. return true;
  3128. #else
  3129. /* when the underlying physical file system does NOT support version,
  3130. pathname version is not printed, so cannot be read back! */
  3131. return nullp(ThePathname(*path_)->pathname_version);
  3132. #endif
  3133. }
  3134. #endif
  3135. #endif /* PATHNAME_NOEXT */
  3136. local object copy_pathname (object pathname);
  3137. /* check whether the list is a valid directory list */
  3138. local bool directory_list_valid_p (bool logical, object dirlist) {
  3139. { /* CAR must be either :RELATIVE or :ABSOLUTE ? */
  3140. var object startpoint = Car(dirlist);
  3141. if (!(eq(startpoint,S(Krelative)) || eq(startpoint,S(Kabsolute))))
  3142. return false;
  3143. }
  3144. dirlist = Cdr(dirlist);
  3145. /* check subdir list: */
  3146. while (consp(dirlist)) {
  3147. /* check the next subdir = POP(dirlist); */
  3148. var object subdir = Car(dirlist); dirlist = Cdr(dirlist);
  3149. #ifdef LOGICAL_PATHNAMES
  3150. if (logical) {
  3151. if (!(eq(subdir,S(Kwild_inferiors)) || eq(subdir,S(Kwild))
  3152. || legal_logical_word(subdir) || eq(subdir,S(Kup))))
  3153. return false;
  3154. } else
  3155. #endif
  3156. {
  3157. #ifdef PATHNAME_NOEXT
  3158. #if defined(PATHNAME_UNIX) || defined(PATHNAME_WIN32)
  3159. if (!(eq(subdir,S(Kwild_inferiors)) || eq(subdir,S(Kwild))
  3160. || legal_name(subdir) || eq(subdir,S(Kup))))
  3161. return false;
  3162. #endif
  3163. #endif
  3164. }
  3165. }
  3166. return true;
  3167. }
  3168. #ifdef LOGICAL_PATHNAMES
  3169. #define COERCE_PATHNAME_SLOT(slot,obj,stack_res) \
  3170. stack_res = ThePathname(coerce_pathname(obj))->pathname_##slot
  3171. #else
  3172. #define COERCE_PATHNAME_SLOT(slot,obj,stack_res) \
  3173. stack_res = ThePathname(obj)->pathname_##slot
  3174. #endif
  3175. /* (MAKE-PATHNAME [:host] [:device] [:directory] [:name] [:type] [:version]
  3176. [:defaults] [:case]),
  3177. CLTL p. 416, CLtL2 p. 643 */
  3178. LISPFUN(make_pathname,seclass_read,0,0,norest,key,8,
  3179. (kw(defaults),kw(case),kw(host),kw(device),kw(directory),
  3180. kw(name),kw(type),kw(version)) )
  3181. { /* stack layout: defaults, case, host, device, directory,
  3182. name, type, version. */
  3183. var bool logical = false;
  3184. var bool convert = eq(STACK_6,S(Kcommon));
  3185. /* 0. check defaults (STACK_7): */
  3186. if (boundp(STACK_7)) {
  3187. #ifdef LOGICAL_PATHNAMES
  3188. if (!nullpSv(parse_namestring_ansi)
  3189. && stringp(STACK_7) && looks_logical_p(STACK_7))
  3190. STACK_7 = parse_as_logical(STACK_7);
  3191. else
  3192. #endif
  3193. STACK_7 = coerce_xpathname(STACK_7);
  3194. }
  3195. /* 1. check host: */
  3196. #ifdef LOGICAL_PATHNAMES
  3197. if (logpathnamep(STACK_5)) {
  3198. STACK_5 = TheLogpathname(STACK_5)->pathname_host;
  3199. logical = true;
  3200. }
  3201. #endif
  3202. if (!boundp(STACK_5)) {
  3203. var object d_path = defaults_pathname();
  3204. STACK_5 = (!boundp(STACK_7) ?
  3205. xpathname_host(logpathnamep(d_path),d_path) :
  3206. xpathname_host(logpathnamep(STACK_7),STACK_7));
  3207. } else {
  3208. #if HAS_HOST
  3209. STACK_5 = test_optional_host(STACK_5,convert);
  3210. #else
  3211. STACK_5 = test_optional_host(STACK_5);
  3212. #endif
  3213. }
  3214. #ifdef LOGICAL_PATHNAMES
  3215. if (!nullp(STACK_5) && logical_host_p(STACK_5)) {
  3216. logical = true; STACK_5 = string_upcase(STACK_5);
  3217. }
  3218. #endif
  3219. DOUT("make-pathname:[version]",STACK_0);
  3220. DOUT("make-pathname:[type]",STACK_1);
  3221. DOUT("make-pathname:[name]",STACK_2);
  3222. DOUT("make-pathname:[directory]",STACK_3);
  3223. DOUT("make-pathname:[device]",STACK_4);
  3224. DOUT("make-pathname:[host]",STACK_5);
  3225. DOUT("make-pathname:[case]",STACK_6);
  3226. DOUT("make-pathname:[defaults]",STACK_7);
  3227. #if HAS_DEVICE
  3228. { /* 2. check device: */
  3229. var object device = STACK_4;
  3230. if (!boundp(device)) {
  3231. if (!boundp(STACK_7)) /* no defaults? */
  3232. STACK_4 = NIL; /* -> use NIL */
  3233. } else {
  3234. if (stringp(device))
  3235. STACK_4 = device = coerce_normal_ss(device);
  3236. if (convert)
  3237. STACK_4 = device = common_case(device);
  3238. if (nullp(device)) /* = NIL ? */
  3239. goto device_ok;
  3240. #ifdef LOGICAL_PATHNAMES
  3241. else if (logical) {
  3242. if (logpathnamep(device) /* Pathname -> its device */
  3243. || (eq(device,S(Kunspecific)))) { /* :UNSPECIFIC -> NIL */
  3244. STACK_4 = NIL; goto device_ok;
  3245. }
  3246. }
  3247. #endif
  3248. #ifdef PATHNAME_WIN32
  3249. else if (eq(device,S(Kwild))) /* = :WILD ? */
  3250. goto device_ok;
  3251. else if (simple_string_p(device)) { /* Simple-String ? */
  3252. if (Sstring_length(device) == 1) { /* of length 1 ? */
  3253. var chart ch = schar(device,0);
  3254. if ((as_cint(ch) >= 'A') && (as_cint(ch) <= 'Z')) /* with letters >='A' and <='Z' ? */
  3255. goto device_ok;
  3256. }
  3257. }
  3258. #endif
  3259. else if (xpathnamep(device)) { /* Pathname -> its Device */
  3260. COERCE_PATHNAME_SLOT(device,device,STACK_4);
  3261. goto device_ok;
  3262. }
  3263. /* None of the desired cases -> error: */
  3264. pushSTACK(STACK_4); pushSTACK(S(Kdevice)); goto error_arg;
  3265. device_ok: ;
  3266. #ifdef PATHNAME_WIN32
  3267. if (!nullp(STACK_5) && !nullp(STACK_4)) {
  3268. pushSTACK(STACK_4);
  3269. pushSTACK(STACK_(5+1));
  3270. pushSTACK(TheSubr(subr_self)->name);
  3271. error(error_condition,
  3272. GETTEXT("~S: on host ~S, device ~S is invalid, should be NIL"));
  3273. }
  3274. #endif
  3275. }
  3276. }
  3277. #else /* HAS_DEVICE */
  3278. {
  3279. var object device = STACK_4;
  3280. if (boundp(device)) { /* specified ? */
  3281. if (!(nullp(device) || eq(device,S(Kunspecific))
  3282. || xpathnamep(device))) { /* NIL or :UNSPECIFIC or Pathname -> OK */
  3283. /* None of the desired cases -> error: */
  3284. pushSTACK(STACK_4); pushSTACK(S(Kdevice)); goto error_arg;
  3285. }
  3286. }
  3287. }
  3288. #endif
  3289. { /* 3. check directory: */
  3290. DOUT("make-pathname:[directory]",STACK_3);
  3291. var object directory = STACK_3;
  3292. if (!boundp(directory) && boundp(STACK_7)) {
  3293. /* not specified but defaults is supplied */
  3294. goto directory_ok;
  3295. } else if (missingp(directory)) { /* not specified or NIL */
  3296. STACK_3 = NIL; /* default_directory == NIL */
  3297. goto directory_ok;
  3298. } else if (eq(directory,S(Kwild)) || eq(directory,S(Kwild_inferiors))) {
  3299. directory = S(Kwild_inferiors);
  3300. goto directory_add_absolute;
  3301. } else if (stringp(directory)) {
  3302. if (!legal_name(directory)) goto directory_bad;
  3303. STACK_3 = directory = coerce_normal_ss(directory);
  3304. directory_add_absolute:
  3305. pushSTACK(S(Kabsolute));
  3306. pushSTACK(directory);
  3307. directory = listof(2); STACK_3 = directory;
  3308. goto directory_ok;
  3309. } else if (consp(directory)) { /* a Cons? */
  3310. STACK_3 = directory = simplify_directory(copy_list(directory));
  3311. if (convert)
  3312. STACK_3 = directory = subst_common_case(directory);
  3313. if (!directory_list_valid_p(logical,directory))
  3314. goto directory_bad;
  3315. else
  3316. goto directory_ok;
  3317. }
  3318. #ifdef LOGICAL_PATHNAMES
  3319. else if (logical) {
  3320. if (logpathnamep(directory)) { /* Pathname -> its Directory */
  3321. STACK_3 = TheLogpathname(directory)->pathname_directory;
  3322. goto directory_ok;
  3323. }
  3324. }
  3325. #endif
  3326. else if (xpathnamep(directory)) { /* Pathname -> its Directory */
  3327. COERCE_PATHNAME_SLOT(directory,directory,STACK_3);
  3328. goto directory_ok;
  3329. }
  3330. /* None of the desired cases -> error: */
  3331. directory_bad:
  3332. { pushSTACK(STACK_3); pushSTACK(S(Kdirectory)); } goto error_arg;
  3333. directory_ok: ;
  3334. }
  3335. { /* 4. check name: */
  3336. DOUT("make-pathname:[name]",STACK_2);
  3337. var object name = STACK_2;
  3338. if (stringp(name))
  3339. STACK_2 = name = coerce_normal_ss(name);
  3340. if (convert)
  3341. STACK_2 = name = common_case(name);
  3342. if (!boundp(name)) { /* not specified */
  3343. if (!boundp(STACK_7)) /* no defaults? */
  3344. STACK_2 = NIL; /* -> use NIL */
  3345. } else if (equal(name,O(empty_string))) { /* name = "" ? */
  3346. STACK_2 = NIL; /* -> use NIL */
  3347. } else if (nullp(name)) { /* NIL is OK */
  3348. }
  3349. #ifdef LOGICAL_PATHNAMES
  3350. else if (logical) {
  3351. if (legal_logical_word(name)) { /* OK */
  3352. } else if (logpathnamep(name)) { /* Pathname -> its Name */
  3353. STACK_2 = TheLogpathname(name)->pathname_name;
  3354. } else { /* None of the desired cases -> error: */
  3355. pushSTACK(STACK_2); pushSTACK(S(Kname)); goto error_arg;
  3356. }
  3357. }
  3358. #endif
  3359. #ifdef PATHNAME_NOEXT
  3360. else if (eq(name,S(Kwild))) { /* :WILD is OK */
  3361. }
  3362. #endif
  3363. else if (legal_name(name)) { /* admissible Name is OK */
  3364. STACK_2 = name = coerce_normal_ss(name);
  3365. } else if (xpathnamep(name)) { /* Pathname -> its Name */
  3366. COERCE_PATHNAME_SLOT(name,name,STACK_2);
  3367. } else { /* None of the desired cases -> error: */
  3368. pushSTACK(STACK_2); pushSTACK(S(Kname)); goto error_arg;
  3369. }
  3370. }
  3371. { /* 5. check type: */
  3372. DOUT("make-pathname:[type]",STACK_1);
  3373. var object type = STACK_1;
  3374. if (stringp(type))
  3375. STACK_1 = type = coerce_normal_ss(type);
  3376. if (convert)
  3377. STACK_1 = type = common_case(type);
  3378. if (!boundp(type)) {
  3379. if (!boundp(STACK_7)) /* no Defaults ? */
  3380. STACK_1 = NIL; /* -> use NIL */
  3381. } else if (nullp(type)) { /* NIL is OK */
  3382. }
  3383. #ifdef LOGICAL_PATHNAMES
  3384. else if (logical) {
  3385. if (legal_logical_word(type)) { /* OK */
  3386. } else if (logpathnamep(type)) { /* Pathname -> its Type */
  3387. STACK_1 = TheLogpathname(type)->pathname_type;
  3388. } else { /* None of the desired cases -> error: */
  3389. pushSTACK(STACK_1); pushSTACK(S(Ktype)); goto error_arg;
  3390. }
  3391. }
  3392. #endif
  3393. #ifdef PATHNAME_NOEXT
  3394. else if (eq(type,S(Kwild))) { /* :WILD is OK */
  3395. }
  3396. #endif
  3397. else if (legal_type(type)) {
  3398. } else if (xpathnamep(type)) { /* Pathname -> its Type */
  3399. COERCE_PATHNAME_SLOT(type,type,STACK_1);
  3400. } else { /* None of the desired cases -> error: */
  3401. pushSTACK(STACK_1); pushSTACK(S(Ktype)); goto error_arg;
  3402. }
  3403. }
  3404. /* 6. check version: */
  3405. { STACK_0 = test_optional_version(!boundp(STACK_7) ? NIL : unbound); }
  3406. DOUT("make-pathname:[ver]",STACK_0);
  3407. DOUT("make-pathname:[ver]",STACK_7);
  3408. { /* 7. build Pathname: */
  3409. var object pathname;
  3410. #ifdef LOGICAL_PATHNAMES
  3411. if (logical) {
  3412. pathname = allocate_logpathname(); /* new Logical Pathname */
  3413. TheLogpathname(pathname)->pathname_version = popSTACK();
  3414. TheLogpathname(pathname)->pathname_type = popSTACK();
  3415. TheLogpathname(pathname)->pathname_name = popSTACK();
  3416. TheLogpathname(pathname)->pathname_directory = popSTACK();
  3417. skipSTACK(1);
  3418. TheLogpathname(pathname)->pathname_host = popSTACK();
  3419. } else
  3420. #endif
  3421. {
  3422. pathname = allocate_pathname(); /* new Pathname */
  3423. ThePathname(pathname)->pathname_version = popSTACK();
  3424. ThePathname(pathname)->pathname_type = popSTACK();
  3425. ThePathname(pathname)->pathname_name = popSTACK();
  3426. ThePathname(pathname)->pathname_directory = popSTACK();
  3427. #if HAS_DEVICE
  3428. ThePathname(pathname)->pathname_device = popSTACK();
  3429. #else
  3430. skipSTACK(1);
  3431. #endif
  3432. #if HAS_HOST
  3433. ThePathname(pathname)->pathname_host = popSTACK();
  3434. #else
  3435. skipSTACK(1);
  3436. #endif
  3437. }
  3438. STACK_0 = pathname; /* forget case */
  3439. DOUT("make-pathname:[pathname]",STACK_0);
  3440. DOUT("make-pathname:[defaults]",STACK_1);
  3441. pathname = popSTACK();
  3442. /* 8. poss. merge in Defaults: */
  3443. var object defaults = popSTACK();
  3444. if (!boundp(defaults)) { /* no defaults given -> pathname is the value */
  3445. value1 = pathname;
  3446. } else {
  3447. /* (MERGE-PATHNAMES pathname defaults [nil] :wild #'make-pathname) */
  3448. pushSTACK(pathname); pushSTACK(defaults);
  3449. pushSTACK(unbound); pushSTACK(S(Kwild)); pushSTACK(L(make_pathname));
  3450. funcall(L(merge_pathnames),5);
  3451. }
  3452. mv_count=1;
  3453. DOUT("make-pathname:[ret]",value1);
  3454. return;
  3455. }
  3456. error_arg: /* error-message: */
  3457. pushSTACK(TheSubr(subr_self)->name);
  3458. error(error_condition,GETTEXT("~S: illegal ~S argument ~S"));
  3459. }
  3460. #undef COERCE_PATHNAME_SLOT
  3461. #ifdef LOGICAL_PATHNAMES
  3462. /* (MAKE-LOGICAL-PATHNAME [:host] [:device] [:directory] [:name]
  3463. [:type] [:version] [:defaults] [:case]),
  3464. like MAKE-PATHNAME, except that a Logical Pathname is built. */
  3465. LISPFUN(make_logical_pathname,seclass_read,0,0,norest,key,8,
  3466. (kw(defaults),kw(case),kw(host),kw(device),
  3467. kw(directory),kw(name),kw(type),kw(version)) )
  3468. { /* A logical pathname as :HOST-Argument for MAKE-PATHNAME
  3469. enforces a logical pathname as result. */
  3470. if (boundp(STACK_5)) STACK_5 = string_upcase(STACK_5); /* host */
  3471. else STACK_5 = NIL;
  3472. { var object obj = allocate_logpathname();
  3473. TheLogpathname(obj)->pathname_host = STACK_5;
  3474. STACK_5 = obj; }
  3475. /* PATHNAME-DEVICE for LOGICAL-PATHNAME returns :UNSPECIFIC, so
  3476. MAKE-LOGICAL-PATHNAME must accept :DEVICE :UNSPECIFIC */
  3477. if (eq(STACK_4,S(Kunspecific))) STACK_4 = NIL; /* device */
  3478. /* continue at MAKE-PATHNAME. */
  3479. C_make_pathname();
  3480. }
  3481. #endif
  3482. #ifdef USER_HOMEDIR
  3483. /* (USER-HOMEDIR-PATHNAME [host]), CLTL p. 418 */
  3484. LISPFUN(user_homedir_pathname,seclass_default,0,1,norest,nokey,0,NIL) {
  3485. DOUT("user-homedir-pathname:[host]",STACK_0);
  3486. #if HAS_HOST
  3487. STACK_0 = test_optional_host(STACK_0,false); /* check Host */
  3488. if (!nullp(STACK_0)) {
  3489. #if defined(PATHNAME_WIN32)
  3490. /* This is very primitive. Does Windows have the notion of homedirs on
  3491. remote hosts?? */
  3492. {
  3493. var object pathname = allocate_pathname(); /* new Pathname */
  3494. ThePathname(pathname)->pathname_host = popSTACK();
  3495. #if HAS_DEVICE
  3496. ThePathname(pathname)->pathname_device = NIL;
  3497. #endif
  3498. ThePathname(pathname)->pathname_directory = O(directory_absolute);
  3499. ThePathname(pathname)->pathname_name = NIL;
  3500. ThePathname(pathname)->pathname_type = NIL;
  3501. ThePathname(pathname)->pathname_version = NIL;
  3502. VALUES1(pathname);
  3503. }
  3504. #else
  3505. ??; /* FIXME for HAS_HOST & not WIN32 */
  3506. #endif
  3507. } else { /* no host given */
  3508. skipSTACK(1);
  3509. VALUES1(O(user_homedir)); /* User-Homedir-Pathname */
  3510. }
  3511. #else /* HAS_HOST */
  3512. test_optional_host(popSTACK()); /* check Host and ignore */
  3513. VALUES1(O(user_homedir)); /* User-Homedir-Pathname */
  3514. #endif
  3515. DOUT("user-homedir-pathname:[ret]",value1);
  3516. }
  3517. #endif
  3518. /* UP: copies a pathname.
  3519. copy_pathname(pathname)
  3520. > pathname: non-logical pathname
  3521. < result: copy of the pathname, with the same components
  3522. can trigger GC */
  3523. local maygc object copy_pathname (object pathname) {
  3524. pushSTACK(pathname);
  3525. var object newp = allocate_pathname();
  3526. pathname = popSTACK();
  3527. #if HAS_HOST
  3528. ThePathname(newp)->pathname_host
  3529. = ThePathname(pathname)->pathname_host;
  3530. #endif
  3531. #if HAS_DEVICE
  3532. ThePathname(newp)->pathname_device
  3533. = ThePathname(pathname)->pathname_device;
  3534. #endif
  3535. ThePathname(newp)->pathname_directory
  3536. = ThePathname(pathname)->pathname_directory;
  3537. ThePathname(newp)->pathname_name
  3538. = ThePathname(pathname)->pathname_name;
  3539. ThePathname(newp)->pathname_type
  3540. = ThePathname(pathname)->pathname_type;
  3541. ThePathname(newp)->pathname_version
  3542. = ThePathname(pathname)->pathname_version;
  3543. return newp;
  3544. }
  3545. /*
  3546. * Wildcards
  3547. * =========
  3548. */
  3549. #ifdef PATHNAME_NOEXT
  3550. /* UP: check whether the object is wild
  3551. wild_p(object)
  3552. > object: normal simple-string or symbol
  3553. < return: true when the object is wild */
  3554. local bool wild_p (object obj, bool dirp) {
  3555. if (simple_string_p(obj)) {
  3556. var uintL len = Sstring_length(obj);
  3557. if (len > 0) {
  3558. SstringDispatch(obj,X, {
  3559. var const cintX* charptr = &((SstringX)TheVarobject(obj))->data[0];
  3560. dotimespL(len,len, {
  3561. var chart ch = as_chart(*charptr++);
  3562. if (wild_char_p(ch))
  3563. return true;
  3564. });
  3565. });
  3566. }
  3567. return false;
  3568. } else
  3569. return eq(obj,S(Kwild)) || (dirp && eq(obj,S(Kwild_inferiors)));
  3570. }
  3571. #endif
  3572. #ifdef LOGICAL_PATHNAMES
  3573. /* UP: check whether the obj is a string with '*' or a :WILD
  3574. word_wild_p(object)
  3575. > object: normal simple-string or symbol
  3576. < return: true when the object is word-wild */
  3577. local bool word_wild_p (object obj, bool dirp) {
  3578. if (simple_string_p(obj)) {
  3579. var uintL len = Sstring_length(obj);
  3580. if (len > 0) {
  3581. SstringDispatch(obj,X, {
  3582. var const cintX* charptr = &((SstringX)TheVarobject(obj))->data[0];
  3583. dotimespL(len,len, {
  3584. if (starp(as_chart(*charptr++)))
  3585. return true;
  3586. });
  3587. });
  3588. }
  3589. return false;
  3590. } else
  3591. return eq(obj,S(Kwild)) || (dirp && eq(obj,S(Kwild_inferiors)));
  3592. }
  3593. #endif
  3594. /* UP: checks, if the host-component of a pathname contains wildcards.
  3595. has_host_wildcards(pathname)
  3596. > pathname: pathname
  3597. < result: true if (PATHNAME-HOST pathname) contains wildcards. */
  3598. local bool has_host_wildcards (object pathname);
  3599. /* host can not contain wildcards. */
  3600. #define has_host_wildcards(pathname) (unused (pathname), false)
  3601. /* UP: checks, if the device-component of a pathname contains wildcards.
  3602. has_device_wildcards(pathname)
  3603. > pathname: pathname
  3604. < result: true if (PATHNAME-DEVICE pathname) contains wildcards. */
  3605. local bool has_device_wildcards (object pathname) {
  3606. #ifdef PATHNAME_WIN32
  3607. #ifdef LOGICAL_PATHNAMES
  3608. if (logpathnamep(pathname))
  3609. return false;
  3610. #endif
  3611. /* check device: = :WILD ? */
  3612. return eq(ThePathname(pathname)->pathname_device,S(Kwild));
  3613. #else
  3614. return false;
  3615. #endif
  3616. }
  3617. /* UP: checks, if the directory-component of a pathname contains wildcards.
  3618. has_directory_wildcards(pathname)
  3619. > pathname: pathname
  3620. < result: true if (PATHNAME-DIRECTORY pathname) contains wildcards. */
  3621. local bool has_directory_wildcards (object pathname) {
  3622. /* check directory: */
  3623. #ifdef LOGICAL_PATHNAMES
  3624. if (logpathnamep(pathname)) {
  3625. var object directory = TheLogpathname(pathname)->pathname_directory;
  3626. for (;consp(directory); directory = Cdr(directory))
  3627. if (word_wild_p(Car(directory),true))
  3628. return true;
  3629. return false;
  3630. }
  3631. #endif
  3632. var object directory = ThePathname(pathname)->pathname_directory;
  3633. for (;consp(directory); directory = Cdr(directory))
  3634. if (wild_p(Car(directory),true))
  3635. return true;
  3636. return false;
  3637. }
  3638. /* UP: checks, if the name-component of a pathname contains wildcards.
  3639. has_name_wildcards(pathname)
  3640. > pathname: pathname
  3641. < result: true if (PATHNAME-NAME pathname) contains wildcards. */
  3642. local bool has_name_wildcards (object pathname) {
  3643. /* check name: */
  3644. #ifdef LOGICAL_PATHNAMES
  3645. if (logpathnamep(pathname))
  3646. return word_wild_p(TheLogpathname(pathname)->pathname_name,false);
  3647. #endif
  3648. #ifdef PATHNAME_NOEXT
  3649. return wild_p(ThePathname(pathname)->pathname_name,false);
  3650. #endif
  3651. return false;
  3652. }
  3653. /* UP: checks, if the type-component of a pathname contains wildcards.
  3654. has_type_wildcards(pathname)
  3655. > pathname: pathname
  3656. < result: true if (PATHNAME-TYPE pathname) contains wildcards. */
  3657. local bool has_type_wildcards (object pathname) {
  3658. /* check type: */
  3659. #ifdef LOGICAL_PATHNAMES
  3660. if (logpathnamep(pathname))
  3661. return word_wild_p(TheLogpathname(pathname)->pathname_type,false);
  3662. #endif
  3663. #ifdef PATHNAME_NOEXT
  3664. return wild_p(ThePathname(pathname)->pathname_type,false);
  3665. #endif
  3666. return false;
  3667. }
  3668. /* UP: checks, if the version-component of a pathname contains wildcards.
  3669. has_version_wildcards(pathname)
  3670. > pathname: pathname
  3671. < result: true if (PATHNAME-VERSION pathname) contains wildcards. */
  3672. local bool has_version_wildcards (object pathname) {
  3673. /* check version: */
  3674. return eq(S(Kwild),xpathname_version(logpathnamep(pathname),pathname));
  3675. }
  3676. /* UP: checks, if any component of a pathname contains wildcards.
  3677. has_some_wildcards(pathname)
  3678. > pathname: pathname
  3679. < result: true if pathname contains wildcards. */
  3680. local bool has_some_wildcards (object pathname) {
  3681. if (has_host_wildcards(pathname)) return true;
  3682. if (has_device_wildcards(pathname)) return true;
  3683. if (has_directory_wildcards(pathname)) return true;
  3684. if (has_name_wildcards(pathname)) return true;
  3685. if (has_type_wildcards(pathname)) return true;
  3686. if (has_version_wildcards(pathname)) return true;
  3687. return false;
  3688. }
  3689. /* UP: checks, if a pathname contains no wildcards.
  3690. check_no_wildcards(pathname);
  3691. > pathname: pathname */
  3692. local void check_no_wildcards (object pathname) {
  3693. if (!has_some_wildcards(pathname)) /* no wildcards found. */
  3694. return;
  3695. /* error-message, if the pathname contains wildcards: */
  3696. pushSTACK(pathname); /* FILE-ERROR slot PATHNAME */
  3697. pushSTACK(pathname);
  3698. error(file_error,GETTEXT("wildcards are not allowed here: ~S"));
  3699. }
  3700. LISPFUN(wild_pathname_p,seclass_read,1,1,norest,nokey,0,NIL)
  3701. { /* (WILD-PATHNAME-P pathname [field-key]), CLtL2 p. 623 */
  3702. var object pathname = coerce_xpathname(STACK_1);
  3703. var object key = STACK_0;
  3704. var bool erg;
  3705. if (missingp(key)) {
  3706. erg = has_some_wildcards(pathname);
  3707. } else if (eq(key,S(Khost))) {
  3708. erg = has_host_wildcards(pathname);
  3709. } else if (eq(key,S(Kdevice))) {
  3710. erg = has_device_wildcards(pathname);
  3711. } else if (eq(key,S(Kdirectory))) {
  3712. erg = has_directory_wildcards(pathname);
  3713. } else if (eq(key,S(Kname))) {
  3714. erg = has_name_wildcards(pathname);
  3715. } else if (eq(key,S(Ktype))) {
  3716. erg = has_type_wildcards(pathname);
  3717. } else if (eq(key,S(Kversion))) {
  3718. erg = has_version_wildcards(pathname);
  3719. } else {
  3720. pushSTACK(key); /* TYPE-ERROR slot DATUM */
  3721. pushSTACK(O(type_pathname_field_key)); /* TYPE-ERROR slot EXPECTED-TYPE */
  3722. pushSTACK(NIL);
  3723. pushSTACK(S(Kversion));
  3724. pushSTACK(S(Ktype));
  3725. pushSTACK(S(Kname));
  3726. pushSTACK(S(Kdirectory));
  3727. pushSTACK(S(Kdevice));
  3728. pushSTACK(S(Khost));
  3729. pushSTACK(key);
  3730. pushSTACK(TheSubr(subr_self)->name);
  3731. error(type_error,
  3732. GETTEXT("~S: argument ~S should be ~S, ~S, ~S, ~S, ~S, ~S or ~S"));
  3733. }
  3734. VALUES_IF(erg); /* boolean value */
  3735. skipSTACK(2);
  3736. }
  3737. /* Wildcard Matching
  3738. ================= */
  3739. /* For the purposes of wildcard matching, according to CLHS, non-present
  3740. components (i.e. NIL or a directory = (:RELATIVE)) are treated as wild. */
  3741. #if defined(PATHNAME_NOEXT) || defined(LOGICAL_PATHNAMES)
  3742. /* UP: Matches a Wildcard-String ("Pattern") with a "Sample".
  3743. > pattern : Normal-Simple-String, with wildcards
  3744. '?' for exactly 1 character
  3745. '*' for arbitrary many characters
  3746. > sample : Normal-Simple-String, that has to be matched
  3747. recursive implementation because of backtracking: */
  3748. local bool wildcard_match_ab (uintL m_count, const chart* m_ptr,
  3749. uintL b_count, const chart* b_ptr);
  3750. local bool wildcard_match (object pattern, object sample) {
  3751. if (eq(pattern,S(Kwild)) || eq(pattern,S(Kwild_inferiors)))
  3752. return true;
  3753. if (eq(pattern,S(Kup)) || eq(pattern,S(Kback)))
  3754. return false;
  3755. ASSERT(sstring_normal_p(pattern));
  3756. ASSERT(sstring_normal_p(sample));
  3757. return wildcard_match_ab(
  3758. /* m_count = */ Sstring_length(pattern),
  3759. /* m_ptr = */ &TheSnstring(pattern)->data[0],
  3760. /* b_count = */ Sstring_length(sample),
  3761. /* b_ptr = */ &TheSnstring(sample)->data[0]
  3762. );
  3763. }
  3764. local bool wildcard_match_ab (uintL m_count, const chart* m_ptr,
  3765. uintL b_count, const chart* b_ptr) {
  3766. var chart c;
  3767. while (1) {
  3768. if (m_count==0)
  3769. return (b_count==0); /* "" matches only "" */
  3770. m_count--;
  3771. c = *m_ptr++; /* next match-character */
  3772. if (chareq(c,ascii('?'))) { /* wildcard '?' */
  3773. if (b_count==0) return false; /* at least one character still has to come */
  3774. b_count--; b_ptr++; /* it will be ignored */
  3775. } else if (starp(c))
  3776. break; /* wildcard '*' later */
  3777. else { /* everything else must match exactly: */
  3778. if (b_count==0) return false;
  3779. b_count--; if (!equal_pathchar(*b_ptr++,c)) return false;
  3780. }
  3781. }
  3782. /* Wildcard '*': Search next non-wildcard-character and also count the '?'
  3783. (because a sequence '*??*???***?' matches everything, that is as least as
  3784. long as the sequence of question marks). The '?' can also be utilized
  3785. immediately, because '*??*???***?' is equivalent to '??????*' . */
  3786. while (1) {
  3787. if (m_count==0) return true; /* wildcard at the end matches the rest. */
  3788. m_count--;
  3789. c = *m_ptr++; /* next match-character */
  3790. if (chareq(c,ascii('?'))) {
  3791. /* question mark: move forward, process instantly */
  3792. if (b_count==0) return false;
  3793. b_count--; b_ptr++;
  3794. }
  3795. else if (!starp(c))
  3796. break;
  3797. }
  3798. /* c = next non-wildcard-character. Search it. */
  3799. while (1) {
  3800. if (b_count==0) return false; /* c not found */
  3801. b_count--;
  3802. if (equal_pathchar(*b_ptr++,c)) {
  3803. if (wildcard_match_ab(m_count,m_ptr,b_count,b_ptr))
  3804. return true;
  3805. }
  3806. }
  3807. }
  3808. #endif
  3809. /* UPs: matches a pathname-component ("Sample") and
  3810. a pathname-component ("Pattern") at a time. */
  3811. local bool host_match (object pattern, object sample, bool logical);
  3812. local bool device_match (object pattern, object sample, bool logical);
  3813. local bool directory_match (object pattern, object sample, bool logical);
  3814. local bool nametype_match (object pattern, object sample, bool logical);
  3815. local bool version_match (object pattern, object sample, bool logical);
  3816. local bool host_match (object pattern, object sample, bool logical)
  3817. {/* logical is ignored */
  3818. #if defined(LOGICAL_PATHNAMES) || HAS_HOST
  3819. if (nullp(pattern)) return true;
  3820. return equal(pattern,sample);
  3821. #else
  3822. return true;
  3823. #endif
  3824. }
  3825. local bool device_match (object pattern, object sample, bool logical) {
  3826. #if HAS_DEVICE
  3827. #ifdef LOGICAL_PATHNAMES
  3828. if (logical) {
  3829. return true;
  3830. }
  3831. #endif
  3832. if (nullp(pattern)) return true;
  3833. #ifdef PATHNAME_WIN32
  3834. if (eq(pattern,S(Kwild))) return true;
  3835. if (eq(sample,S(Kwild))) return false;
  3836. #endif
  3837. #ifdef PATHNAME_WIN32
  3838. return equalp(pattern,sample);
  3839. #else
  3840. return equal(pattern,sample);
  3841. #endif
  3842. #else
  3843. return true;
  3844. #endif
  3845. }
  3846. local bool nametype_match_aux (object pattern, object sample, bool logical)
  3847. { /* logical is ignored */
  3848. #if defined(LOGICAL_PATHNAMES) || defined(PATHNAME_NOEXT)
  3849. if (eq(pattern,S(Kwild))) return true;
  3850. if (eq(sample,S(Kwild))) return false;
  3851. if (nullp(pattern)) {
  3852. if (nullp(sample))
  3853. return true;
  3854. else
  3855. return false;
  3856. }
  3857. if (nullp(sample))
  3858. return false;
  3859. return wildcard_match(pattern,sample);
  3860. #else
  3861. return true; /* when do we get here?! */
  3862. #endif
  3863. }
  3864. local bool subdir_match (object pattern, object sample, bool logical)
  3865. { /* logical is ignored */
  3866. if (eq(pattern,sample)) return true;
  3867. #if defined(LOGICAL_PATHNAMES) || defined(PATHNAME_NOEXT)
  3868. if (eq(pattern,S(Kwild))) return true;
  3869. if (!simple_string_p(pattern) || !simple_string_p(sample)) return false;
  3870. return wildcard_match(pattern,sample);
  3871. #else
  3872. return true; /* when do we get here?! */
  3873. #endif
  3874. }
  3875. /* recursive implementation because of backtracking: */
  3876. local bool directory_match_ab (object m_list, object b_list, bool logical);
  3877. local bool directory_match_ab (object m_list, object b_list, bool logical) {
  3878. /* Algorithm analogous to wildcard_match_ab. */
  3879. var object item;
  3880. while (1) {
  3881. if (atomp(m_list)) { return atomp(b_list); }
  3882. item = Car(m_list); m_list = Cdr(m_list);
  3883. if (eq(item,S(Kwild_inferiors))) break;
  3884. if (atomp(b_list)) return false;
  3885. if (!subdir_match(item,Car(b_list),logical)) return false;
  3886. b_list = Cdr(b_list);
  3887. }
  3888. while (1) {
  3889. if (atomp(m_list)) return true;
  3890. item = Car(m_list); m_list = Cdr(m_list);
  3891. if (!eq(item,S(Kwild_inferiors))) break;
  3892. }
  3893. while (1) {
  3894. if (atomp(b_list)) return false;
  3895. if (subdir_match(item,Car(b_list),logical)) {
  3896. b_list = Cdr(b_list);
  3897. if (directory_match_ab(m_list,b_list,logical)) return true;
  3898. } else {
  3899. b_list = Cdr(b_list);
  3900. }
  3901. }
  3902. }
  3903. local inline bool directory_trivial_p (object dir) {
  3904. return nullp(dir)
  3905. || (consp(dir) ? (eq(Car(dir),S(Krelative)) && nullp(Cdr(dir))) : false);
  3906. }
  3907. local bool directory_match (object pattern, object sample, bool logical) {
  3908. if (nullp(pattern)) /* compare pattern with directory_default */
  3909. return true;
  3910. if (missingp(sample)) return true;
  3911. /* match startpoint: */
  3912. if (!eq(Car(pattern),Car(sample)))
  3913. return false;
  3914. pattern = Cdr(pattern); sample = Cdr(sample);
  3915. /* match subdirs: */
  3916. return directory_match_ab(pattern,sample,logical);
  3917. }
  3918. local bool nametype_match (object pattern, object sample, bool logical) {
  3919. if (missingp(pattern)) return true;
  3920. return nametype_match_aux(pattern,sample,logical);
  3921. }
  3922. local bool version_match (object pattern, object sample, bool logical)
  3923. { /* logical is ignored */
  3924. SDOUT("version_match:",pattern);
  3925. SDOUT("version_match:",sample);
  3926. if (!boundp(sample)) return true;
  3927. if (nullp(pattern) || eq(pattern,S(Kwild))) return true;
  3928. if (eq(sample,S(Kwild))) return false;
  3929. return eql(pattern,sample);
  3930. }
  3931. LISPFUNNR(pathname_match_p,2)
  3932. { /* (PATHNAME-MATCH-P pathname wildname), CLtL2 p. 623 */
  3933. /* stack layout: pathname, wildname. */
  3934. var bool logical = false;
  3935. STACK_1 = coerce_xpathname(STACK_1);
  3936. STACK_0 = coerce_xpathname(STACK_0);
  3937. #ifdef LOGICAL_PATHNAMES
  3938. if (logpathnamep(STACK_1) && logpathnamep(STACK_0)) {
  3939. logical = true;
  3940. } else {
  3941. /* not both logical pathnames -> first convert into normal pathnames: */
  3942. STACK_1 = coerce_pathname(STACK_1);
  3943. STACK_0 = coerce_pathname(STACK_0);
  3944. }
  3945. #endif
  3946. DOUT("pathname-match-p:[s0]",STACK_0);
  3947. DOUT("pathname-match-p:[s1]",STACK_1);
  3948. var object wildname = popSTACK();
  3949. var object pathname = popSTACK();
  3950. if (!host_match(xpathname_host(logical,wildname),
  3951. xpathname_host(logical,pathname),
  3952. logical))
  3953. goto no;
  3954. if (!device_match(xpathname_device(logical,wildname),
  3955. xpathname_device(logical,pathname),
  3956. logical))
  3957. goto no;
  3958. if (!directory_match(xpathname_directory(logical,wildname),
  3959. xpathname_directory(logical,pathname),
  3960. logical))
  3961. goto no;
  3962. if (!nametype_match(xpathname_name(logical,wildname),
  3963. xpathname_name(logical,pathname),
  3964. logical))
  3965. goto no;
  3966. if (!nametype_match(xpathname_type(logical,wildname),
  3967. xpathname_type(logical,pathname),
  3968. logical))
  3969. goto no;
  3970. if (!version_match(xpathname_version(logical,wildname),
  3971. xpathname_version(logical,pathname),
  3972. logical))
  3973. goto no;
  3974. yes:
  3975. VALUES1(T); return;
  3976. no:
  3977. VALUES1(NIL); return;
  3978. }
  3979. /* (TRANSLATE-PATHNAME sample pattern1 pattern2) implemented as follows:
  3980. 1. (PATHNAME-MATCH-P sample pattern1) while checking, extract
  3981. text items from the substitution pattern (:WILD -> "*").
  3982. 2. Put the text items into pattern2 until pattern2 is full or all the
  3983. text items are used up
  3984. 3. finally, (MERGE-PATHNAMES modified_pattern2 sample). */
  3985. /* UP: Compare a wildcard string ("Pattern") with "Sample".
  3986. wildcard_diff(pattern,sample,previous,solutions);
  3987. > pattern: normal simple string, with substitution characters
  3988. '?' for exactly 1 character
  3989. '*' for as many characters as desired
  3990. > sample: normal simple string, to compare with
  3991. > previous: the already known result of comparison
  3992. (reversed list of normal simple strings, NILs and lists)
  3993. > solutions: address of a list in the STACK, onto which the results of
  3994. the comparisons (reversed list of normal simple strings
  3995. and lists) have to be consed
  3996. can trigger GC */
  3997. /* Here you need not Lisp or C, but PROLOG!
  3998. (PUSH previous solutions) */
  3999. #define push_solution() do { \
  4000. var object new_cons = allocate_cons(); \
  4001. Car(new_cons) = *previous; \
  4002. Cdr(new_cons) = *solutions; \
  4003. *solutions = new_cons; \
  4004. } while(0)
  4005. /* (PUSH (CONS new_piece previous) solutions) */
  4006. #define push_solution_with(new_piece) do { \
  4007. pushSTACK(new_piece); \
  4008. {var object new_cons = allocate_cons(); \
  4009. Car(new_cons) = STACK_0; Cdr(new_cons) = *previous; \
  4010. STACK_0 = new_cons; \
  4011. new_cons = allocate_cons(); \
  4012. Car(new_cons) = popSTACK(); Cdr(new_cons) = *solutions; \
  4013. *solutions = new_cons; \
  4014. }} while(0)
  4015. #if defined(PATHNAME_NOEXT) || defined(LOGICAL_PATHNAMES)
  4016. /* recursive implementation because of backtracking: */
  4017. local maygc void wildcard_diff_ab (object pattern, object sample,
  4018. uintL m_index, uintL b_index,
  4019. const gcv_object_t* previous,
  4020. gcv_object_t* solutions) {
  4021. var chart cc;
  4022. while (1) {
  4023. if (m_index == Sstring_length(pattern)) {
  4024. if (b_index == Sstring_length(sample))
  4025. push_solution();
  4026. return;
  4027. }
  4028. cc = schar(pattern,m_index++);
  4029. if (starp(cc))
  4030. break;
  4031. if (b_index == Sstring_length(sample))
  4032. return;
  4033. if (chareq(cc,ascii('?'))) {
  4034. /* recursive call to wildcard_diff_ab(), with extended previous: */
  4035. cc = schar(sample,b_index++);
  4036. pushSTACK(pattern); pushSTACK(sample);
  4037. {
  4038. var object new_string = allocate_string(1);
  4039. TheS32string(new_string)->data[0] = as_cint(cc);
  4040. pushSTACK(new_string);
  4041. }
  4042. {
  4043. var object new_cons = allocate_cons();
  4044. Car(new_cons) = STACK_0; Cdr(new_cons) = *previous;
  4045. STACK_0 = new_cons; /* (CONS ... previous) */
  4046. }
  4047. wildcard_diff_ab(STACK_2,STACK_1,m_index,b_index,&STACK_0,solutions);
  4048. skipSTACK(3);
  4049. return;
  4050. } else {
  4051. if (!equal_pathchar(schar(sample,b_index++),cc))
  4052. return;
  4053. }
  4054. }
  4055. var uintL b_start_index = b_index;
  4056. while (1) {
  4057. /* to reduce consing, intercept cases when wildcard_diff_ab()
  4058. does nothing */
  4059. if (m_index == Sstring_length(pattern)
  4060. ? b_index == Sstring_length(sample)
  4061. : (cc = schar(pattern,m_index),
  4062. starp(cc) || chareq(cc,ascii('?'))
  4063. || (b_index < Sstring_length(sample)
  4064. && equal_pathchar(schar(sample,b_index),cc)))) {
  4065. /* wildcard_diff_ab() recursive call, with extended previous: */
  4066. pushSTACK(pattern); pushSTACK(sample);
  4067. /* (SUBSTRING sample b_start_index b_index) */
  4068. pushSTACK(subsstring(sample,b_start_index,b_index));
  4069. var object new_cons = allocate_cons();
  4070. Car(new_cons) = STACK_0; Cdr(new_cons) = *previous;
  4071. STACK_0 = new_cons; /* (CONS ... previous) */
  4072. wildcard_diff_ab(STACK_2,STACK_1,m_index,b_index,&STACK_0,solutions);
  4073. skipSTACK(1);
  4074. sample = popSTACK(); pattern = popSTACK();
  4075. }
  4076. if (b_index == Sstring_length(sample))
  4077. break;
  4078. b_index++;
  4079. }
  4080. }
  4081. local maygc void wildcard_diff (object pattern, object sample,
  4082. const gcv_object_t* previous,
  4083. gcv_object_t* solutions) {
  4084. ASSERT(sstring_normal_p(pattern));
  4085. ASSERT(sstring_normal_p(sample));
  4086. wildcard_diff_ab(pattern,sample,0,0,previous,solutions);
  4087. }
  4088. #endif
  4089. #if DEBUG_TRANSLATE_PATHNAME>1
  4090. /* all arguments to *_diff are on stack - this should be safe */
  4091. #define DEBUG_DIFF(f) \
  4092. printf("\n* " #f " [logical: %d]\n",logical); \
  4093. DOUT("",pattern); DOUT("",sample); DOUT("",*previous); DOUT("",*solutions)
  4094. #else
  4095. #define DEBUG_DIFF(f)
  4096. #endif
  4097. /* UPs: compares a pathname-component ("Sample") and
  4098. a pathname-component ("Pattern") at a time.
  4099. can trigger GC */
  4100. local maygc void host_diff (object pattern, object sample, bool logical,
  4101. const gcv_object_t* previous,
  4102. gcv_object_t* solutions);
  4103. local maygc void device_diff (object pattern, object sample, bool logical,
  4104. const gcv_object_t* previous,
  4105. gcv_object_t* solutions);
  4106. local maygc void directory_diff (object pattern, object sample, bool logical,
  4107. const gcv_object_t* previous,
  4108. gcv_object_t* solutions);
  4109. local maygc void nametype_diff (object pattern, object sample, bool logical,
  4110. const gcv_object_t* previous,
  4111. gcv_object_t* solutions);
  4112. local maygc void version_diff (object pattern, object sample, bool logical,
  4113. const gcv_object_t* previous,
  4114. gcv_object_t* solutions);
  4115. local maygc void host_diff (object pattern, object sample, bool logical,
  4116. const gcv_object_t* previous, gcv_object_t* solutions) {
  4117. DEBUG_DIFF(host_diff);
  4118. #ifdef LOGICAL_PATHNAMES
  4119. if (logical) {
  4120. if (nullp(pattern)) {
  4121. push_solution_with(sample); return;
  4122. }
  4123. if (!equal(pattern,sample)) return;
  4124. } else
  4125. #endif
  4126. {
  4127. #if HAS_HOST
  4128. if (nullp(pattern)) {
  4129. push_solution_with(sample); return;
  4130. }
  4131. if (!equal(pattern,sample)) return;
  4132. #endif
  4133. }
  4134. #if HAS_HOST
  4135. push_solution_with(S(Khost));
  4136. #else
  4137. push_solution();
  4138. #endif
  4139. }
  4140. local maygc void device_diff (object pattern, object sample, bool logical,
  4141. const gcv_object_t* previous, gcv_object_t* solutions) {
  4142. DEBUG_DIFF(device_diff);
  4143. #ifdef LOGICAL_PATHNAMES
  4144. if (logical) {
  4145. #if HAS_DEVICE
  4146. push_solution_with(S(Kdevice));
  4147. #else
  4148. push_solution();
  4149. #endif
  4150. return;
  4151. }
  4152. #endif
  4153. #if HAS_DEVICE
  4154. #ifdef PATHNAME_WIN32
  4155. if (nullp(pattern) || eq(pattern,S(Kwild))) {
  4156. var object string = wild2string(sample);
  4157. push_solution_with(string);
  4158. return;
  4159. }
  4160. if (eq(sample,S(Kwild))) return;
  4161. #endif
  4162. #ifdef PATHNAME_WIN32
  4163. if (nullp(pattern)) {
  4164. var object string = wild2string(sample);
  4165. push_solution_with(string);
  4166. return;
  4167. }
  4168. if (!equalp(pattern,sample)) return;
  4169. #else
  4170. if (!equal(pattern,sample)) return;
  4171. #endif
  4172. push_solution_with(S(Kdevice));
  4173. #else /* HAS_DEVICE */
  4174. push_solution();
  4175. #endif
  4176. }
  4177. local maygc void nametype_diff_aux (object pattern, object sample, bool logical,
  4178. const gcv_object_t* previous,
  4179. gcv_object_t* solutions) {
  4180. #if defined(LOGICAL_PATHNAMES) || defined(PATHNAME_NOEXT)
  4181. unused(logical);
  4182. if (eq(pattern,S(Kwild))) {
  4183. var object string = wild2string(sample);
  4184. push_solution_with(string);
  4185. return;
  4186. }
  4187. if (eq(sample,S(Kwild))) return;
  4188. if (nullp(pattern)) {
  4189. if (nullp(sample))
  4190. push_solution();
  4191. return;
  4192. }
  4193. if (nullp(sample))
  4194. return;
  4195. wildcard_diff(pattern,sample,previous,solutions);
  4196. #endif
  4197. }
  4198. local maygc void subdir_diff (object pattern, object sample, bool logical,
  4199. const gcv_object_t* previous, gcv_object_t* solutions)
  4200. {
  4201. DEBUG_DIFF(subdir_diff);
  4202. if (eq(pattern,sample)) {
  4203. if (eq(sample,S(Kwild)))
  4204. push_solution_with(O(wild_string));
  4205. else
  4206. push_solution();
  4207. return;
  4208. }
  4209. #if defined(LOGICAL_PATHNAMES) || defined(PATHNAME_NOEXT)
  4210. unused(logical);
  4211. if (eq(pattern,S(Kwild))) {
  4212. var object string = wild2string(sample);
  4213. push_solution_with(string);
  4214. return;
  4215. }
  4216. if (eq(sample,S(Kwild))) return;
  4217. if (!simple_string_p(pattern) || !simple_string_p(sample)) return;
  4218. wildcard_diff(pattern,sample,previous,solutions);
  4219. #endif
  4220. }
  4221. /* recursive implementation because of backtracking: */
  4222. local maygc void directory_diff_ab (object m_list, object b_list, bool logical,
  4223. const gcv_object_t* previous,
  4224. gcv_object_t* solutions) {
  4225. /* algorithm analogous to wildcard_diff_ab. */
  4226. var object item;
  4227. if (atomp(m_list)) {
  4228. if (atomp(b_list))
  4229. push_solution();
  4230. return;
  4231. }
  4232. item = Car(m_list); m_list = Cdr(m_list);
  4233. if (!eq(item,S(Kwild_inferiors))) {
  4234. if (atomp(b_list)) return;
  4235. pushSTACK(NIL); pushSTACK(m_list); pushSTACK(Cdr(b_list));
  4236. subdir_diff(item,Car(b_list),logical,previous,&STACK_2);
  4237. /* call directory_diff_ab() recursively, with extended previous: */
  4238. while (mconsp(STACK_2)) {
  4239. pushSTACK(Car(STACK_2));
  4240. directory_diff_ab(STACK_(1+1),STACK_(0+1),logical,&STACK_0,solutions);
  4241. skipSTACK(1);
  4242. STACK_2 = Cdr(STACK_2);
  4243. }
  4244. skipSTACK(3);
  4245. } else {
  4246. pushSTACK(b_list); /* b_start_list := b_list */
  4247. while (1) {
  4248. /* to reduce consing, intercept cases when directory_diff_ab()
  4249. does nothing: */
  4250. if (atomp(m_list)
  4251. ? atomp(b_list)
  4252. : (eq(Car(m_list),S(Kwild_inferiors)) || !atomp(b_list))) {
  4253. /* call directory_diff_ab() recursively, with extended previous: */
  4254. pushSTACK(m_list); pushSTACK(b_list);
  4255. pushSTACK(STACK_2); pushSTACK(b_list);
  4256. funcall(L(ldiff),2); /* (LDIFF b_start_list b_list) */
  4257. pushSTACK(value1);
  4258. { /* (:DIRECTORY subdir1 ... subdirn) */
  4259. var object new_piece = allocate_cons();
  4260. Car(new_piece) = S(Kdirectory); Cdr(new_piece) = STACK_0;
  4261. STACK_0 = new_piece;
  4262. }
  4263. {
  4264. var object new_cons = allocate_cons();
  4265. Car(new_cons) = STACK_0; Cdr(new_cons) = *previous;
  4266. STACK_0 = new_cons; /* (CONS ... previous) */
  4267. directory_diff_ab(STACK_2,STACK_1,logical,&STACK_0,solutions);
  4268. skipSTACK(1);
  4269. b_list = popSTACK(); m_list = popSTACK();
  4270. }
  4271. }
  4272. if (atomp(b_list))
  4273. break;
  4274. b_list = Cdr(b_list);
  4275. }
  4276. skipSTACK(1);
  4277. }
  4278. }
  4279. local maygc void directory_diff (object pattern, object sample, bool logical,
  4280. const gcv_object_t* previous,
  4281. gcv_object_t* solutions) {
  4282. DEBUG_DIFF(directory_diff);
  4283. if (missingp(sample)) { push_solution_with(pattern); return; }
  4284. if (directory_trivial_p(pattern)) { /* compare with directory_default */
  4285. /* Augment the solution with the sample list - starting
  4286. with :ABSOLUTE or :RELATIVE, it will not fit for "**". */
  4287. push_solution_with(sample);
  4288. return;
  4289. }
  4290. /* compare startpoint: */
  4291. if (!eq(Car(pattern),Car(sample)))
  4292. return;
  4293. pattern = Cdr(pattern); sample = Cdr(sample);
  4294. /* compare subdirs: */
  4295. directory_diff_ab(pattern,sample,logical,previous,solutions);
  4296. }
  4297. local maygc void nametype_diff (object pattern, object sample, bool logical,
  4298. const gcv_object_t* previous,
  4299. gcv_object_t* solutions) {
  4300. DEBUG_DIFF(nametype_diff);
  4301. if (!boundp(sample)) { push_solution_with(pattern); return; }
  4302. if (nullp(pattern)) {
  4303. var object string = wild2string(sample);
  4304. push_solution_with(string);
  4305. return;
  4306. }
  4307. nametype_diff_aux(pattern,sample,logical,previous,solutions);
  4308. }
  4309. local maygc void version_diff (object pattern, object sample, bool logical,
  4310. const gcv_object_t* previous, gcv_object_t* solutions)
  4311. { /* logical is ignored */
  4312. DEBUG_DIFF(version_diff);
  4313. if (!boundp(sample)) { push_solution_with(pattern); return; }
  4314. if (nullp(pattern) || eq(pattern,S(Kwild))) {
  4315. push_solution_with(sample);
  4316. return;
  4317. }
  4318. if (eq(sample,S(Kwild))) return;
  4319. if (!eql(pattern,sample)) return;
  4320. push_solution();
  4321. }
  4322. #undef push_solution_with
  4323. #undef push_solution
  4324. #undef DEBUG_DIFF
  4325. /* Each substitution is a list of Normal-Simple-Strings or Lists.
  4326. (The Lists come into being with :WILD-INFERIORS in directory_diff().)
  4327. A Normal-Simple-String fits only with '?' or '*' or :WILD,
  4328. A List fits only with :WILD-INFERIORS. */
  4329. #ifdef LOGICAL_PATHNAMES
  4330. /* On insertion of pieces of normal pathnames in logical pathnames:
  4331. Conversion to capital letters.
  4332. logical_case(string)
  4333. > string: Normal-Simple-String or Symbol/Number
  4334. < result: converted Normal-Simple-String or the same Symbol/Number
  4335. can trigger GC */
  4336. local maygc object logical_case (object string) {
  4337. if (!simple_string_p(string))
  4338. return string;
  4339. return string_upcase(string);
  4340. }
  4341. /* The same, recursive like with SUBST: */
  4342. local maygc object subst_logical_case (object obj) {
  4343. SUBST_RECURSE(logical_case(obj),subst_logical_case);
  4344. }
  4345. /* On insertion of pieces of logical pathnames in normal pathnames:
  4346. Conversion to capital letters.
  4347. customary_case(string)
  4348. > string: Normal-Simple-String or Symbol/Number
  4349. < result: converted Normal-Simple-String or the same Symbol/Number
  4350. can trigger GC */
  4351. local maygc object customary_case (object string) {
  4352. if (!simple_string_p(string))
  4353. return string;
  4354. #if defined(PATHNAME_UNIX) || defined(PATHNAME_WIN32)
  4355. /* operating system with preference for lowercase letters */
  4356. return string_downcase(string);
  4357. #endif
  4358. }
  4359. /* The same, recursive like with SUBST: */
  4360. local maygc object subst_customary_case (object obj) {
  4361. SUBST_RECURSE(customary_case(obj),subst_customary_case);
  4362. }
  4363. #endif
  4364. #undef SUBST_RECURSE
  4365. /* Apply substitution SUBST to the PATTERN.
  4366. translate_pathname(&subst,pattern) */
  4367. local object translate_pathname (object* subst, object pattern);
  4368. /* Pop the CAR of *subst and return it. */
  4369. #define RET_POP(subst) \
  4370. { var object ret = Car(*subst); *subst = Cdr(*subst); return ret; }
  4371. /* is the value trivial enough to ensure a trivial action? */
  4372. #define TRIVIAL_P(val) (simple_string_p(val)||nullp(val))
  4373. /* is the value simple enough to ensure a simple action? */
  4374. #define SIMPLE_P(val) (TRIVIAL_P(val)||eq(val,S(Kwild)))
  4375. /* translate_host(&subst,pattern,logical) etc.
  4376. returns the appropriate replacement for host etc.; shortens subst;
  4377. returns nullobj on failure
  4378. can trigger GC */
  4379. local maygc object translate_host (gcv_object_t* subst, object pattern,
  4380. bool logical);
  4381. local maygc object translate_device (gcv_object_t* subst, object pattern,
  4382. bool logical);
  4383. local maygc object translate_subdir (gcv_object_t* subst, object pattern,
  4384. bool logical);
  4385. local maygc object translate_directory (gcv_object_t* subst, object pattern,
  4386. bool logical);
  4387. local maygc object translate_nametype (gcv_object_t* subst, object pattern,
  4388. bool logical);
  4389. local maygc object translate_version (gcv_object_t* subst, object pattern,
  4390. bool logical);
  4391. #if DEBUG_TRANSLATE_PATHNAME
  4392. /* all arguments to translate_* should be on stack - this should be safe */
  4393. #define DEBUG_TRAN(f) \
  4394. printf("\n* " #f " [logical: %d]\n",logical); \
  4395. DOUT("",*subst); DOUT("",pattern)
  4396. #else
  4397. #define DEBUG_TRAN(f)
  4398. #endif
  4399. local maygc object translate_host (gcv_object_t* subst, object pattern,
  4400. bool logical) {
  4401. DEBUG_TRAN(translate_host);
  4402. #define TRAN_HOST(subst,pattern) \
  4403. if (nullp(pattern) && mconsp(*subst)) { \
  4404. if (TRIVIAL_P(Car(*subst))) { \
  4405. RET_POP(subst); \
  4406. } else if (eq(Car(*subst),S(Khost))) { \
  4407. *subst = Cdr(*subst); \
  4408. return pattern; \
  4409. } else \
  4410. return nullobj; \
  4411. }
  4412. #ifdef LOGICAL_PATHNAMES
  4413. if (logical) {
  4414. TRAN_HOST(subst,pattern);
  4415. } else
  4416. #endif
  4417. {
  4418. #if HAS_HOST
  4419. TRAN_HOST(subst,pattern);
  4420. #endif
  4421. }
  4422. #if HAS_HOST
  4423. if (eq(Car(*subst),S(Khost)))
  4424. *subst = Cdr(*subst);
  4425. #endif
  4426. return pattern;
  4427. #undef TRAN_HOST
  4428. }
  4429. local maygc object translate_device (gcv_object_t* subst, object pattern,
  4430. bool logical) {
  4431. DEBUG_TRAN(translate_device);
  4432. #if HAS_DEVICE
  4433. #ifdef LOGICAL_PATHNAMES
  4434. if (logical) {
  4435. if (eq(Car(*subst),S(Kdevice)))
  4436. { *subst = Cdr(*subst); }
  4437. return pattern;
  4438. }
  4439. #endif
  4440. #ifdef PATHNAME_WIN32
  4441. if (nullp(pattern) && mconsp(*subst))
  4442. #else
  4443. if ((nullp(pattern) || eq(pattern,S(Kwild))) && mconsp(*subst))
  4444. #endif
  4445. {
  4446. if (TRIVIAL_P(Car(*subst))) {
  4447. RET_POP(subst);
  4448. } else if (eq(Car(*subst),S(Kdevice))) {
  4449. *subst = Cdr(*subst);
  4450. return pattern;
  4451. } else
  4452. return nullobj;
  4453. }
  4454. if (eq(Car(*subst),S(Kdevice)))
  4455. *subst = Cdr(*subst);
  4456. #endif
  4457. return pattern;
  4458. }
  4459. local maygc object translate_nametype_aux (gcv_object_t* subst, object pattern,
  4460. bool logical) {
  4461. DEBUG_TRAN(translate_nametype_aux);
  4462. if (eq(pattern,S(Kwild)) && mconsp(*subst)) {
  4463. if (TRIVIAL_P(Car(*subst))) {
  4464. var object erg = Car(*subst); *subst = Cdr(*subst);
  4465. return erg;
  4466. } else
  4467. return nullobj;
  4468. }
  4469. if (simple_string_p(pattern)) {
  4470. pushSTACK(pattern); /* save pattern */
  4471. var gcv_object_t* pattern_ = &STACK_0;
  4472. var uintL len = Sstring_length(pattern);
  4473. var uintL index = 0;
  4474. var uintL stringcount = 0; /* number of strings on the stack */
  4475. while (1) {
  4476. var uintL last_index = index;
  4477. var chart cc;
  4478. /* search next wildcard-character: */
  4479. pattern = *pattern_;
  4480. while (index != len) {
  4481. cc = schar(pattern,index);
  4482. if ((starp(cc) /* wildcard for arbitrary many characters */
  4483. || (!logical && singlewild_char_p(cc))) /* wildcard for exactly one character */
  4484. && mconsp(*subst))
  4485. break;
  4486. index++;
  4487. }
  4488. /* Next (SUBSTRING pattern last_index index) on the stack: */
  4489. pushSTACK(subsstring(pattern,last_index,index));
  4490. stringcount++;
  4491. /* finished? */
  4492. if (index == len)
  4493. break;
  4494. /* replace wildcard: */
  4495. if (TRIVIAL_P(Car(*subst))) {
  4496. var object s = Car(*subst);
  4497. pushSTACK(nullp(s) ? (object)O(empty_string) : s);
  4498. *subst = Cdr(*subst); stringcount++;
  4499. } else {
  4500. skipSTACK(stringcount+1); return nullobj;
  4501. }
  4502. index++;
  4503. }
  4504. value1 = string_concat(stringcount);
  4505. skipSTACK(1); /* skip pattern */
  4506. return value1;
  4507. }
  4508. return pattern;
  4509. }
  4510. local maygc object translate_subdir (gcv_object_t* subst, object pattern,
  4511. bool logical) {
  4512. DEBUG_TRAN(translate_subdir);
  4513. #if defined(LOGICAL_PATHNAMES) || defined(PATHNAME_NOEXT)
  4514. return translate_nametype_aux(subst,pattern,logical);
  4515. #endif
  4516. }
  4517. local maygc object translate_directory (gcv_object_t* subst, object pattern,
  4518. bool logical) {
  4519. DEBUG_TRAN(translate_directory);
  4520. /* compare pattern with directory_default: */
  4521. if (nullp(pattern) && mconsp(*subst)) {
  4522. var object list = Car(*subst); *subst = Cdr(*subst);
  4523. return listp(list) ? copy_list(list) : nullobj;
  4524. }
  4525. /* if subst is :relative while pattern is :absolute,
  4526. nothing is to be done */
  4527. if (eq(Car(pattern),S(Kabsolute)) && mconsp(*subst)
  4528. && directory_trivial_p(Car(*subst))) {
  4529. *subst = Cdr(*subst);
  4530. return copy_list(pattern);
  4531. }
  4532. var uintL itemcount = 0; /* number of items on the stack */
  4533. /* Startpoint: */
  4534. pushSTACK(Car(pattern)); pattern = Cdr(pattern); itemcount++;
  4535. /* subdirs: */
  4536. while (consp(pattern)) {
  4537. var object item = Car(pattern);
  4538. pattern = Cdr(pattern);
  4539. if (eq(item,S(Kwild_inferiors))) {
  4540. if (mconsp(*subst)) {
  4541. if (consp(Car(*subst)) && eq(Car(Car(*subst)),S(Kdirectory))) {
  4542. var object list = Cdr(Car(*subst)); *subst = Cdr(*subst);
  4543. while (consp(list)) {
  4544. pushSTACK(Car(list)); list = Cdr(list); itemcount++;
  4545. }
  4546. } else {
  4547. skipSTACK(itemcount); return nullobj;
  4548. }
  4549. } else {
  4550. pushSTACK(item); itemcount++;
  4551. }
  4552. } else {
  4553. pushSTACK(pattern); /* save pattern */
  4554. item = translate_subdir(subst,item,logical);
  4555. if (eq(item,nullobj)) { skipSTACK(itemcount+1); return nullobj; }
  4556. pattern = STACK_0; STACK_0 = item; itemcount++;
  4557. }
  4558. }
  4559. return listof(itemcount);
  4560. }
  4561. local maygc object translate_nametype (gcv_object_t* subst, object pattern,
  4562. bool logical) {
  4563. DEBUG_TRAN(translate_nametype);
  4564. if (nullp(pattern) && mconsp(*subst)) {
  4565. if (SIMPLE_P(Car(*subst))) {
  4566. RET_POP(subst);
  4567. } else
  4568. return nullobj;
  4569. }
  4570. return translate_nametype_aux(subst,pattern,logical);
  4571. }
  4572. local object translate_version (gcv_object_t* subst, object pattern,
  4573. bool logical)
  4574. { /* logical is ignored */
  4575. DEBUG_TRAN(translate_version);
  4576. if ((nullp(pattern) || eq(pattern,S(Kwild))) && mconsp(*subst)) {
  4577. var object erg = Car(*subst);
  4578. if (nullp(erg) || integerp(erg)
  4579. || eq(erg,S(Kwild)) || eq(erg,S(Knewest))) {
  4580. *subst = Cdr(*subst);
  4581. return erg;
  4582. } else
  4583. return nullobj;
  4584. }
  4585. return pattern;
  4586. }
  4587. #undef SIMPLE_P
  4588. #undef TRIVIAL_P
  4589. #undef RET_POP
  4590. #undef DEBUG_TRAN
  4591. local maygc object translate_pathname (gcv_object_t* subst, object pattern) {
  4592. var bool logical = false;
  4593. var object item;
  4594. pushSTACK(*subst); /* save subst for the error message */
  4595. pushSTACK(pattern);
  4596. #ifdef LOGICAL_PATHNAMES
  4597. if (logpathnamep(pattern))
  4598. logical = true;
  4599. #endif
  4600. #define GET_ITEM(what,xwhat,where,skip) do { \
  4601. item = translate_##what(subst,xpathname_##xwhat(logical,where),logical); \
  4602. if (eq(item,nullobj)) { skipSTACK(skip); goto subst_error; } \
  4603. DOUT(#what " > ",item); pushSTACK(S(K##xwhat)); pushSTACK(item); \
  4604. } while(0)
  4605. #define GET_ITEM_S(y,x,w) GET_ITEM(y,x,STACK_(w),w)
  4606. /* build together arguments for MAKE-PATHNAME: */
  4607. GET_ITEM(host,host,pattern,0);
  4608. #if HAS_DEVICE
  4609. GET_ITEM_S(device,device,2);
  4610. #endif
  4611. GET_ITEM_S(directory,directory,2+2*HAS_DEVICE);
  4612. GET_ITEM_S(nametype,name,2+2*HAS_DEVICE+2);
  4613. GET_ITEM_S(nametype,type,2+2*HAS_DEVICE+4);
  4614. GET_ITEM_S(version,version,2+2*HAS_DEVICE+6);
  4615. /* All replacement pieces must be consumed! */
  4616. if (mconsp(*subst)) { skipSTACK(2+2*HAS_DEVICE+8); goto subst_error; }
  4617. /* call (MAKE-PATHNAME ...) resp. (SYS::MAKE-LOGICAL-PATHNAME ...) : */
  4618. #ifdef LOGICAL_PATHNAMES
  4619. if (logical)
  4620. funcall(L(make_logical_pathname),2+2*HAS_DEVICE+8);
  4621. else
  4622. #endif
  4623. funcall(L(make_pathname),2+2*HAS_DEVICE+8);
  4624. skipSTACK(2);
  4625. return value1;
  4626. subst_error: /* Error because of nullobj. */
  4627. /* stack layout: subst, pattern. */
  4628. pushSTACK(STACK_1);
  4629. pushSTACK(S(translate_pathname));
  4630. error(error_condition,GETTEXT("~S: replacement pieces ~S do not fit into ~S"));
  4631. }
  4632. #undef GET_ITEM
  4633. #undef GET_ITEM_S
  4634. /* (TRANSLATE-PATHNAME sample pattern1 pattern2 [:all] [:merge] [:absolute]),
  4635. CLtL2 p. 624
  4636. :absolute = T --> convert the resulting pathnames to absolute
  4637. :all = T --> return a list of all fitting pathnames
  4638. :all = NIL --> Error, if more than one pathname fits
  4639. :merge = NIL --> skip last MERGE-PATHNAMES step */
  4640. LISPFUN(translate_pathname,seclass_default,3,0,norest,key,3,
  4641. (kw(all),kw(merge),kw(absolute)))
  4642. { /* stack layout: sample, pattern1, pattern2, all, merge, absolute. */
  4643. var bool absolute_p = !missingp(STACK_0);
  4644. var bool logical = false; /* sample and pattern are logical pathnames */
  4645. var bool logical2 = false; /* pattern2 is a logical pathname */
  4646. skipSTACK(1); /* drop absolute */
  4647. STACK_4 = coerce_xpathname(STACK_4);
  4648. STACK_3 = coerce_xpathname(STACK_3);
  4649. STACK_2 = coerce_xpathname(STACK_2);
  4650. #ifdef LOGICAL_PATHNAMES
  4651. if (logpathnamep(STACK_4) && logpathnamep(STACK_3)) {
  4652. logical = true;
  4653. } else {
  4654. /* not both logical pathnames -> first convert into normal pathnames: */
  4655. STACK_4 = coerce_pathname(STACK_4);
  4656. STACK_3 = coerce_pathname(STACK_3);
  4657. }
  4658. if (logpathnamep(STACK_2))
  4659. logical2 = true;
  4660. #endif
  4661. /* 1. step: construct list of all fitting substitutions. */
  4662. pushSTACK(NIL); pushSTACK(NIL);
  4663. host_diff(xpathname_host(logical,STACK_(3+2)),
  4664. xpathname_host(logical,STACK_(4+2)),
  4665. logical,&STACK_1,&STACK_0);
  4666. while (mconsp(STACK_0)) {
  4667. pushSTACK(Car(STACK_0)); pushSTACK(NIL);
  4668. device_diff(xpathname_device(logical,STACK_(3+4)),
  4669. xpathname_device(logical,STACK_(4+4)),
  4670. logical,&STACK_1,&STACK_0);
  4671. while (mconsp(STACK_0)) {
  4672. pushSTACK(Car(STACK_0)); pushSTACK(NIL);
  4673. directory_diff(xpathname_directory(logical,STACK_(3+6)),
  4674. xpathname_directory(logical,STACK_(4+6)),
  4675. logical,&STACK_1,&STACK_0);
  4676. while (mconsp(STACK_0)) {
  4677. pushSTACK(Car(STACK_0)); pushSTACK(NIL);
  4678. nametype_diff(xpathname_name(logical,STACK_(3+8)),
  4679. xpathname_name(logical,STACK_(4+8)),
  4680. logical,&STACK_1,&STACK_0);
  4681. while (mconsp(STACK_0)) {
  4682. pushSTACK(Car(STACK_0)); pushSTACK(NIL);
  4683. nametype_diff(xpathname_type(logical,STACK_(3+10)),
  4684. xpathname_type(logical,STACK_(4+10)),
  4685. logical,&STACK_1,&STACK_0);
  4686. while (mconsp(STACK_0)) {
  4687. pushSTACK(Car(STACK_0));
  4688. version_diff(xpathname_version(logical,STACK_(3+11)),
  4689. xpathname_version(logical,STACK_(4+11)),
  4690. logical,&STACK_0,&STACK_10);
  4691. skipSTACK(1);
  4692. STACK_0 = Cdr(STACK_0);
  4693. }
  4694. skipSTACK(2);
  4695. STACK_0 = Cdr(STACK_0);
  4696. }
  4697. skipSTACK(2);
  4698. STACK_0 = Cdr(STACK_0);
  4699. }
  4700. skipSTACK(2);
  4701. STACK_0 = Cdr(STACK_0);
  4702. }
  4703. skipSTACK(2);
  4704. STACK_0 = Cdr(STACK_0);
  4705. }
  4706. skipSTACK(1);
  4707. /* stack layout: ..., solutions. */
  4708. if (matomp(STACK_0)) {
  4709. pushSTACK(STACK_(3+1));
  4710. pushSTACK(STACK_(4+1+1));
  4711. pushSTACK(S(translate_pathname));
  4712. error(error_condition,GETTEXT("~S: ~S is not a specialization of ~S"));
  4713. }
  4714. /* 2.,3. step: */
  4715. pushSTACK(NIL); /* pathnames := '() */
  4716. while (mconsp(STACK_1)) { /* traverse solutions */
  4717. var object solutions = STACK_1;
  4718. STACK_1 = Cdr(solutions);
  4719. { /* reverse list solution */
  4720. var object solution = reverse(Car(solutions));
  4721. /* 2. step: insert substitution in pattern2. */
  4722. #ifdef LOGICAL_PATHNAMES
  4723. /* convert capital-/small letters suitably: */
  4724. if (!logical) {
  4725. if (logical2)
  4726. solution = subst_logical_case(solution);
  4727. } else {
  4728. if (!logical2)
  4729. solution = subst_customary_case(solution);
  4730. }
  4731. #endif
  4732. pushSTACK(solution);
  4733. STACK_0 = translate_pathname(&STACK_0,STACK_(2+1+2));
  4734. }
  4735. /* 3. step: (MERGE-PATHNAMES modified_pattern2 sample :WILD T) */
  4736. if (!nullp(STACK_(0+1+2)) /* query :MERGE-Argument */
  4737. && has_some_wildcards(STACK_0)) {/*MERGE-PATHNAMES may be unnecessary*/
  4738. pushSTACK(STACK_(4+1+2)); pushSTACK(unbound);
  4739. pushSTACK(S(Kwild)); pushSTACK(T);
  4740. funcall(L(merge_pathnames),5);
  4741. pushSTACK(value1);
  4742. }
  4743. /* step 4: merge in default pathname */
  4744. #if defined(PATHNAME_UNIX) || defined(PATHNAME_WIN32)
  4745. if (absolute_p) {
  4746. STACK_0 = use_default_dir(STACK_0); /* insert default-directory */
  4747. /* (because Unix does not know the default-directory of LISP
  4748. and Win32 is multitasking) */
  4749. }
  4750. #endif
  4751. { /* (PUSH pathname pathnames) */
  4752. var object new_cons = allocate_cons();
  4753. Car(new_cons) = popSTACK(); Cdr(new_cons) = STACK_0;
  4754. STACK_0 = new_cons;
  4755. }
  4756. }
  4757. /* 4. step: (DELETE-DUPLICATES pathnames :TEST #'EQUAL) */
  4758. pushSTACK(S(Ktest)); pushSTACK(L(equal));
  4759. funcall(L(delete_duplicates),3);
  4760. /* stack layout: ..., nil. */
  4761. if (missingp(STACK_(1+1))) { /* query :ALL-Argument */
  4762. if (mconsp(Cdr(value1))) {
  4763. pushSTACK(value1);
  4764. pushSTACK(STACK_(2+2));
  4765. pushSTACK(STACK_(3+3));
  4766. pushSTACK(STACK_(4+4));
  4767. pushSTACK(S(translate_pathname));
  4768. error(error_condition,GETTEXT("(~S ~S ~S ~S) is ambiguous: ~S"));
  4769. }
  4770. value1 = Car(value1);
  4771. }
  4772. mv_count=1;
  4773. skipSTACK(5+1);
  4774. }
  4775. /* (ABSOLUTE-PATHNAME pathname) converts pathname to a physical pathname, if
  4776. necessary, and makes it absolute (using clisp's notion of default
  4777. directory). */
  4778. LISPFUNN(absolute_pathname,1)
  4779. {
  4780. var object thing = popSTACK();
  4781. var object pathname = coerce_pathname(thing);
  4782. pathname = use_default_dir(pathname); /* insert default-directory */
  4783. VALUES1(pathname);
  4784. }
  4785. /* Converts an object into an absolute physical pathname and returns its
  4786. namestring.
  4787. physical_namestring(thing)
  4788. > thing: an object
  4789. < result: the namestring of the pathname denoted by thing
  4790. can trigger GC */
  4791. global maygc object physical_namestring (object thing) {
  4792. var object pathname = coerce_pathname(thing);
  4793. pathname = use_default_dir(pathname); /* insert default-directory */
  4794. return whole_namestring(pathname);
  4795. }
  4796. /* UP: tests, if the name of a pathname is =NIL.
  4797. namenullp(pathname)
  4798. > pathname: non-logical pathname
  4799. local bool namenullp (object pathname);
  4800. local bool namenullp(pathname)
  4801. { return nullp(ThePathname(pathname)->pathname_name); } */
  4802. #define namenullp(path) (nullp(ThePathname(path)->pathname_name))
  4803. /* error, if directory does not exist
  4804. > obj: pathname or (better) erroneous component */
  4805. nonreturning_function(local, error_dir_not_exists, (object obj)) {
  4806. pushSTACK(obj); /* FILE-ERROR slot PATHNAME */
  4807. pushSTACK(obj);
  4808. error(file_error,GETTEXT("nonexistent directory: ~S"));
  4809. }
  4810. /* error, if a file already exits
  4811. > STACK_0: pathname */
  4812. nonreturning_function(local, error_file_exists, (void)) {
  4813. /* STACK_0 = FILE-ERROR slot PATHNAME */
  4814. pushSTACK(STACK_0); /* pathname */
  4815. pushSTACK(TheSubr(subr_self)->name);
  4816. error(file_error,GETTEXT("~S: file ~S already exists"));
  4817. }
  4818. #ifdef LOGICAL_PATHNAMES
  4819. /* An "absolute pathname" is always a non-logical pathname, poss.
  4820. with further restrictions. */
  4821. #endif
  4822. #ifdef PATHNAME_WIN32
  4823. /* An "absolute pathname" is a pathname, whose device is a checked
  4824. String and directory does not contain :RELATIVE, :CURRENT, :PARENT. */
  4825. /* UP: returns a namestring of a pathname for the operating system.
  4826. OSnamestring(dir_namestring)
  4827. > STACK_0: non-logical pathname
  4828. > dir_namestring: directory-namestring (for DOS)
  4829. < result: namestring (for DOS)
  4830. can trigger GC */
  4831. local maygc object OSnamestring (object dir_namestring) {
  4832. var uintC stringcount;
  4833. pushSTACK(dir_namestring); /* Directory-Namestring as the first String */
  4834. stringcount = file_namestring_parts(STACK_(0+1)); /* filename Strings */
  4835. return string_concat(1+stringcount); /* concatenate */
  4836. }
  4837. /* UP: tests, if a drive exists.
  4838. > uintB drive: drive-(capital-)letter
  4839. < bool result: if this drive exists and is responsive */
  4840. local bool good_drive (uintB drive);
  4841. #ifdef WIN32_NATIVE
  4842. local bool good_drive (uintB drive) {
  4843. var char rootpath[4];
  4844. var DWORD result;
  4845. rootpath[0] = drive;
  4846. rootpath[1] = ':';
  4847. rootpath[2] = '\\';
  4848. rootpath[3] = '\0';
  4849. begin_system_call();
  4850. result = GetDriveType(rootpath);
  4851. switch (result) {
  4852. case DRIVE_UNKNOWN:
  4853. end_system_call();
  4854. return false;
  4855. case DRIVE_NO_ROOT_DIR:
  4856. /* Distinguish NFS mounts from nonassigned drive letters: */
  4857. result = GetFileAttributes(rootpath);
  4858. end_system_call();
  4859. return !(result==0xFFFFFFFF);
  4860. default:
  4861. end_system_call();
  4862. return true;
  4863. }
  4864. }
  4865. #if 0
  4866. /* The following fails to recognize some (but not all) NFS mounts on WinNT. */
  4867. local bool good_drive_notsogood (uintB drive) {
  4868. var DWORD drives_bitmask;
  4869. begin_system_call();
  4870. drives_bitmask = GetLogicalDrives();
  4871. end_system_call();
  4872. return ((drives_bitmask & ((DWORD)1 << (drive-'A'))) != 0);
  4873. }
  4874. #endif
  4875. #endif /* WIN32_NATIVE */
  4876. /* UP: returns the current drive.
  4877. < char drive: drive-(capital-)letter */
  4878. local char default_drive (void) {
  4879. #ifdef WIN32_NATIVE
  4880. var DWORD path_buflen = _MAX_PATH;
  4881. var char* path_buffer = (char*)alloca(path_buflen);
  4882. var DWORD result;
  4883. begin_system_call();
  4884. result = GetCurrentDirectory(path_buflen,path_buffer);
  4885. if (!result) { OS_error(); }
  4886. if (result >= path_buflen) {
  4887. path_buflen = result; path_buffer = (char*)alloca(path_buflen);
  4888. result = GetCurrentDirectory(path_buflen,path_buffer);
  4889. if (!result) { OS_error(); }
  4890. }
  4891. end_system_call();
  4892. if (path_buffer[1]==':') { /* local device */
  4893. ASSERT(path_buffer[2]=='\\');
  4894. return as_cint(up_case(as_chart(path_buffer[0])));
  4895. } else if (path_buffer[0]=='\\') { /* network host */
  4896. ASSERT(path_buffer[1]=='\\');
  4897. return 0;
  4898. } else NOTREACHED;
  4899. #endif
  4900. }
  4901. /* UP: returns the current directory on the given drive.
  4902. > uintB drive: drive-(capital-)letter
  4903. > object pathname: pathname (for error-reporting purposes)
  4904. < result: current directory (as pathname)
  4905. can trigger GC */
  4906. local maygc object default_directory_of (uintB drive, object pathname) {
  4907. /* working directory (of DOS) is the current directory: */
  4908. #if defined(WIN32_NATIVE)
  4909. var char currpath[4];
  4910. var DWORD path_buflen = _MAX_PATH;
  4911. var char* path_buffer = (char*)alloca(path_buflen+1);
  4912. var char* dummy;
  4913. var DWORD result;
  4914. if (drive) { /* local disk */
  4915. currpath[0] = drive;
  4916. currpath[1] = ':';
  4917. currpath[2] = '.'; /* this dot is actually not needed */
  4918. currpath[3] = '\0';
  4919. begin_system_call();
  4920. result = GetFullPathName(currpath,path_buflen,path_buffer,&dummy);
  4921. if (!result) { end_system_call(); OS_file_error(pathname); }
  4922. if (result >= path_buflen) {
  4923. path_buflen = result; path_buffer = (char*)alloca(path_buflen+1);
  4924. result = GetFullPathName(currpath,path_buflen,path_buffer,&dummy);
  4925. if (!result) { end_system_call(); OS_file_error(pathname); }
  4926. }
  4927. end_system_call();
  4928. } else { /* network path */
  4929. begin_system_call();
  4930. result = GetCurrentDirectory(path_buflen,path_buffer);
  4931. if (!result) { end_system_call(); OS_file_error(pathname); }
  4932. if (result >= path_buflen) {
  4933. path_buflen = result; path_buffer = (char*)alloca(path_buflen);
  4934. result = GetCurrentDirectory(path_buflen,path_buffer);
  4935. if (!result) { OS_file_error(pathname); }
  4936. }
  4937. end_system_call();
  4938. }
  4939. { /* poss. add a '\' at the end: */
  4940. var char* path_end = &path_buffer[asciz_length(path_buffer)];
  4941. if (!(path_end[-1]=='\\')) { path_end[0] = '\\'; path_end[1] = '\0'; }
  4942. }
  4943. #else
  4944. var char path_buffer[3+MAXPATHLEN]; /* cf. GETWD(3) */
  4945. path_buffer[0] = drive; path_buffer[1] = ':';
  4946. /* file working directory in path_buffer: */
  4947. begin_system_call();
  4948. getwd_of(&path_buffer[2],drive);
  4949. end_system_call();
  4950. #endif
  4951. /* Hack by DJ (see GO32/EXPHDLR.C) and EM (see LIB/MISC/_GETCWD1.C):
  4952. converts all '\' to '/' and all captial- to small letters (only cosmetics,
  4953. because DOS and our PARSE-NAMESTRING also understand filenames with '/'
  4954. instead of '\').
  4955. convert to pathname: */
  4956. return asciz_dir_to_pathname(&path_buffer[0],O(pathname_encoding));
  4957. }
  4958. /* UP: Fills default-drive and default-directory into a pathname.
  4959. use_default_dir(pathname)
  4960. > pathname: non-logical pathname with Device /= :WILD
  4961. < result: new absolute pathname
  4962. can trigger GC */
  4963. local maygc object use_default_dir (object pathname) {
  4964. /* first copy the pathname: */
  4965. pathname = copy_pathname(pathname);
  4966. pushSTACK(pathname);
  4967. /* stack layout: pathname.
  4968. default for the device: */
  4969. #if HAS_HOST /* PATHNAME_WIN32 */
  4970. if (nullp(ThePathname(pathname)->pathname_host))
  4971. #endif
  4972. if (nullp(ThePathname(pathname)->pathname_device)) {
  4973. /* no device specified? --- take the default-drive instead: */
  4974. ThePathname(pathname)->pathname_device = O(default_drive);
  4975. }
  4976. { /* Default for the directory: */
  4977. var object subdirs = ThePathname(pathname)->pathname_directory;
  4978. /* Does pathname-directory start with :RELATIVE ? */
  4979. if (nullp(subdirs) || eq(Car(subdirs),S(Krelative))) {
  4980. /* yes -> replace :RELATIVE with the default-directory: */
  4981. pushSTACK(consp(subdirs) ? (object)Cdr(subdirs) : NIL);
  4982. #if HAS_HOST /* PATHNAME_WIN32 */
  4983. if (!nullp(ThePathname(pathname)->pathname_host)) {
  4984. /* We do not have the concept of a current directory on a
  4985. remote host. Simply use :ABSOLUTE instead of :RELATIVE. */
  4986. subdirs = allocate_cons();
  4987. Car(subdirs) = S(Kabsolute);
  4988. Cdr(subdirs) = popSTACK();
  4989. } else
  4990. #endif
  4991. { /* drive does not have to be present if we start on a network path */
  4992. var object drive = ThePathname(pathname)->pathname_device;
  4993. if (eq(drive,S(Kwild))) check_no_wildcards(pathname); /* error */
  4994. var uintB dr = nullp(drive) ? 0 : as_cint(TheSnstring(drive)->data[0]);
  4995. var object default_dir = default_directory_of(dr,pathname);
  4996. #if HAS_HOST /* PATHNAME_WIN32 */
  4997. ThePathname(STACK_1)->pathname_host = /* replace NIL in pathname ... */
  4998. ThePathname(default_dir)->pathname_host; /* ... with default */
  4999. #endif
  5000. /* default_dir (a Pathname) is finished.
  5001. Replace :RELATIVE with default-subdirs, i.e.
  5002. form (append default-subdirs (cdr subdirs))
  5003. = (nreconc (reverse default-subdirs) (cdr subdirs)) */
  5004. var object temp = ThePathname(default_dir)->pathname_directory;
  5005. temp = reverse(temp);
  5006. subdirs = nreconc(temp,popSTACK());
  5007. }
  5008. }
  5009. /* traverse list and freshly cons up, thereby process '.\' and '..\'
  5010. and '...\' (do not leave it to DOS): */
  5011. pushSTACK(subdirs);
  5012. pushSTACK(NIL);
  5013. /* stack layout: pathname, subdir-oldlist, subdir-newlist. */
  5014. while (mconsp(STACK_1)) { /* until oldlist is finished: */
  5015. var object subdir = Car(STACK_1); /* next subdir */
  5016. if (equal(subdir,O(dot_string))) {
  5017. /* = :CURRENT -> leave newlist unchanged */
  5018. } else if (equal(subdir,O(dotdot_string))) {
  5019. /* = :PARENT -> shorten newlist by one: */
  5020. if (matomp(Cdr(STACK_0))) { /* newlist (except for :ABSOLUTE) empty ? */
  5021. /* :PARENT from "\" returns Error */
  5022. pushSTACK(STACK_2); /* FILE-ERROR slot PATHNAME */
  5023. pushSTACK(O(backslash_string)); /* "\\" */
  5024. pushSTACK(directory_namestring(STACK_(2+2))); /* directory of pathname */
  5025. error(file_error,GETTEXT("no directory ~S above ~S"));
  5026. }
  5027. if (eq(Car(STACK_0),S(Kwild_inferiors))) { /* newlist starts with '...\' ? */
  5028. /* :PARENT from "...\" returns Error */
  5029. pushSTACK(STACK_2); /* FILE-ERROR slot PATHNAME */
  5030. pushSTACK(directory_namestring(STACK_(2+1))); /* directory of pathname */
  5031. error(file_error, /* '"..\\" after "...\\" is inadmissible: ~' */
  5032. GETTEXT("\"..\\\\\" after \"...\\\\\" is invalid: ~S"));
  5033. }
  5034. STACK_0 = Cdr(STACK_0);
  5035. } else { /* (also if :ABSOLUTE !) */
  5036. /* lengthen newlist by one: */
  5037. pushSTACK(subdir);
  5038. var object new_cons = allocate_cons();
  5039. Car(new_cons) = popSTACK();
  5040. Cdr(new_cons) = STACK_0;
  5041. STACK_0 = new_cons;
  5042. }
  5043. STACK_1 = Cdr(STACK_1);
  5044. }
  5045. subdirs = nreverse(popSTACK()); /* newlist, reverse again */
  5046. skipSTACK(1);
  5047. /* stack layout: pathname. */
  5048. ThePathname(STACK_0)->pathname_directory =
  5049. simplify_directory(subdirs); /* enter into the pathname */
  5050. pathname = popSTACK();
  5051. }
  5052. return pathname;
  5053. }
  5054. #ifdef WIN32_NATIVE
  5055. /* UP: translates short name to full name
  5056. > shortname: old DOS 8.3 pathname
  5057. wildcards aren't allowed. "." and ".." can be used.
  5058. < fullname: buffer should be not less than MAX_PATH
  5059. < result: true on success */
  5060. static BOOL FullName (LPCSTR shortname, LPSTR fullname) {
  5061. var char current[_MAX_PATH];
  5062. var char * rent = current;/* current+end-device-pos, rest after X: */
  5063. var int state = 1;
  5064. /* states for automata reading 'rent' pathname backward:
  5065. 0 - end
  5066. 1 - beginning
  5067. 2 - name component
  5068. 3 - slash component
  5069. 9,11,13... slash component after dots ("..").
  5070. components to be skipped = (state - 9)/2
  5071. 10,12,14... name components after dots.
  5072. components to be skipped = (state - 10)/2; */
  5073. var enum {fn_eof, fn_name, fn_dots, fn_dot, fn_slash} symbol;
  5074. /* symbol at the end of 'rent':
  5075. 1 - generic name
  5076. 2 - ".."
  5077. 3 - "."
  5078. 4 - slash
  5079. 0 - EOF i.e. beginning of 'rent' */
  5080. var int pos;
  5081. var int ops = 0;/* output position */
  5082. strcpy(current,shortname);
  5083. /* determine the end of device part */
  5084. if (((current[0] >= 'a' && current[0] <= 'z')
  5085. || (current[0] >= 'A' && current[0] <= 'Z'))
  5086. && current[1] == ':') {
  5087. rent = current+2;
  5088. } else if (current[0]=='\\' && current[1]=='\\') {
  5089. int i;rent = current;
  5090. /* host */
  5091. rent+=2;
  5092. for (i=0;i<2;i++) {/* skip host and sharename */
  5093. while (*rent && !cpslashp(*rent))
  5094. rent++;
  5095. if (*rent) rent++; else
  5096. return false;/*host and sharename don't end with slash*/
  5097. }
  5098. }
  5099. pos = strlen(rent);
  5100. do {
  5101. rent[pos] = '\0';
  5102. if (pos == 0) symbol = fn_eof; else
  5103. if (cpslashp(rent[pos-1])) { pos--; symbol = fn_slash; } else
  5104. { int dotcount = 0;/* < 0 -> not only dots */
  5105. int wild = 0;
  5106. while(pos > 0 && !cpslashp(rent[pos-1])) {
  5107. if (rent[pos-1] == '.') dotcount++; else dotcount = -pos;
  5108. if (rent[pos-1] == '*' || rent[pos-1] == '?') wild = 1;
  5109. pos--;
  5110. }
  5111. if (wild) return false;
  5112. if (dotcount <= 0) symbol = fn_name; else
  5113. if (dotcount == 1) symbol = fn_dot; else
  5114. if (dotcount == 2) symbol = fn_dots; else
  5115. return false; /* too many dots */
  5116. }
  5117. if (state == 1 /* beginning */
  5118. || state == 2 /* name component */) {
  5119. switch(symbol) {
  5120. case fn_dot: state = 3; break; /* slash */
  5121. case fn_dots: state = 11; break; /* dots-slash */
  5122. case fn_name: {
  5123. var WIN32_FIND_DATA wfd;
  5124. var HANDLE h = NULL;
  5125. h = FindFirstFile(current,&wfd);
  5126. if (h != INVALID_HANDLE_VALUE) {
  5127. strrev(wfd.cFileName);
  5128. if (ops > 0 || wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  5129. fullname[ops++] = '\\';
  5130. strcpy(fullname+ops,wfd.cFileName);
  5131. ops+=strlen(wfd.cFileName);
  5132. FindClose(h);
  5133. } else return false; /* file not found */
  5134. state = 3;
  5135. } break;
  5136. case fn_slash:
  5137. if (state == 1) state = 2;
  5138. else return false; /* two slashes in a row */
  5139. break;
  5140. case fn_eof:
  5141. if (state == 1 && current == rent) return false; /* D: */
  5142. else state = 0;
  5143. break;
  5144. default:
  5145. return false;/* program error */
  5146. }
  5147. } else if (state == 3) {/* slash */
  5148. switch(symbol) {
  5149. case fn_slash: state = 2;break;
  5150. case fn_eof:
  5151. if (current == rent) state = 0; else return false; /*D:FOO*/
  5152. break;
  5153. default: return false; /* program error */
  5154. }
  5155. } else if (state % 2 == 1) {/* dots - slash 9, 11, 13 ... */
  5156. switch(symbol) {
  5157. case fn_slash:
  5158. state += 1;
  5159. if (state == 10) state = 2; /* zero depth */
  5160. break; /* same depth */
  5161. case fn_eof:
  5162. return false; /* too many ".." */
  5163. break;
  5164. default: return false; /* program error */
  5165. }
  5166. } else {/* dots - name 10, 12, 14, ... */
  5167. switch(symbol) {
  5168. case fn_dot: state -= 1; break; /* same depth */
  5169. case fn_dots: state += 1; break; /* increase depth */
  5170. case fn_name: state -= 3; /* decrease depth */
  5171. if (state < 9) return false; /* program error */
  5172. break;
  5173. case fn_slash: return false; /* two slashes */
  5174. case fn_eof: return false; /* too many ".."s */
  5175. }
  5176. }
  5177. } while (state != 0);
  5178. if (rent > current) fullname[ops++] = '\\';
  5179. /* add device */
  5180. while(rent > current)
  5181. fullname[ops++] = (rent--)[-1];
  5182. fullname[ops] = '\0';
  5183. strrev(fullname);
  5184. return true;
  5185. }
  5186. #endif
  5187. /* UP: guarantees that the Directory of the Pathname exists
  5188. (signals an error if it does not)
  5189. assure_dir_exists(file_status,links_resolved,tolerantp)
  5190. > fs->fs_pathname: absolute pathname without wildcards in directory
  5191. > links_resolved: Flag, whether all links in the directory
  5192. of the pathname are already resolved
  5193. > tolerantp: Flag, whether an error should be avoided
  5194. < fs->fs_namestring:
  5195. if Name=NIL: Directory-Namestring (for DOS)
  5196. if Name/=NIL: Namestring (for DOS)
  5197. if tolerantp, maybe: nullobj
  5198. can trigger GC */
  5199. #ifdef WIN32_NATIVE
  5200. struct file_status {
  5201. gcv_object_t *fs_pathname; /* pointer into STACK */
  5202. object fs_namestring; /* usually returned by assure_dir_exists() */
  5203. };
  5204. local inline void file_status_init(struct file_status *fs,gcv_object_t *path) {
  5205. fs->fs_pathname = path;
  5206. fs->fs_namestring = nullobj;
  5207. }
  5208. local maygc void assure_dir_exists (struct file_status *fs,
  5209. bool links_resolved, bool tolerantp) {
  5210. var bool nnullp = namenullp(*(fs->fs_pathname));
  5211. if (nnullp && links_resolved) {
  5212. fs->fs_namestring = directory_namestring(*(fs->fs_pathname));
  5213. return;
  5214. }
  5215. with_sstring_0(whole_namestring(*(fs->fs_pathname)),O(pathname_encoding),
  5216. path, {
  5217. var char resolved[MAX_PATH];
  5218. var bool substitute = false;
  5219. var bool error = false;
  5220. begin_system_call();
  5221. if (links_resolved) { /* use light function */
  5222. shell_shortcut_target_t rresolve = resolve_shell_symlink(path,resolved);
  5223. if (rresolve != shell_shortcut_notresolved) {
  5224. if (rresolve == shell_shortcut_notexists)
  5225. error = true;
  5226. else
  5227. substitute = true;
  5228. }
  5229. } else {
  5230. if (real_path(path,resolved))
  5231. substitute = true;
  5232. else { /* A file doesn't exist. Maybe dir does ? */
  5233. error = true; /* let's be pessimistic */
  5234. if (!nnullp) {
  5235. var uintL lastslashpos = strlen(path) - 1;
  5236. while (lastslashpos > 0 && path[lastslashpos]!=slash) lastslashpos--;
  5237. if (path[lastslashpos]==slash) {
  5238. path[lastslashpos + 1] = '\0'; /* leave only path without name */
  5239. if (real_path(path,resolved)) {
  5240. /* substitute only directory part */
  5241. var DWORD fileattr = GetFileAttributes(resolved);
  5242. /* resolved to a file ? Only directories allowed
  5243. - nonmaskable error */
  5244. if (fileattr == 0xFFFFFFFF
  5245. || !(fileattr & FILE_ATTRIBUTE_DIRECTORY)) {
  5246. SetLastError(ERROR_DIRECTORY);
  5247. end_system_call(); OS_file_error(*(fs->fs_pathname));
  5248. }
  5249. pushSTACK(asciz_to_string(resolved,O(pathname_encoding)));
  5250. /* substitute immediately - w/o substitute flag
  5251. turn it into a pathname and use it with old name: */
  5252. pushSTACK(coerce_pathname(STACK_0));
  5253. /* save old pathname name and type components */
  5254. pushSTACK(ThePathname(STACK_2)->pathname_name);
  5255. pushSTACK(ThePathname(STACK_3)->pathname_type);
  5256. *(fs->fs_pathname) = STACK_2;
  5257. ThePathname(*(fs->fs_pathname))->pathname_name = STACK_1;
  5258. ThePathname(*(fs->fs_pathname))->pathname_type = STACK_0;
  5259. skipSTACK(4);
  5260. error = false;
  5261. }
  5262. }
  5263. }
  5264. }
  5265. }
  5266. end_system_call();
  5267. if (error) {
  5268. if (tolerantp) {
  5269. fs->fs_namestring = nullobj;
  5270. return;
  5271. }
  5272. pushSTACK(copy_pathname(*(fs->fs_pathname)));
  5273. ThePathname(STACK_0)->pathname_name = NIL;
  5274. ThePathname(STACK_0)->pathname_type = NIL;
  5275. error_dir_not_exists(popSTACK());
  5276. }
  5277. if (substitute) {
  5278. var object resolved_string =
  5279. asciz_to_string(resolved,O(pathname_encoding));
  5280. *(fs->fs_pathname) = coerce_pathname(resolved_string);
  5281. nnullp = namenullp(*(fs->fs_pathname));
  5282. }
  5283. });
  5284. if (!nnullp) {
  5285. /* merge in *DEFAULT-PATHNAME-DEFAULTS* & :VERSION :NEWEST:
  5286. for cross-platform consistency, either all or no versions of
  5287. assure_dir_exists() must call MERGE-PATHNAMES */
  5288. funcall(L(merge_pathnames),1); pushSTACK(value1);
  5289. }
  5290. { var object dns = directory_namestring(*(fs->fs_pathname));
  5291. fs->fs_namestring = nnullp ? dns : OSnamestring(dns); }
  5292. }
  5293. #endif
  5294. #endif
  5295. #ifdef PATHNAME_UNIX
  5296. /* UP: Return the current Directory.
  5297. < result: current Directory (as Pathname)
  5298. can trigger GC */
  5299. local maygc object default_directory (void) {
  5300. var char path_buffer[MAXPATHLEN]; /* cf. GETWD(3) */
  5301. /* store Working Directory in path_buffer: */
  5302. begin_system_call();
  5303. if ( getwd(&path_buffer[0]) ==NULL) {
  5304. end_system_call();
  5305. pushSTACK(O(dot_string)); /* FILE-ERROR slot PATHNAME */
  5306. pushSTACK(asciz_to_string(&path_buffer[0],O(pathname_encoding))); /* message */
  5307. error(file_error,GETTEXT("UNIX error while GETWD: ~S"));
  5308. }
  5309. end_system_call();
  5310. /* It must start with '/' : */
  5311. if (!(path_buffer[0] == '/')) {
  5312. pushSTACK(O(dot_string)); /* FILE-ERROR slot PATHNAME */
  5313. pushSTACK(asciz_to_string(&path_buffer[0],O(pathname_encoding)));
  5314. error(file_error,GETTEXT("UNIX GETWD returned ~S"));
  5315. }
  5316. /* convert to pathname: */
  5317. return asciz_dir_to_pathname(&path_buffer[0],O(pathname_encoding));
  5318. }
  5319. /* UP: Fills Default-Directory into a pathname.
  5320. use_default_dir(pathname)
  5321. > pathname: non-logical pathname
  5322. < result: new pathname, whose directory contains no :RELATIVE .
  5323. (short: "absolute pathname")
  5324. can trigger GC */
  5325. local maygc object use_default_dir (object pathname) {
  5326. /* copy the pathname first: */
  5327. pathname = copy_pathname(pathname);
  5328. { /* then build the default-directory into the pathname: */
  5329. var object subdirs = ThePathname(pathname)->pathname_directory;
  5330. /* does pathname-directory start with :RELATIVE? */
  5331. if (nullp(subdirs) || eq(Car(subdirs),S(Krelative))) {
  5332. /* yes -> replace :RELATIVE with default-subdirs, i.e.
  5333. form (append default-subdirs (cdr subdirs))
  5334. = (nreconc (reverse default-subdirs) (cdr subdirs)) */
  5335. pushSTACK(pathname);
  5336. pushSTACK(consp(subdirs) ? (object)Cdr(subdirs) : NIL);
  5337. var object temp = default_directory();
  5338. temp = ThePathname(temp)->pathname_directory;
  5339. temp = reverse(temp);
  5340. subdirs = nreconc(temp,popSTACK());
  5341. subdirs = simplify_directory(subdirs);
  5342. pathname = popSTACK();
  5343. /* enter into the pathname: */
  5344. ThePathname(pathname)->pathname_directory = subdirs;
  5345. }
  5346. }
  5347. return pathname;
  5348. }
  5349. /* UP: Assures, that the directory of a pathname exists, and thereby resolves
  5350. symbolic links.
  5351. assure_dir_exists(file_status, links_resolved, tolerantp)
  5352. > file_status->fs_pathname: non-logical pathname,
  5353. whose directory does not contain :RELATIVE.
  5354. > links_resolved: Flag, if all links in the directory of the pathname
  5355. are already resolved and if it is known to exist
  5356. > tolerantp: flag, if an error is to be avoided
  5357. < file_status->fs_pathname: (poss. the same) pathname, whereas neither for
  5358. the directory nor for the Filename a symbolic link is to be tracked.
  5359. < file_status->fs_namestring:
  5360. if Name=NIL: directory-namestring (for UNIX, with '/' at the end)
  5361. if Name/=NIL: namestring (for UNIX)
  5362. if tolerantp poss.: nullobj
  5363. < file_status->fs_stat_validp: if Name/=NIL:
  5364. false if the file does not exist,
  5365. true if it exists, in which case file_status->fs_stat contains its stats
  5366. can trigger GC */
  5367. struct file_status {
  5368. gcv_object_t *fs_pathname; /* pointer into STACK */
  5369. object fs_namestring; /* usually returned by assure_dir_exists() */
  5370. bool fs_stat_validp;
  5371. struct stat fs_stat;
  5372. };
  5373. local inline void file_status_init(struct file_status *fs,gcv_object_t *path) {
  5374. fs->fs_pathname = path;
  5375. fs->fs_namestring = nullobj;
  5376. fs->fs_stat_validp = false;
  5377. }
  5378. /* this has to be done this ugly way since C does not allow conditionals
  5379. (like #ifdef HAVE_LSTAT) inside macros (like with_sstring_0) */
  5380. #ifdef HAVE_LSTAT
  5381. #define if_HAVE_LSTAT(statement) statement
  5382. #else
  5383. #define if_HAVE_LSTAT(statement)
  5384. #endif
  5385. local char* realpath_obj (object namestring, char *path_buffer) {
  5386. char* ret;
  5387. with_sstring_0(namestring,O(pathname_encoding),namestring_asciz, {
  5388. begin_system_call();
  5389. ret = realpath(namestring_asciz,path_buffer);
  5390. end_system_call();
  5391. });
  5392. return ret;
  5393. }
  5394. local maygc void assure_dir_exists (struct file_status *fs,
  5395. bool links_resolved, bool tolerantp) {
  5396. var uintC allowed_links = MAXSYMLINKS; /* number of allowed symbolic links */
  5397. if (links_resolved)
  5398. goto dir_exists;
  5399. while (1) { /* loop over the symbolic links to be resolved */
  5400. { /* determine Truepath of the directory: */
  5401. var char path_buffer[MAXPATHLEN]; /* cf. REALPATH(3) */
  5402. {
  5403. var object pathname = *(fs->fs_pathname);
  5404. var uintC stringcount = /* host and directory strings */
  5405. directory_namestring_parts(pathname);
  5406. pushSTACK(O(dot_string)); /* and "." */
  5407. var object string = string_concat(stringcount+1); /* concatenate */
  5408. /* resolve symbolic links therein: */
  5409. if (realpath_obj(string,path_buffer) == NULL) {
  5410. if (errno!=ENOENT) { OS_file_error(*(fs->fs_pathname)); }
  5411. if (!tolerantp)
  5412. error_dir_not_exists(asciz_dir_to_pathname(path_buffer,O(pathname_encoding))); /* erroneous component */
  5413. fs->fs_namestring = nullobj; return;
  5414. }
  5415. }
  5416. /* new Directory-Path must start with '/' : */
  5417. if (!(path_buffer[0] == '/')) {
  5418. pushSTACK(*(fs->fs_pathname)); /* FILE-ERROR slot PATHNAME */
  5419. pushSTACK(asciz_to_string(&path_buffer[0],O(pathname_encoding)));
  5420. error(file_error,GETTEXT("UNIX REALPATH returned ~S"));
  5421. }
  5422. /* possibly add a '/' at the end: */
  5423. var char* pathptr = &path_buffer[0];
  5424. var uintL len = 0; /* string-length */
  5425. while (*pathptr != 0) { pathptr++; len++; } /* search ASCIZ-string-end */
  5426. if (!((len>0) && (pathptr[-1]=='/'))) {
  5427. *pathptr = '/'; len++; /* add a '/' */
  5428. }
  5429. /* and convert to a string: */
  5430. var object new_string =
  5431. n_char_to_string(&path_buffer[0],len,O(pathname_encoding));
  5432. /* turn it into a pathname and use its Directory: */
  5433. var object new_pathname = coerce_pathname(new_string);
  5434. ThePathname(*(fs->fs_pathname))->pathname_directory
  5435. = ThePathname(new_pathname)->pathname_directory;
  5436. }
  5437. dir_exists:
  5438. /* get information for the addressed file: */
  5439. if (namenullp(*(fs->fs_pathname))) { /* no file addressed? */
  5440. fs->fs_namestring = directory_namestring(*(fs->fs_pathname));
  5441. return; /* yes -> finished */
  5442. }
  5443. fs->fs_namestring = whole_namestring(*(fs->fs_pathname)); /* concat */
  5444. /* get information: */
  5445. with_sstring_0(fs->fs_namestring,O(pathname_encoding),namestring_asciz, {
  5446. begin_system_call();
  5447. if (!( lstat(namestring_asciz,&(fs->fs_stat)) ==0)) {
  5448. if (!(errno==ENOENT))
  5449. { end_system_call(); OS_file_error(*(fs->fs_pathname)); }
  5450. /* file does not exist. */
  5451. end_system_call();
  5452. FREE_DYNAMIC_ARRAY(namestring_asciz);
  5453. fs->fs_stat_validp = false; return;
  5454. }
  5455. end_system_call();
  5456. /* file exists. */
  5457. if (S_ISDIR(fs->fs_stat.st_mode)) { /* is it a directory? */
  5458. pushSTACK(*(fs->fs_pathname)); /* FILE-ERROR slot PATHNAME */
  5459. pushSTACK(whole_namestring(*(fs->fs_pathname)));
  5460. pushSTACK(TheSubr(subr_self)->name);
  5461. error(file_error,GETTEXT("~S: ~S names a directory, not a file"));
  5462. }
  5463. if_HAVE_LSTAT(
  5464. else if (possible_symlink(namestring_asciz)
  5465. && S_ISLNK(fs->fs_stat.st_mode)) {
  5466. /* is it a symbolic link?
  5467. yes -> continue resolving: */
  5468. if (allowed_links==0) { /* no more links allowed? */
  5469. /* yes -> simulate UNIX-Error ELOOP */
  5470. begin_system_call();
  5471. errno = ELOOP_VALUE;
  5472. end_system_call();
  5473. OS_file_error(*(fs->fs_pathname));
  5474. }
  5475. allowed_links--; /* after that, one link less is allowed */
  5476. var uintL linklen = fs->fs_stat.st_size; /* presumed length of the link-content */
  5477. retry_readlink: {
  5478. var DYNAMIC_ARRAY(linkbuf,char,linklen+1); /* buffer for the Link-content */
  5479. /* read link-content: */
  5480. begin_system_call();
  5481. {
  5482. var int result = readlink(namestring_asciz,linkbuf,linklen);
  5483. end_system_call();
  5484. if (result<0)
  5485. OS_file_error(*(fs->fs_pathname));
  5486. if (!(result == (int)linklen)) { /* sometimes (AIX, NFS) status.st_size is incorrect */
  5487. FREE_DYNAMIC_ARRAY(linkbuf); linklen = result; goto retry_readlink;
  5488. }
  5489. }
  5490. /* turn it into a pathname:
  5491. (MERGE-PATHNAMES (PARSE-NAMESTRING linkbuf) pathname-without-name&type) */
  5492. pushSTACK(n_char_to_string(linkbuf,linklen,O(pathname_encoding)));
  5493. FREE_DYNAMIC_ARRAY(linkbuf);
  5494. }
  5495. funcall(L(parse_namestring),1);
  5496. pushSTACK(value1);
  5497. var object pathname = copy_pathname(*(fs->fs_pathname));
  5498. ThePathname(pathname)->pathname_name = NIL;
  5499. ThePathname(pathname)->pathname_type = NIL;
  5500. pushSTACK(pathname);
  5501. funcall(L(merge_pathnames),2);
  5502. *(fs->fs_pathname) = value1;
  5503. }
  5504. ) /* HAVE_LSTAT */
  5505. else { /* normal file */
  5506. fs->fs_stat_validp = true; return;
  5507. }
  5508. });
  5509. }
  5510. }
  5511. #endif
  5512. #ifdef PATHNAME_WIN32
  5513. #if 0 /* unused */
  5514. /* UP: Turns a directory-namestring into one, that is suitably for DOS.
  5515. OSdirnamestring(namestring)
  5516. > namestring: newly created directory-namestring, with '\' at the end,
  5517. a normal-simple-string
  5518. < result: namestring for this directory, in DOS-Format: last '\'
  5519. discarded, if superfluous, a normal-simple-string
  5520. can trigger GC */
  5521. local maygc object OSdirnamestring (object namestring) {
  5522. var uintL len = Sstring_length(namestring);
  5523. if (len==0) goto ok; /* empty string -> do not discard anything */
  5524. var chart ch = TheSnstring(namestring)->data[len-1];
  5525. if (!chareq(ch,ascii('\\'))) /* no '\' at the end -> do not discard */
  5526. goto ok;
  5527. if (len==1) goto ok; /* "\" means Root -> do not discard */
  5528. ch = TheSnstring(namestring)->data[len-2];
  5529. if (chareq(ch,ascii('\\')) || colonp(ch)) /* '\' or ':' before it */
  5530. goto ok; /* -> means parent -> do not discard */
  5531. /* discard '\' at the end: */
  5532. namestring = subsstring(namestring,0,len-1);
  5533. ok: /* do not discard anything */
  5534. return namestring;
  5535. }
  5536. #endif
  5537. /* UP: Changes the default-drive and its default-directory.
  5538. change_default();
  5539. > STACK_0: absolute pathname, whose device is a string and directory
  5540. contains no :RELATIVE, :CURRENT, :PARENT, and name and type are =NIL.
  5541. can trigger GC */
  5542. local maygc void change_default (void) {
  5543. { /* change default-directory for this drive: */
  5544. var object pathname = STACK_0;
  5545. var uintC stringcount = directory_namestring_parts(pathname);
  5546. /* no redundant '\' at the end */
  5547. if (mconsp(Cdr(ThePathname(pathname)->pathname_directory))) {
  5548. skipSTACK(1); stringcount--;
  5549. }
  5550. var object string = string_concat(stringcount); /* concatenate */
  5551. with_sstring_0(string,O(pathname_encoding),asciz, {
  5552. /* change default-directory: */
  5553. change_current_directory(asciz);
  5554. });
  5555. }
  5556. /* change default-drive: */
  5557. O(default_drive) = ThePathname(STACK_0)->pathname_device;
  5558. /* set *DEFAULT-PATHNAME-DEFAULTS* : */
  5559. recalc_defaults_pathname();
  5560. }
  5561. #endif
  5562. #ifdef PATHNAME_UNIX
  5563. /* UP: changes the default-directory.
  5564. change_default();
  5565. > STACK_0: absolute pathname, whose directory contains no :RELATIVE,
  5566. :CURRENT, :PARENT , and name and Type are =NIL.
  5567. can trigger GC */
  5568. local maygc void change_default (void) {
  5569. var object string = directory_namestring(STACK_0);
  5570. with_sstring_0(string,O(pathname_encoding),asciz, {
  5571. /* change default-directory: */
  5572. begin_system_call();
  5573. if (!( chdir(asciz) ==0)) { end_system_call(); OS_file_error(STACK_0); }
  5574. end_system_call();
  5575. });
  5576. }
  5577. #endif
  5578. LISPFUNNR(namestring,1) { /* (NAMESTRING pathname), CLTL p. 417 */
  5579. var object pathname = coerce_xpathname(popSTACK());
  5580. VALUES1(whole_namestring(pathname));
  5581. }
  5582. /* error-message because of missing file name
  5583. error_noname(pathname);
  5584. > pathname: pathname */
  5585. nonreturning_function(local, error_noname, (object pathname)) {
  5586. pushSTACK(pathname); /* FILE-ERROR slot PATHNAME */
  5587. pushSTACK(pathname);
  5588. error(file_error,GETTEXT("no file name given: ~S"));
  5589. }
  5590. #define check_noname(pathname) \
  5591. do { if (namenullp(pathname)) { error_noname(pathname); } } while(0)
  5592. /* error-message because of illegal Name/Type-specification
  5593. error_notdir(pathname);
  5594. > pathname: pathname */
  5595. nonreturning_function(local, error_notdir, (object pathname)) {
  5596. pushSTACK(pathname); /* FILE-ERROR slot PATHNAME */
  5597. pushSTACK(pathname);
  5598. error(file_error,GETTEXT("not a directory: ~S"));
  5599. }
  5600. #define check_notdir(pathname) \
  5601. do { if (!(nullp(ThePathname(pathname)->pathname_name) \
  5602. && nullp(ThePathname(pathname)->pathname_type))) \
  5603. error_notdir(pathname); } while(0)
  5604. /* test, if a file exists:
  5605. file_exists(file_status)
  5606. > only after: assure_dir_exists() */
  5607. #ifdef WIN32_NATIVE
  5608. local inline int access0 (const char* path, struct file_status *fs) {
  5609. begin_system_call();
  5610. var DWORD fileattr = GetFileAttributes(path);
  5611. if (fileattr == 0xFFFFFFFF) {
  5612. if (WIN32_ERROR_NOT_FOUND) {
  5613. end_system_call(); return -1;
  5614. }
  5615. end_system_call(); OS_file_error(*(fs->fs_pathname));
  5616. }
  5617. end_system_call();
  5618. return 0;
  5619. }
  5620. local bool file_exists (struct file_status *fs) {
  5621. var bool exists;
  5622. with_sstring_0(fs->fs_namestring,O(pathname_encoding),namestring_asciz, {
  5623. exists = (access0(namestring_asciz,fs)==0);
  5624. });
  5625. return exists;
  5626. }
  5627. #endif
  5628. #ifdef UNIX
  5629. #define file_exists(fs) ((fs)->fs_stat_validp)
  5630. #define FILE_EXISTS_TRIVIAL
  5631. #endif
  5632. /* error-message because of non-existent file
  5633. error_file_not_exists();
  5634. > STACK_0: pathname */
  5635. nonreturning_function(local, error_file_not_exists, (void)) {
  5636. /* STACK_0 = FILE-ERROR slot PATHNAME */
  5637. pushSTACK(STACK_0); /* pathname */
  5638. pushSTACK(TheSubr(subr_self)->name);
  5639. error(file_error,GETTEXT("~S: file ~S does not exist"));
  5640. }
  5641. /* TRUENAME for a pathname
  5642. set fs->fs_pathname to the truename (filename for the operating system)
  5643. or nullobj
  5644. can trigger GC */
  5645. local maygc void true_namestring (struct file_status *fs, bool noname_p,
  5646. bool tolerantp) {
  5647. check_no_wildcards(*fs->fs_pathname); /* with wildcards -> error */
  5648. *(fs->fs_pathname) = use_default_dir(*(fs->fs_pathname)); /* insert default-directory */
  5649. if (noname_p) check_noname(*(fs->fs_pathname));
  5650. assure_dir_exists(fs,false,tolerantp);
  5651. }
  5652. LISPFUNNR(truename,1)
  5653. { /* (TRUENAME pathname), CLTL p. 413 */
  5654. var object pathname = STACK_0; /* pathname-argument */
  5655. if (builtin_stream_p(pathname)) { /* stream -> treat extra: */
  5656. /* must be file-stream: */
  5657. pathname = as_file_stream(pathname);
  5658. test_file_stream_named(pathname);
  5659. /* Streamtype File-Stream */
  5660. VALUES1(TheStream(pathname)->strm_file_truename);
  5661. } else {
  5662. var struct file_status fs; file_status_init(&fs,&STACK_0);
  5663. *(fs.fs_pathname) = merge_defaults(coerce_pathname(pathname));
  5664. true_namestring(&fs,false,false);
  5665. if (namenullp(*(fs.fs_pathname))) { /* no name specified */
  5666. if (!nullp(ThePathname(*(fs.fs_pathname))->pathname_type)) {
  5667. pushSTACK(*(fs.fs_pathname)); /* FILE-ERROR slot PATHNAME */
  5668. pushSTACK(STACK_0); /* pathname */
  5669. pushSTACK(TheSubr(subr_self)->name);
  5670. error(file_error,GETTEXT("~S: pathname with type but without name makes no sense: ~S"));
  5671. }
  5672. /* no name and no type specified -> pathname as result */
  5673. } else {
  5674. /* name specified.
  5675. check, if the file exists: */
  5676. if (!file_exists(&fs)) { error_file_not_exists(); }
  5677. /* file exists -> pathname as value */
  5678. }
  5679. VALUES1(*(fs.fs_pathname));
  5680. }
  5681. skipSTACK(1);
  5682. }
  5683. LISPFUNNR(probe_file,1)
  5684. { /* (PROBE-FILE filename), CLTL p. 424 */
  5685. var object pathname = popSTACK(); /* pathname-argument */
  5686. if (builtin_stream_p(pathname)) { /* stream -> treat extra: */
  5687. /* must be file-stream: */
  5688. pathname = as_file_stream(pathname);
  5689. test_file_stream_named(pathname);
  5690. /* streamtype file-stream -> take truename: */
  5691. var uintB flags = TheStream(pathname)->strmflags;
  5692. pathname = TheStream(pathname)->strm_file_truename;
  5693. if (flags & strmflags_open_B) { /* file opened? */
  5694. /* yes -> truename instantly as result: */
  5695. VALUES1(pathname); return;
  5696. }
  5697. /* no -> yet to test, if the file for the truename exists. */
  5698. } else /* turn into a pathname */
  5699. pathname = merge_defaults(coerce_pathname(pathname));
  5700. /* pathname is now a Pathname. */
  5701. pushSTACK(pathname);
  5702. var struct file_status fs; file_status_init(&fs,&STACK_0);
  5703. true_namestring(&fs,true,true);
  5704. if (eq(fs.fs_namestring,nullobj)) {
  5705. /* path to the file does not exist -> NIL as value: */
  5706. skipSTACK(1); VALUES1(NIL); return;
  5707. }
  5708. if (file_exists(&fs)) /* check, if the file exists: */
  5709. VALUES1(*(fs.fs_pathname)); /* file exists -> pathname as value */
  5710. else VALUES1(NIL); /* else NIL as value */
  5711. skipSTACK(1);
  5712. }
  5713. /* call stat(2) on the object and return its return value
  5714. > namestring: string
  5715. > status: pointer to a stat
  5716. < status */
  5717. local int stat_obj (object namestring, struct stat *status) {
  5718. int ret;
  5719. with_sstring_0(namestring,O(pathname_encoding),namestring_asciz, {
  5720. begin_system_call();
  5721. ret = stat(namestring_asciz,status);
  5722. end_system_call();
  5723. });
  5724. return ret;
  5725. }
  5726. /* tests, if a directory exists.
  5727. directory_exists(pathname)
  5728. > pathname: an absolute pathname without wildcards, with Name=NIL and Type=NIL
  5729. < result: true, if it denotes an existing directory
  5730. can trigger GC */
  5731. local maygc bool directory_exists (object pathname) {
  5732. pushSTACK(pathname); /* save pathname */
  5733. var object dir_namestring = directory_namestring(pathname);
  5734. /* existence test, see assure_dir_exists(): */
  5735. var bool exists = true;
  5736. #ifdef WIN32_NATIVE
  5737. with_sstring_0(dir_namestring,O(pathname_encoding),dir_namestring_asciz, {
  5738. if (!nullp(Cdr(ThePathname(STACK_0)->pathname_directory))) {
  5739. var uintL len = Sstring_length(dir_namestring);
  5740. ASSERT((len > 0) && cpslashp(dir_namestring_asciz[len-1]));
  5741. dir_namestring_asciz[len-1] = '\0'; /* replace '\' at the end with nullbyte */
  5742. }
  5743. begin_system_call();
  5744. var DWORD fileattr = GetFileAttributes(dir_namestring_asciz);
  5745. if (fileattr == 0xFFFFFFFF) {
  5746. if (!WIN32_ERROR_NOT_FOUND) {
  5747. end_system_call(); OS_file_error(STACK_0);
  5748. }
  5749. exists = false;
  5750. } else {
  5751. if (!(fileattr & FILE_ATTRIBUTE_DIRECTORY)) /* found file is no subdirectory ? */
  5752. exists = false;
  5753. }
  5754. end_system_call();
  5755. });
  5756. #endif
  5757. #ifdef PATHNAME_UNIX
  5758. pushSTACK(dir_namestring);
  5759. pushSTACK(O(dot_string)); /* and "." */
  5760. dir_namestring = string_concat(2); /* concatenate */
  5761. var struct stat statbuf;
  5762. if (stat_obj(dir_namestring,&statbuf) < 0) {
  5763. if (errno != ENOENT) OS_file_error(STACK_0);
  5764. exists = false;
  5765. } else {
  5766. if (!S_ISDIR(statbuf.st_mode)) /* found file is no subdirectory ? */
  5767. exists = false;
  5768. }
  5769. #endif
  5770. skipSTACK(1);
  5771. return exists;
  5772. }
  5773. LISPFUNNR(probe_directory,1)
  5774. { /* (PROBE-DIRECTORY filename) tests, if a directory exists. */
  5775. var object pathname = popSTACK(); /* pathname-argument */
  5776. pathname = merge_defaults(coerce_pathname(pathname)); /* --> pathname */
  5777. check_no_wildcards(pathname); /* with wildcards -> error */
  5778. pathname = use_default_dir(pathname); /* insert default-directory */
  5779. check_notdir(pathname); /* ensure that Name=NIL and Type=NIL */
  5780. VALUES_IF(directory_exists(pathname));
  5781. }
  5782. /* Converts a directory pathname to an OS directory specification.
  5783. > pathname: an object
  5784. > use_default: whether to use the current default directory
  5785. < result: a simple-bit-vector containing an ASCIZ string in OS format
  5786. can trigger GC */
  5787. global maygc object pathname_to_OSdir (object pathname, bool use_default) {
  5788. pathname = coerce_pathname(pathname); /* convert to pathname */
  5789. check_no_wildcards(pathname); /* if it has wildcards -> error */
  5790. if (use_default)
  5791. pathname = use_default_dir(pathname); /* insert default directory */
  5792. check_notdir(pathname); /* ensure that Name=NIL and Type=NIL */
  5793. pushSTACK(pathname); /* save pathname */
  5794. var object dir_namestring = directory_namestring(pathname);
  5795. var object dir_namestring_asciz =
  5796. string_to_asciz(dir_namestring,O(pathname_encoding));
  5797. var char* asciz = TheAsciz(dir_namestring_asciz);
  5798. var uintL len = asciz_length(asciz);
  5799. #if defined(WIN32_NATIVE) || defined(UNIX)
  5800. if (!nullp(Cdr(ThePathname(STACK_0)->pathname_directory))) {
  5801. ASSERT((len > 0) && cpslashp(asciz[len-1]));
  5802. asciz[len-1] = '\0';
  5803. }
  5804. #endif
  5805. skipSTACK(1); /* forget pathname */
  5806. return dir_namestring_asciz;
  5807. }
  5808. /* Converts an OS directory specification to a directory pathname.
  5809. > path: a pathname referring to a directory
  5810. < result: a pathname without name and type
  5811. can trigger GC */
  5812. global maygc object OSdir_to_pathname (const char* path) {
  5813. return asciz_dir_to_pathname(path,O(pathname_encoding));
  5814. }
  5815. /* UP: determines, if a file is opened.
  5816. openp(pathname) */
  5817. #ifdef PATHNAME_WIN32
  5818. /* > pathname: absolute pathname, without wildcards. */
  5819. #endif
  5820. #ifdef PATHNAME_UNIX
  5821. /* > pathname: absolute pathname, without wildcards, after resolution
  5822. of symbolic links */
  5823. #endif
  5824. /* < result: true, if an opened file-stream exits for this file. */
  5825. local bool openp (object pathname) {
  5826. var object flist = O(open_files); /* traverse list of all open files */
  5827. while (consp(flist)) {
  5828. var object f = Car(flist); /* next open stream */
  5829. if (TheStream(f)->strmtype == strmtype_file) { /* file-stream ? */
  5830. if (equal(TheStream(f)->strm_file_truename,pathname))
  5831. return true;
  5832. }
  5833. flist = Cdr(flist);
  5834. }
  5835. return false;
  5836. }
  5837. /* error-message because of deletion attempt on opened file
  5838. error_delete_open(pathname);
  5839. > pathname: truename of the file */
  5840. nonreturning_function(local, error_delete_open, (object pathname)) {
  5841. pushSTACK(pathname); /* FILE-ERROR slot PATHNAME */
  5842. pushSTACK(pathname); pushSTACK(TheSubr(subr_self)->name);
  5843. error(file_error,GETTEXT("~S: Cannot delete file ~S since there is a file stream open to it"));
  5844. }
  5845. #define check_delete_open(pathname) \
  5846. do { if (openp(pathname)) { error_delete_open(pathname); } } while(0)
  5847. /* (DELETE-FILE filename), CLTL p. 424 */
  5848. LISPFUNN(delete_file,1) {
  5849. var object pathname = popSTACK(); /* pathname-argument */
  5850. if (builtin_stream_p(pathname)) { /* stream -> treat extra: */
  5851. var object stream = as_file_stream(pathname); /* must be file-stream */
  5852. test_file_stream_named(stream);
  5853. /* Streamtype file-stream.
  5854. if file is opened, close file first: */
  5855. if (TheStream(stream)->strmflags & strmflags_open_B) { /* file opened ? */
  5856. pushSTACK(stream); builtin_stream_close(&STACK_0,0); stream = popSTACK();
  5857. }
  5858. /* then take the truename as file to be deleted: */
  5859. pathname = TheStream(stream)->strm_file_truename;
  5860. } else /* turn into a pathname */
  5861. pathname = merge_defaults(coerce_pathname(pathname));
  5862. /* pathname is now a pathname. */
  5863. check_no_wildcards(pathname); /* with wildcards -> error */
  5864. pathname = use_default_dir(pathname); /* insert default-directory */
  5865. check_noname(pathname);
  5866. pushSTACK(pathname); pushSTACK(pathname);
  5867. var struct file_status fs; file_status_init(&fs,&STACK_0);
  5868. assure_dir_exists(&fs,false,true);
  5869. if (!eq(fs.fs_namestring,nullobj)) /* path to the file exists */
  5870. check_delete_open(*(fs.fs_pathname));
  5871. /* delete the original filename - not the truename (which may be invalid!) */
  5872. if (delete_file_if_exists_obj(whole_namestring(STACK_1)))
  5873. /* file existed, was deleted -> pathname (/=NIL) as value */
  5874. VALUES1(nullp(O(ansi)) ? (object)STACK_1 : T);
  5875. else /* file does not exist -> value NIL */
  5876. VALUES1(NIL);
  5877. skipSTACK(2);
  5878. }
  5879. /* error-message because of renaming attempt of an opened file
  5880. error_rename_open(pathname);
  5881. > pathname: truename of the file */
  5882. nonreturning_function(local, error_rename_open, (object pathname)) {
  5883. pushSTACK(pathname); /* FILE-ERROR slot PATHNAME */
  5884. pushSTACK(pathname); pushSTACK(TheSubr(subr_self)->name);
  5885. error(file_error,GETTEXT("~S: Cannot rename file ~S since there is a file stream open to it"));
  5886. }
  5887. #define check_rename_open(pathname) \
  5888. do { if (openp(pathname)) { error_rename_open(pathname); } } while(0)
  5889. /* UP: Renames a file.
  5890. rename_file();
  5891. > stack layout: filename, newname, oldpathname.
  5892. < stack layout: filename, newname, oldpathname, newpathname,
  5893. oldtruename, oldnamestring, newtruename, newnamestring. */
  5894. local void rename_file (void) {
  5895. { /* 1. newpathname := (MERGE-PATHNAMES newname oldpathname) */
  5896. pushSTACK(STACK_1); /* newname as 1st argument */
  5897. pushSTACK(STACK_(0+1)); /* oldpathname as 2nd argument */
  5898. funcall(L(merge_pathnames),2);
  5899. pushSTACK(value1);
  5900. }
  5901. /* stack layout: filename, newname, oldpathname, newpathname. */
  5902. { /* 2. check oldpathname: */
  5903. pushSTACK(STACK_1);
  5904. var struct file_status fs; file_status_init(&fs,&STACK_0);
  5905. true_namestring(&fs,true,false);
  5906. check_rename_open(*(fs.fs_pathname)); /* do not rename open files! */
  5907. if (!file_exists(&fs))
  5908. error_file_not_exists();
  5909. pushSTACK(fs.fs_namestring);
  5910. }
  5911. /* stack layout: filename, newname, oldpathname, newpathname,
  5912. oldtruename, oldnamestring. */
  5913. { /* 3. check newpathname: */
  5914. var object newpathname = coerce_pathname(STACK_2);
  5915. pushSTACK(newpathname);
  5916. var struct file_status fs; file_status_init(&fs,&STACK_0);
  5917. true_namestring(&fs,true,false);
  5918. /* stack layout: filename, newname, oldpathname, newpathname,
  5919. oldtruename, oldnamestring, newtruename.
  5920. 4. rename file: */
  5921. if (file_exists(&fs))
  5922. /* file already exists -> do not delete without forewarn */
  5923. error_file_exists();
  5924. pushSTACK(fs.fs_namestring);
  5925. }
  5926. /* stack layout: filename, newname, oldpathname, newpathname,
  5927. oldtruename, oldnamestring, newtruename, newnamestring.
  5928. now it can be renamed without risk: */
  5929. with_sstring_0(STACK_2,O(pathname_encoding),oldnamestring_asciz, {
  5930. with_sstring_0(STACK_0,O(pathname_encoding),newnamestring_asciz, {
  5931. rename_existing_file(oldnamestring_asciz,newnamestring_asciz);
  5932. });
  5933. });
  5934. }
  5935. /* (RENAME-FILE filename newname), CLTL p. 423 */
  5936. LISPFUNN(rename_file,2) {
  5937. var object filename = STACK_1; /* filename-argument */
  5938. if (builtin_stream_p(filename)) { /* stream -> treat extra: */
  5939. /* must be file-stream: */
  5940. filename = as_file_stream(filename);
  5941. test_file_stream_named(filename);
  5942. /* streamtype file-stream -> use truename: */
  5943. filename = TheStream(filename)->strm_file_truename;
  5944. pushSTACK(filename);
  5945. /* rename: */
  5946. rename_file();
  5947. /* update stream: */
  5948. filename = STACK_7;
  5949. TheStream(filename)->strm_file_name = STACK_4; /* newpathname as new name */
  5950. TheStream(filename)->strm_file_truename = STACK_1; /* newtruename as new truename */
  5951. /* leave handle etc. untouched */
  5952. } else { /* turn into a pathname */
  5953. filename = merge_defaults(coerce_pathname(filename));
  5954. pushSTACK(filename);
  5955. /* rename: */
  5956. rename_file();
  5957. }
  5958. VALUES3(STACK_4, /* newpathname as 1st value */
  5959. STACK_3, /* oldtruename as 2nd value */
  5960. STACK_1); /* newtruename as 3rd value */
  5961. skipSTACK(8);
  5962. }
  5963. /* Create a file.
  5964. create_new_file(pathstring);
  5965. It is known that the file does not already exist.
  5966. > pathstring: file name, ASCIZ-String
  5967. > STACK_0: pathname */
  5968. local inline void create_new_file (char* pathstring) {
  5969. #ifdef WIN32_NATIVE
  5970. begin_system_call();
  5971. var Handle handle = CreateFile(pathstring, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  5972. if (handle==INVALID_HANDLE_VALUE)
  5973. { end_system_call(); OS_file_error(STACK_0); }
  5974. /* file was created, handle is the Handle.
  5975. close file again: */
  5976. if (!CloseHandle(handle)) { end_system_call(); OS_file_error(STACK_0); }
  5977. end_system_call();
  5978. #endif
  5979. #ifdef UNIX
  5980. begin_system_call();
  5981. var int result = OPEN(pathstring, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, my_open_mask);
  5982. if (result<0) { end_system_call(); OS_file_error(STACK_0); } /* report error */
  5983. /* file was created, result is the Handle.
  5984. close file again: */
  5985. if (!(CLOSE(result)==0)) { end_system_call(); OS_file_error(STACK_0); } /* report error */
  5986. end_system_call();
  5987. #endif
  5988. }
  5989. /* Open a file for input.
  5990. open_input_file(file_status, pathstring,create_if_not_exists,&handle)
  5991. > only after: assure_dir_exists()
  5992. > file_status: structure, filled in by assure_dir_exists()
  5993. > pathstring: file name, ASCIZ-String
  5994. > create_if_not_exists: if true, the file must be created
  5995. > STACK_0: pathname
  5996. < handle: open file handle
  5997. < result: whether the file could be opened (necessarily true if create_if_not_exists) */
  5998. local inline bool open_input_file (struct file_status *fs, char* pathstring,
  5999. bool create_if_not_exists, Handle* handle_) {
  6000. #ifdef UNIX
  6001. var int result;
  6002. #ifdef FILE_EXISTS_TRIVIAL
  6003. var int oflags = O_RDONLY | O_BINARY;
  6004. if (!file_exists(fs)) {
  6005. /* file does not exist */
  6006. if (!create_if_not_exists) return false;
  6007. /* create file with open: */
  6008. oflags |= O_CREAT;
  6009. }
  6010. begin_system_call();
  6011. result = OPEN(pathstring,oflags,my_open_mask);
  6012. end_system_call();
  6013. if (result<0) { OS_file_error(STACK_0); }
  6014. #else
  6015. var int oflags = O_RDONLY | O_BINARY;
  6016. if (create_if_not_exists) { oflags |= O_CREAT; }
  6017. begin_system_call();
  6018. result = OPEN(pathstring,oflags,my_open_mask);
  6019. if (result<0) {
  6020. if (errno == ENOENT) { /* not found? */
  6021. /* file does not exist */
  6022. if (!create_if_not_exists) { end_system_call(); return false; }
  6023. }
  6024. end_system_call(); OS_file_error(STACK_0); /* report error */
  6025. }
  6026. end_system_call();
  6027. #endif
  6028. *handle_ = result; return true;
  6029. #endif
  6030. #ifdef WIN32_NATIVE
  6031. var Handle handle;
  6032. #ifdef FILE_EXISTS_TRIVIAL
  6033. var DWORD flag = OPEN_EXISTING;
  6034. if (!file_exists(_EMA_)) { /* file does not exist */
  6035. if (!create_if_not_exists) return false;
  6036. /* create file with CreateFile: */
  6037. flag = OPEN_ALWAYS;
  6038. }
  6039. begin_system_call();
  6040. handle = CreateFile(pathstring, GENERIC_READ,
  6041. FILE_SHARE_READ | FILE_SHARE_WRITE,
  6042. NULL, flag, FILE_ATTRIBUTE_NORMAL, NULL);
  6043. end_system_call();
  6044. if (handle==INVALID_HANDLE_VALUE) { OS_file_error(STACK_0); }
  6045. #else
  6046. var DWORD flag = OPEN_EXISTING;
  6047. if (create_if_not_exists) { flag = OPEN_ALWAYS; }
  6048. begin_system_call();
  6049. handle = CreateFile(pathstring, GENERIC_READ,
  6050. FILE_SHARE_READ | FILE_SHARE_WRITE,
  6051. NULL, flag, FILE_ATTRIBUTE_NORMAL, NULL);
  6052. if (handle==INVALID_HANDLE_VALUE) {
  6053. if (WIN32_ERROR_NOT_FOUND) { /* not found? */
  6054. /* file does not exist */
  6055. if (!create_if_not_exists) { end_system_call(); return false; }
  6056. }
  6057. end_system_call(); OS_file_error(STACK_0); /* report Error */
  6058. }
  6059. end_system_call();
  6060. #endif
  6061. *handle_ = handle; return true;
  6062. #endif
  6063. }
  6064. #if defined(UNIX) || defined(WIN32_NATIVE)
  6065. /* Open a file for output.
  6066. open_output_file(pathstring,truncate_if_exists)
  6067. > pathstring: file name, ASCIZ-String
  6068. > truncate_if_exists: if true, the file is truncated to zero size
  6069. > STACK_0: pathname
  6070. < result: open file handle */
  6071. local inline Handle open_output_file (char* pathstring, bool wronly,
  6072. bool truncate_if_exists) {
  6073. #ifdef UNIX
  6074. begin_system_call();
  6075. var int flags = O_BINARY | O_CREAT | (truncate_if_exists ? O_TRUNC : 0);
  6076. /* regular file or !wronly => O_RDWR
  6077. i.e., for the handle to be O_WRONLY, it must be opened :DIRECTION :OUTPUT
  6078. AND the underlying file must be special (pipe &c)
  6079. see bug #[ 1379620 ]: open FIFOs with write-only access for IPC
  6080. see Stevens, UNIX Network Programming, vol 2 (IPC), ch 4 (pipes & FIFOs)*/
  6081. if (wronly) { /* regular (regular_handle_p) => ignore wronly for buffering */
  6082. var struct stat statbuf;
  6083. if (stat(pathstring,&statbuf) ||
  6084. S_ISREG(statbuf.st_mode) || S_ISBLK(statbuf.st_mode))
  6085. flags |= O_RDWR; /* not exists or regular => read-write */
  6086. else flags |= O_WRONLY; /* special => write-only */
  6087. } else flags |= O_RDWR;
  6088. var int result = OPEN(pathstring,flags,my_open_mask);
  6089. end_system_call();
  6090. if (result<0) { OS_file_error(STACK_0); } /* report error */
  6091. return result;
  6092. #endif
  6093. #ifdef WIN32_NATIVE
  6094. begin_system_call();
  6095. var Handle handle = /* ignore wronly: no "special" files where it may hurt */
  6096. CreateFile(pathstring, GENERIC_READ | GENERIC_WRITE,
  6097. FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
  6098. (truncate_if_exists ? CREATE_ALWAYS : OPEN_ALWAYS),
  6099. FILE_ATTRIBUTE_NORMAL, NULL);
  6100. end_system_call();
  6101. if (handle==INVALID_HANDLE_VALUE) { OS_file_error(STACK_0); }
  6102. return handle;
  6103. #endif
  6104. }
  6105. local inline Handle open_output_file_obj (object namestring, bool wronly,
  6106. bool truncate_if_exists) {
  6107. Handle ret;
  6108. with_sstring_0(namestring,O(pathname_encoding),namestring_asciz, {
  6109. ret = open_output_file(namestring_asciz,wronly,truncate_if_exists);
  6110. });
  6111. return ret;
  6112. }
  6113. #endif
  6114. /* Create a backup file before opening a file for output.
  6115. create_backup_file(pathstring,delete_backup_file);
  6116. > only after: assure_dir_exists()
  6117. > pathstring: file name, ASCIZ-String
  6118. > delete_backup_file: if true, delete the backup file
  6119. > STACK_0: pathname
  6120. Can trigger GC */
  6121. local inline maygc void create_backup_file (char* pathstring,
  6122. bool delete_backup_file) {
  6123. var object filename = STACK_0;
  6124. check_rename_open(filename); /* do not rename open files! */
  6125. var object new_namestring;
  6126. #if defined(UNIX) || defined(WIN32_NATIVE)
  6127. /* extend truename with "%" resp. ".bak" resp. "~" :
  6128. filename := (parse-namestring (concatenate 'string (namestring filename) "%")) : */
  6129. filename = whole_namestring(filename); /* as String */
  6130. pushSTACK(filename); pushSTACK(O(backupextend_string)); /* "%" */
  6131. filename = string_concat(2); /* concatenate */
  6132. pushSTACK(filename); /* save */
  6133. pushSTACK(filename); /* save */
  6134. filename = coerce_pathname(filename); /* again as filename */
  6135. check_delete_open(filename);
  6136. STACK_1 = filename;
  6137. /* directory already exists. Do not resolve further links here. */
  6138. new_namestring = popSTACK(); /* filename for the operating system */
  6139. #endif
  6140. with_sstring_0(new_namestring,O(pathname_encoding),new_namestring_asciz, {
  6141. /* delete file (or link) with this name, if existing: */
  6142. delete_file_before_rename(new_namestring_asciz);
  6143. /* rename file from the old name to this name: */
  6144. rename_existing_file(pathstring,new_namestring_asciz);
  6145. if (delete_backup_file) { delete_existing_file(new_namestring_asciz); }
  6146. });
  6147. skipSTACK(1);
  6148. }
  6149. local inline maygc void create_backup_file_obj
  6150. (object namestring, bool delete_backup_file) {
  6151. with_sstring_0(namestring,O(pathname_encoding),namestring_asciz,
  6152. { create_backup_file(namestring_asciz,delete_backup_file); });
  6153. }
  6154. /* check the :DIRECTION argument */
  6155. global direction_t check_direction (object dir) {
  6156. if (!boundp(dir) || eq(dir,S(Kinput)))
  6157. return DIRECTION_INPUT;
  6158. else if (eq(dir,S(Kinput_immutable)))
  6159. return DIRECTION_INPUT_IMMUTABLE;
  6160. else if (eq(dir,S(Koutput)))
  6161. return DIRECTION_OUTPUT;
  6162. else if (eq(dir,S(Kio)))
  6163. return DIRECTION_IO;
  6164. else if (eq(dir,S(Kprobe)))
  6165. return DIRECTION_PROBE;
  6166. else {
  6167. pushSTACK(dir); /* TYPE-ERROR slot DATUM */
  6168. pushSTACK(O(type_direction)); /* TYPE-ERROR slot EXPECTED-TYPE */
  6169. pushSTACK(dir); pushSTACK(S(Kdirection));
  6170. pushSTACK(TheSubr(subr_self)->name);
  6171. error(type_error,GETTEXT("~S: illegal ~S argument ~S"));
  6172. }
  6173. }
  6174. local object direction_symbol (direction_t direction) {
  6175. switch (direction) {
  6176. case DIRECTION_INPUT: { return S(Kinput); }
  6177. case DIRECTION_INPUT_IMMUTABLE: { return S(Kinput_immutable); }
  6178. case DIRECTION_OUTPUT: { return S(Koutput); }
  6179. case DIRECTION_IO: { return S(Kio); }
  6180. case DIRECTION_PROBE: { return S(Kprobe); }
  6181. default: NOTREACHED;
  6182. }
  6183. }
  6184. /* check the :IF-DOES-NOT-EXIST argument
  6185. check_if_does_not_exist(argument) */
  6186. global if_does_not_exist_t check_if_does_not_exist (object if_not_exist) {
  6187. if (!boundp(if_not_exist))
  6188. return IF_DOES_NOT_EXIST_UNBOUND;
  6189. else if (eq(if_not_exist,S(Kerror)))
  6190. return IF_DOES_NOT_EXIST_ERROR;
  6191. else if (nullp(if_not_exist))
  6192. return IF_DOES_NOT_EXIST_NIL;
  6193. else if (eq(if_not_exist,S(Kcreate)))
  6194. return IF_DOES_NOT_EXIST_CREATE;
  6195. else {
  6196. pushSTACK(if_not_exist); /* TYPE-ERROR slot DATUM */
  6197. pushSTACK(O(type_if_does_not_exist)); /* TYPE-ERROR slot EXPECTED-TYPE */
  6198. pushSTACK(if_not_exist); pushSTACK(S(Kif_does_not_exist));
  6199. pushSTACK(S(open));
  6200. error(type_error,GETTEXT("~S: illegal ~S argument ~S"));
  6201. }
  6202. }
  6203. /* Converts a :IF-DOES-NOT-EXIST enum item to a symbol.
  6204. if_does_not_exist_symbol(item)*/
  6205. global object if_does_not_exist_symbol (if_does_not_exist_t if_not_exist) {
  6206. switch (if_not_exist) {
  6207. case IF_DOES_NOT_EXIST_UNBOUND: { return unbound; }
  6208. case IF_DOES_NOT_EXIST_ERROR: { return S(Kerror); }
  6209. case IF_DOES_NOT_EXIST_NIL: { return NIL; }
  6210. case IF_DOES_NOT_EXIST_CREATE: { return S(Kcreate); }
  6211. }
  6212. NOTREACHED;
  6213. }
  6214. /* check the :IF-EXISTS argument
  6215. check_if_exists(argument) */
  6216. global if_exists_t check_if_exists (object if_exists) {
  6217. if (!boundp(if_exists))
  6218. return IF_EXISTS_UNBOUND;
  6219. else if (eq(if_exists,S(Kerror)))
  6220. return IF_EXISTS_ERROR;
  6221. else if (nullp(if_exists))
  6222. return IF_EXISTS_NIL;
  6223. else if (eq(if_exists,S(Krename)))
  6224. return IF_EXISTS_RENAME;
  6225. else if (eq(if_exists,S(Krename_and_delete)))
  6226. return IF_EXISTS_RENAME_AND_DELETE;
  6227. else if (eq(if_exists,S(Knew_version)) || eq(if_exists,S(Ksupersede)))
  6228. return IF_EXISTS_SUPERSEDE;
  6229. else if (eq(if_exists,S(Kappend)))
  6230. return IF_EXISTS_APPEND;
  6231. else if (eq(if_exists,S(Koverwrite)))
  6232. return IF_EXISTS_OVERWRITE;
  6233. else {
  6234. pushSTACK(if_exists); /* TYPE-ERROR slot DATUM */
  6235. pushSTACK(O(type_if_exists)); /* TYPE-ERROR slot EXPECTED-TYPE */
  6236. pushSTACK(if_exists); pushSTACK(S(Kif_exists)); pushSTACK(S(open));
  6237. error(type_error,GETTEXT("~S: illegal ~S argument ~S"));
  6238. }
  6239. }
  6240. /* Converts a :IF-EXISTS enum item to a symbol.
  6241. if_exists_symbol(item) */
  6242. global object if_exists_symbol (if_exists_t if_exists) {
  6243. switch (if_exists) { /* :IF-EXISTS */
  6244. case IF_EXISTS_UNBOUND: { return unbound; }
  6245. case IF_EXISTS_ERROR: { return S(Kerror); }
  6246. case IF_EXISTS_NIL: { return NIL; }
  6247. case IF_EXISTS_RENAME: { return S(Krename); }
  6248. case IF_EXISTS_RENAME_AND_DELETE: { return S(Krename_and_delete); }
  6249. case IF_EXISTS_SUPERSEDE: { return S(Ksupersede); }
  6250. case IF_EXISTS_APPEND: { return S(Kappend); }
  6251. case IF_EXISTS_OVERWRITE: { return S(Koverwrite); }
  6252. }
  6253. NOTREACHED;
  6254. }
  6255. /* UP: check that the file we are about to open has not been opened yet
  6256. > object truename - the name of the file that is being opened
  6257. > direction_t direction - the direction of the pending OPEN
  6258. can trigger GC - if CERROR is signaled */
  6259. extern void* find_open_file (struct file_id *fid, void* data);
  6260. local maygc void check_file_re_open (object truename, direction_t direction) {
  6261. var uintB flags;
  6262. switch (direction) {
  6263. case DIRECTION_INPUT_IMMUTABLE: case DIRECTION_INPUT:
  6264. flags = strmflags_wr_B;
  6265. break;
  6266. case DIRECTION_IO: case DIRECTION_OUTPUT:
  6267. flags = (strmflags_rd_B | strmflags_wr_B);
  6268. break;
  6269. default: return; /* PROBE: nothing to check */
  6270. }
  6271. var object bad_stream = nullobj;
  6272. with_string_0(truename,O(pathname_encoding),namez, {
  6273. begin_system_call();
  6274. var void *ret = with_file_id(namez,(void*)&flags,&find_open_file);
  6275. end_system_call();
  6276. if (ret) bad_stream = popSTACK();
  6277. });
  6278. if (!eq(bad_stream,nullobj)) { /* found an existing open stream */
  6279. pushSTACK(NIL); /* 8: continue-format-string */
  6280. pushSTACK(S(file_error)); /* 7: error type */
  6281. pushSTACK(S(Kpathname)); /* 6: :PATHNAME */
  6282. pushSTACK(truename); /* 5: the offending pathname */
  6283. pushSTACK(NIL); /* 4: error-format-string */
  6284. pushSTACK(TheSubr(subr_self)->name); /* 3: caller */
  6285. pushSTACK(bad_stream); /* 2: bad stream */
  6286. pushSTACK(truename); /* 1: truename */
  6287. pushSTACK(direction_symbol(direction)); /* 0: direction */
  6288. STACK_8 = CLSTEXT("Open the file anyway"); /* continue-format-string */
  6289. STACK_4 = CLSTEXT("~S: ~S already points to file ~S, opening the file again for ~S may produce unexpected results"); /* error-format-string */
  6290. funcall(L(cerror_of_type),9);
  6291. }
  6292. }
  6293. /* UP: create a file-stream
  6294. open_file(filename,direction,if_exists,if_not_exists)
  6295. > STACK_3: original filename (may be logical)
  6296. > STACK_2: :BUFFERED argument
  6297. > STACK_1: :EXTERNAL-FORMAT argument
  6298. > STACK_0: :ELEMENT-TYPE argument
  6299. > filename: filename, a pathname
  6300. > direction: direction_t (see lispbibl.d)
  6301. > if_exists: :IF-EXISTS argument if_exists_t (see lispbibl.d)
  6302. > if_not_exists: :IF-DOES-NOT-EXIST argument (see lispbibl.d)
  6303. < result: Stream or NIL
  6304. < STACK: cleaned up
  6305. can trigger GC */
  6306. local maygc object open_file (object filename, direction_t direction,
  6307. if_exists_t if_exists,
  6308. if_does_not_exist_t if_not_exists) {
  6309. pushSTACK(NIL); /* reserve space on STACK for namestring ... */
  6310. var gcv_object_t *namestring_ = &STACK_0; /* ... and remember it */
  6311. pushSTACK(STACK_(3+1)); /* save filename */
  6312. /* Directory must exist: */
  6313. pushSTACK(filename);
  6314. var struct file_status fs; file_status_init(&fs,&STACK_0);
  6315. /* tolerant only if :PROBE and if_not_exists = UNBOUND or NIL */
  6316. true_namestring(&fs,true,
  6317. ((direction == DIRECTION_PROBE)
  6318. && (if_not_exists == IF_DOES_NOT_EXIST_UNBOUND))
  6319. || (if_not_exists == IF_DOES_NOT_EXIST_NIL));
  6320. if (eq(fs.fs_namestring,nullobj))
  6321. /* path to the file does not exist,
  6322. and :IF-DOES-NOT-EXIST = unbound or NIL */
  6323. goto result_NIL;
  6324. *namestring_ = fs.fs_namestring;
  6325. /* stack layout: Namestring, Pathname, Truename
  6326. check filename and get the handle: */
  6327. check_file_re_open(*namestring_,direction);
  6328. var object handle;
  6329. {var bool append_flag = false;
  6330. var bool wronly_flag = false;
  6331. switch (direction) {
  6332. case DIRECTION_PROBE:
  6333. if (!file_exists(&fs)) { /* file does not exist */
  6334. /* :IF-DOES-NOT-EXIST decides: */
  6335. if (if_not_exists==IF_DOES_NOT_EXIST_ERROR)
  6336. goto error_notfound;
  6337. if (if_not_exists==IF_DOES_NOT_EXIST_UNBOUND
  6338. || if_not_exists==IF_DOES_NOT_EXIST_NIL)
  6339. goto result_NIL;
  6340. /* :CREATE -> create the file using open and close: */
  6341. with_sstring_0(*namestring_,O(pathname_encoding),namestring_asciz, {
  6342. create_new_file(namestring_asciz);
  6343. });
  6344. }
  6345. { handle = NIL; } /* Handle := NIL */
  6346. break;
  6347. case DIRECTION_INPUT: case DIRECTION_INPUT_IMMUTABLE: { /* == :INPUT */
  6348. var Handle handl;
  6349. var bool result;
  6350. with_sstring_0(*namestring_,O(pathname_encoding),namestring_asciz, {
  6351. result = open_input_file(&fs,namestring_asciz,
  6352. if_not_exists==IF_DOES_NOT_EXIST_CREATE,
  6353. &handl);
  6354. });
  6355. if (!result) {
  6356. /* :IF-DOES-NOT-EXIST decides: */
  6357. if (if_not_exists==IF_DOES_NOT_EXIST_NIL)
  6358. goto result_NIL;
  6359. else /* UNBOUND or :ERROR -> Error */
  6360. goto error_notfound;
  6361. }
  6362. handle = allocate_handle(handl);
  6363. } break;
  6364. case DIRECTION_OUTPUT: wronly_flag = true; /*FALLTHROUGH*/
  6365. case DIRECTION_IO:
  6366. /* default for if_not_exists depends on if_exists: */
  6367. if (if_not_exists==IF_DOES_NOT_EXIST_UNBOUND) {
  6368. if (if_exists!=IF_EXISTS_APPEND && if_exists!=IF_EXISTS_OVERWRITE)
  6369. /* (if_exists<IF_EXISTS_APPEND)
  6370. if_exists = :APPEND or :OVERWRITE -> if_not_exists unchanged,
  6371. otherwise :CREATE is the default */
  6372. if_not_exists = IF_DOES_NOT_EXIST_CREATE;
  6373. }
  6374. /* default for if_exists is :SUPERSEDE (= :NEW-VERSION) : */
  6375. if (if_exists==IF_EXISTS_UNBOUND)
  6376. if_exists = IF_EXISTS_SUPERSEDE;
  6377. #if defined(UNIX) || defined(WIN32_NATIVE)
  6378. if (file_exists(&fs)) {
  6379. /* file exists => :IF-EXISTS decides: */
  6380. switch (if_exists) {
  6381. case IF_EXISTS_ERROR:
  6382. goto error_exists;
  6383. case IF_EXISTS_NIL:
  6384. goto result_NIL;
  6385. case IF_EXISTS_RENAME: case IF_EXISTS_RENAME_AND_DELETE:
  6386. create_backup_file_obj(*namestring_,
  6387. if_exists==IF_EXISTS_RENAME_AND_DELETE);
  6388. break;
  6389. case IF_EXISTS_APPEND:
  6390. append_flag = true; /* position at the end */
  6391. default: ;
  6392. /* :OVERWRITE -> use the existing file
  6393. :NEW-VERSION, :SUPERSEDE -> truncate the file at 0 length */
  6394. }
  6395. } else { /* file does not exist => :IF-DOES-NOT-EXIST decides: */
  6396. if (if_not_exists==IF_DOES_NOT_EXIST_UNBOUND
  6397. || if_not_exists==IF_DOES_NOT_EXIST_ERROR)
  6398. goto error_notfound;
  6399. if (if_not_exists==IF_DOES_NOT_EXIST_NIL)
  6400. goto result_NIL;
  6401. /* :CREATE */
  6402. }
  6403. /* open file:
  6404. if-exists: if if_exists<IF_EXISTS_APPEND delete contents;
  6405. othersise (with :APPEND, :OVERWRITE) preserve contents.
  6406. if-not-exists: create new file. */
  6407. { handle = allocate_handle(open_output_file_obj
  6408. (*namestring_,wronly_flag,
  6409. (if_exists!=IF_EXISTS_APPEND
  6410. && if_exists!=IF_EXISTS_OVERWRITE))); }
  6411. #endif
  6412. break;
  6413. default: NOTREACHED;
  6414. /* STACK_0 = Truename, FILE-ERROR slot PATHNAME */
  6415. error_notfound: /* error: file not found */
  6416. error_file_not_exists();
  6417. error_exists: /* error: file already exists */
  6418. error_file_exists();
  6419. }
  6420. handle_ok:
  6421. /* handle and append_flag are done with.
  6422. make the Stream: */
  6423. pushSTACK(STACK_5); /* :BUFFERED argument */
  6424. pushSTACK(STACK_5); /* :EXTERNAL-FORMAT argument */
  6425. pushSTACK(STACK_5); /* :ELEMENT-TYPE argument */
  6426. pushSTACK(handle);
  6427. {var object stream = make_file_stream(direction,append_flag,true);
  6428. skipSTACK(5);
  6429. return stream;
  6430. }}
  6431. result_NIL: /* return NIL */
  6432. skipSTACK(7); /* forget both Pathnames and three arguments */
  6433. return NIL;
  6434. }
  6435. /* (OPEN filename :direction :element-type :if-exists :if-does-not-exist
  6436. :external-format :buffered) */
  6437. LISPFUN(open,seclass_default,1,0,norest,key,6,
  6438. (kw(direction),kw(element_type),kw(if_exists),
  6439. kw(if_does_not_exist),kw(external_format),kw(buffered)) ) {
  6440. var object filename = STACK_6; /* filename */
  6441. if (builtin_stream_p(filename)) {
  6442. /* must be file-stream: */
  6443. filename = as_file_stream(filename);
  6444. test_file_stream_named(filename);
  6445. /* streamtype file-stream -> use truename: */
  6446. filename = TheStream(filename)->strm_file_truename;
  6447. pushSTACK(filename);
  6448. } else {
  6449. filename = coerce_xpathname(filename); /* turn into a pathname */
  6450. pushSTACK(filename);
  6451. #ifdef LOGICAL_PATHNAMES
  6452. /* Convert from logical to physical pathname: */
  6453. if (logpathnamep(filename))
  6454. filename = coerce_pathname(filename);
  6455. #endif
  6456. filename = merge_defaults(filename);
  6457. }
  6458. /* Stack layout: filename-arg, direction, element-type, if-exists,
  6459. if-does-not-exist, external-format, buffered, origpathname.
  6460. filename is now a pathname. */
  6461. var direction_t direction = check_direction(STACK_(5+1));
  6462. var if_exists_t if_exists = check_if_exists(STACK_(3+1));
  6463. var if_does_not_exist_t if_not_exists=check_if_does_not_exist(STACK_(2+1));
  6464. /* :element-type is checked later.
  6465. :external-format is checked later.
  6466. :buffered is checked later.
  6467. open file: */
  6468. STACK_4 = STACK_5; STACK_5 = STACK_2; STACK_6 = STACK_1; STACK_7 = STACK_0;
  6469. skipSTACK(4);
  6470. VALUES1(open_file(filename,direction,if_exists,if_not_exists));
  6471. }
  6472. /* UP: Returns a list of all matching pathnames.
  6473. directory_search(pathname,dir_search_param)
  6474. > pathname: pathname with device /= :WILD
  6475. > dir_search_param: :if-does-not-exist, :circle flag, :full flag
  6476. < result:
  6477. if name=NIL and type=NIL: list of all matching directories,
  6478. else (name=NIL -> name=:WILD): list of all matching files.
  6479. as absolute pathname without wildcards at a time,
  6480. resp. for files and Full-Flag /=NIL as list
  6481. (Pathname Write-Date Length)
  6482. with Pathname without :WILD/:WILD-INFERIORS-components,
  6483. Write-Date = Date of file creation (ss mm hh dd mm yy),
  6484. as Decoded-Time suitable for ENCODE-UNIVERSAL-TIME,
  6485. Length = Length of the file (in Bytes).
  6486. Method: Breadth-first-search (=> only one search operation runs at a time)
  6487. can trigger GC */
  6488. typedef enum {
  6489. DIR_IF_NONE_DISCARD, DIR_IF_NONE_ERROR, DIR_IF_NONE_KEEP, DIR_IF_NONE_IGNORE
  6490. } dir_search_if_none_t;
  6491. typedef struct {
  6492. dir_search_if_none_t if_none;
  6493. bool full_p;
  6494. bool circle_p;
  6495. } dir_search_param_t;
  6496. local maygc object directory_search (object pathname, dir_search_param_t *dsp);
  6497. /* UP: extends a pathname by the file-information.
  6498. > STACK_1: absolute pathname
  6499. > STACK_0: absolute pathname, links resolved
  6500. > timepoint: decoded mtime
  6501. > entry_size: file size
  6502. < replace STACK_0 with :FULL info:
  6503. (Pathname Truename Write-Date Length [Comment])
  6504. can trigger GC */
  6505. local maygc void pack_full_info (decoded_time_t *timepoint, off_t *entry_size) {
  6506. var object newlist;
  6507. /* Pathname already in STACK_1, as 1st list element
  6508. Truename already in STACK_0, as 2nd list element */
  6509. pushSTACK(timepoint->seconds);
  6510. pushSTACK(timepoint->minutes);
  6511. pushSTACK(timepoint->hours);
  6512. pushSTACK(timepoint->day);
  6513. pushSTACK(timepoint->month);
  6514. pushSTACK(timepoint->year);
  6515. newlist = listof(6); /* build 6-element list */
  6516. pushSTACK(newlist); /* as 3rd list element */
  6517. pushSTACK(off_to_I(*entry_size)); /* length as 4th list element */
  6518. newlist = listof(4); /* build 4-element list */
  6519. pushSTACK(Car(newlist)); /* pathname again in the STACK */
  6520. pushSTACK(newlist); /* list in the STACK */
  6521. }
  6522. #ifdef WIN32_NATIVE
  6523. /* Set of macros for directory search. */
  6524. #define READDIR_wildnametype_suffix O(wild_string) /* "*" */
  6525. #define READDIR_var_declarations \
  6526. var WIN32_FIND_DATA filedata; \
  6527. var HANDLE search_handle;
  6528. #define READDIR_end_declarations
  6529. #define READDIR_findfirst(pathstring,error_statement,done_statement) \
  6530. if ((search_handle = FindFirstFile(pathstring,&filedata)) \
  6531. == INVALID_HANDLE_VALUE) { \
  6532. if (!WIN32_ERROR_NOT_FOUND) { error_statement } \
  6533. else { done_statement } \
  6534. }
  6535. #define READDIR_findnext(error_statement,done_statement) \
  6536. if (!FindNextFile(search_handle,&filedata)) { \
  6537. if (!(GetLastError()==ERROR_NO_MORE_FILES) \
  6538. || !FindClose(search_handle)) \
  6539. { error_statement } \
  6540. else { done_statement } \
  6541. }
  6542. #define READDIR_entry_name() (&filedata.cFileName[0])
  6543. #define READDIR_entry_ISDIR() (filedata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  6544. #define READDIR_entry_timedate(timepointp) \
  6545. { var FILETIME* pftimepoint = &filedata.ftLastWriteTime; \
  6546. if (pftimepoint->dwLowDateTime==0 && pftimepoint->dwHighDateTime==0) \
  6547. pftimepoint = &filedata.ftCreationTime; \
  6548. convert_time(pftimepoint,timepointp); \
  6549. }
  6550. #define READDIR_entry_size() \
  6551. (((uint64)filedata.nFileSizeHigh<<32)|filedata.nFileSizeLow)
  6552. /* UP: get mtime and size from filesystem
  6553. > pathname: absolute pathname, links resolved
  6554. < timepoint: decoded time
  6555. < entry_size: file size
  6556. can trigger GC */
  6557. local maygc void get_time_size (gcv_object_t *pathname,
  6558. decoded_time_t *timepoint, off_t *entry_size) {
  6559. READDIR_var_declarations;
  6560. with_sstring_0(whole_namestring(*pathname),O(pathname_encoding),
  6561. resolved_asciz,{
  6562. var bool notfound = false;
  6563. /* strip trailing slash,
  6564. see http://msdn2.microsoft.com/en-us/library/aa364418.aspx */
  6565. if (resolved_asciz[resolved_asciz_bytelen - 1] == '\\')
  6566. resolved_asciz[resolved_asciz_bytelen - 1] = 0;
  6567. begin_system_call();
  6568. READDIR_findfirst(resolved_asciz, notfound = true; , notfound = true; );
  6569. end_system_call();
  6570. if (notfound) /* just to be paranoid */
  6571. OS_file_error(*pathname);
  6572. begin_system_call(); FindClose(search_handle); end_system_call();
  6573. READDIR_entry_timedate(timepoint);
  6574. *entry_size = READDIR_entry_size();
  6575. });
  6576. READDIR_end_declarations;
  6577. }
  6578. local maygc void with_stat_info_computed (struct file_status *fs) {
  6579. decoded_time_t timepoint;
  6580. off_t entry_size;
  6581. get_time_size(fs->fs_pathname,&timepoint,&entry_size);
  6582. pack_full_info(&timepoint,&entry_size);
  6583. }
  6584. #endif
  6585. #ifdef UNIX
  6586. /* Just like stat(), except that directories or files which would lead
  6587. to problems are silently hidden. */
  6588. local inline int stat_for_search (char* pathstring, struct stat * statbuf) {
  6589. #ifdef UNIX_LINUX
  6590. /* Avoid searching /proc: It is a zoo containing strange animals:
  6591. directories which go away constantly, pseudo-regular files which
  6592. are really pipes, etc. */
  6593. if (asciz_equal(pathstring,"/proc")) { errno = ENOENT; return -1; }
  6594. #endif
  6595. var int result = stat(pathstring,statbuf);
  6596. #ifdef UNIX_CYGWIN32
  6597. if ((result < 0) && (errno == EACCES)) { errno = ENOENT; }
  6598. #endif
  6599. return result;
  6600. }
  6601. #endif
  6602. #ifdef PATHNAME_NOEXT
  6603. /* UP: Extends the directory of a pathname by one component.
  6604. > pathname: a pathname
  6605. > subdir: new Subdir-component, a Simple-String
  6606. < result: new pathname with directory lengthened by subdir
  6607. can trigger GC */
  6608. local maygc object pathname_add_subdir (object pathname, object subdir) {
  6609. pushSTACK(pathname); pushSTACK(subdir);
  6610. /* copy pathname and lengthen its directory according to
  6611. (append x (list y)) = (nreverse (cons y (reverse x))) : */
  6612. pathname = copy_pathname(STACK_1);
  6613. STACK_1 = pathname;
  6614. pushSTACK(reverse(ThePathname(pathname)->pathname_directory));
  6615. var object new_cons = allocate_cons();
  6616. Cdr(new_cons) = popSTACK();
  6617. Car(new_cons) = popSTACK();
  6618. new_cons = nreverse(new_cons);
  6619. pathname = popSTACK();
  6620. ThePathname(pathname)->pathname_directory = new_cons;
  6621. return pathname;
  6622. }
  6623. #ifdef UNIX
  6624. /* UP: extends a pathname by the file-information.
  6625. > STACK_1: absolute pathname
  6626. > STACK_0: absolute pathname, links resolved
  6627. > *filestatus: its stat-info
  6628. < STACK_0: list (Pathname Truename Write-Date Length [Comment])
  6629. in :FULL-Format */
  6630. local void with_stat_info (struct stat *filestatus) {
  6631. var decoded_time_t timepoint; /* Write-Date in decoded form */
  6632. convert_time(&(filestatus->st_mtime),&timepoint);
  6633. pack_full_info(&timepoint,&(filestatus->st_size));
  6634. }
  6635. local void with_stat_info_computed (struct file_status *fs) {
  6636. if (!fs->fs_stat_validp) {
  6637. if (stat_obj(whole_namestring(*(fs->fs_pathname)),&(fs->fs_stat)) < 0)
  6638. OS_file_error(*(fs->fs_pathname));
  6639. fs->fs_stat_validp = true;
  6640. }
  6641. with_stat_info(&(fs->fs_stat));
  6642. }
  6643. #endif
  6644. /* push object in front of a list
  6645. can trigger GC */
  6646. local inline maygc void push (gcv_object_t *head, gcv_object_t *tail) {
  6647. var object new_cons = allocate_cons();
  6648. Car(new_cons) = *head;
  6649. Cdr(new_cons) = *tail;
  6650. *tail = new_cons;
  6651. }
  6652. #define PUSH_ON_STACK(h,t) push(&STACK_(h),&STACK_(t))
  6653. /* Search for a subdirectory with a given name.
  6654. directory_search_1subdir(subdir,namestring);
  6655. > STACK_0 = pathname
  6656. > STACK_(3+1) = new-pathname-list
  6657. > subdir: the new directory component to add to the pathname, if it exists
  6658. > namestring: the namestring (for the OS)
  6659. < STACK_0: replaced
  6660. < STACK_(3+1): augmented
  6661. can trigger GC */
  6662. local maygc void copy_pathname_and_add_subdir (object subdir)
  6663. { /* copy pathname(STACK_0) and lengthen its directory by subdir: */
  6664. STACK_0 = pathname_add_subdir(STACK_0,subdir);
  6665. /* push this new pathname in front of new-pathname-list: */
  6666. PUSH_ON_STACK(0,3+1);
  6667. }
  6668. /* Check whether a directory exists and call copy_pathname_and_add_subdir()
  6669. on it; if the directory does not exist or is a file, do nothing */
  6670. local maygc void check_sub_directory (object subdir, char* namestring_asciz) {
  6671. #if defined(UNIX)
  6672. struct stat status;
  6673. int ret;
  6674. begin_system_call(); ret = stat(namestring_asciz,&status); end_system_call();
  6675. if (ret) {
  6676. if (errno != ENOENT) /* subdirectory does not exist -> OK. */
  6677. OS_file_error(STACK_0);
  6678. } else { /* file exists. */
  6679. if (S_ISDIR(status.st_mode)) /* is it a directory? */
  6680. copy_pathname_and_add_subdir(subdir);
  6681. }
  6682. #elif defined(WIN32_NATIVE)
  6683. char resolved[MAX_PATH];
  6684. if (real_path(namestring_asciz,resolved)) {
  6685. DWORD fileattr;
  6686. begin_system_call();
  6687. fileattr = GetFileAttributes(resolved);
  6688. end_system_call();
  6689. if (fileattr == 0xFFFFFFFF) {
  6690. /* you get ERROR_INVALID_NAME on GetFileAttributes("foo/")
  6691. when file "foo" exists */
  6692. if (!(WIN32_ERROR_NOT_FOUND || GetLastError() == ERROR_INVALID_NAME))
  6693. OS_file_error(STACK_0);
  6694. } else { /* file exists. */
  6695. if (fileattr & FILE_ATTRIBUTE_DIRECTORY) /* is it a directory? */
  6696. copy_pathname_and_add_subdir(subdir);
  6697. }
  6698. }
  6699. #endif
  6700. }
  6701. local maygc void directory_search_1subdir (object subdir, object namestring) {
  6702. with_sstring_0(namestring,O(pathname_encoding),namestring_asciz, {
  6703. check_sub_directory(subdir,namestring_asciz);
  6704. });
  6705. }
  6706. #if defined(UNIX) || defined(WIN32_NATIVE)
  6707. /* Returns a truename dependent hash code for a directory.
  6708. directory_search_hashcode()
  6709. STACK_0 = dir_namestring
  6710. STACK_1 = pathname
  6711. < result: a hash code, or nullobj if the directory does not exist
  6712. can trigger GC */
  6713. #ifdef UNIX
  6714. /* return (cons drive inode) */
  6715. local maygc object directory_search_hashcode (void) {
  6716. pushSTACK(STACK_0); /* Directory-Name */
  6717. pushSTACK(O(dot_string)); /* and "." */
  6718. var object namestring = string_concat(2); /* concatenate */
  6719. var struct stat status;
  6720. if (stat_obj(namestring,&status) != 0) return nullobj;
  6721. /* entry exists (oh miracle...) */
  6722. pushSTACK(UL_to_I(status.st_dev)); /* Device-Number and */
  6723. #if SIZEOF_INO_T > 4
  6724. pushSTACK(UQ_to_I(status.st_ino)); /* Inode-Number */
  6725. #else
  6726. pushSTACK(UL_to_I(status.st_ino)); /* Inode-Number */
  6727. #endif
  6728. var object new_cons = allocate_cons(); /* cons them together */
  6729. Cdr(new_cons) = popSTACK(); Car(new_cons) = popSTACK();
  6730. return new_cons;
  6731. }
  6732. #else
  6733. /* win32 - there is stat but no inodes
  6734. using directory truenames as hashcodes */
  6735. local maygc object directory_search_hashcode (void) {
  6736. return STACK_0;
  6737. }
  6738. #endif
  6739. #endif
  6740. #ifdef UNIX
  6741. /* Tests whether a directory entry actually exists.
  6742. (It could be a link pointing to nowhere, or an undesired directory.)
  6743. directory_search_direntry_ok(namestring,&statbuf)
  6744. STACK_2 = pathname
  6745. < result: true and statbuf filled, or false. */
  6746. local maygc bool directory_search_direntry_ok (object namestring,
  6747. struct stat * statbuf) {
  6748. var bool exists = true;
  6749. with_sstring_0(namestring,O(pathname_encoding),namestring_asciz, {
  6750. begin_system_call();
  6751. if (!( stat_for_search(namestring_asciz,statbuf) ==0)) {
  6752. if (!((errno==ENOENT) || (errno==ELOOP_VALUE))) {
  6753. end_system_call(); OS_file_error(STACK_2);
  6754. }
  6755. end_system_call();
  6756. exists = false;
  6757. }
  6758. end_system_call();
  6759. });
  6760. return exists;
  6761. }
  6762. #endif
  6763. /* the version of files returned by DIRECTORY
  6764. Since all pathnames returned by DIRECTORY must be truenames,
  6765. this must be :NEWEST [but then they will not be printable readably!] */
  6766. #define DEFAULT_VERSION S(Knewest)
  6767. /* Scans an entire directory.
  6768. directory_search_scandir(recursively,next_task);
  6769. stack layout: result-list, pathname, name&type, subdir-list, pathname-list,
  6770. new-pathname-list, ht, pathname-list-rest, pathnames-to-insert,
  6771. pathname, dir_namestring. */
  6772. local maygc void directory_search_scandir (bool recursively, signean next_task,
  6773. dir_search_param_t *dsp) {
  6774. #ifdef UNIX
  6775. {
  6776. var object namestring;
  6777. pushSTACK(STACK_0); /* directory-name */
  6778. pushSTACK(O(dot_string)); /* and "." */
  6779. namestring = string_concat(2); /* concatenate */
  6780. /* scan directory: */
  6781. var DIR* dirp;
  6782. set_break_sem_4();
  6783. with_sstring_0(namestring,O(pathname_encoding),namestring_asciz, {
  6784. begin_system_call();
  6785. dirp = opendir(namestring_asciz); /* open directory */
  6786. end_system_call();
  6787. });
  6788. if (dirp == (DIR*)NULL) {
  6789. if (dsp->if_none == DIR_IF_NONE_IGNORE) return;
  6790. else OS_file_error(STACK_1);
  6791. }
  6792. while (1) {
  6793. var SDIRENT* dp;
  6794. begin_system_call();
  6795. errno = 0;
  6796. dp = readdir(dirp); /* fetch next directory-entry */
  6797. if (dp == (SDIRENT*)NULL) { /* error or directory finished */
  6798. if (!(errno==0)) { end_system_call(); OS_file_error(STACK_1); }
  6799. end_system_call();
  6800. break;
  6801. }
  6802. end_system_call();
  6803. /* convert directory-entry into string: */
  6804. var object direntry;
  6805. {
  6806. var uintL direntry_len;
  6807. #if defined(UNIX_CYGWIN32)
  6808. /* Neither d_reclen nor d_namlen present in DIR structure. */
  6809. direntry_len = asciz_length(dp->d_name);
  6810. #elif !defined(HAVE_STRUCT_DIRENT_D_NAMLEN) || defined(__USE_GNU)
  6811. { /* On UNIX_LINUX direntry_len := dp->d_reclen was sufficient, but in
  6812. general direntry_len := min(dp->d_reclen,asciz_length(dp->d_name))
  6813. is necessary. The GNU libc is buggy: it does
  6814. "#define d_namlen d_reclen", just as the Linux libc-5.0.9. */
  6815. var const uintB* ptr = (const uintB*)(&dp->d_name[0]);
  6816. var uintL count = dp->d_reclen;
  6817. direntry_len = 0;
  6818. while (count-- && *ptr++) direntry_len++;
  6819. }
  6820. #else
  6821. direntry_len = dp->d_namlen;
  6822. #endif
  6823. direntry = n_char_to_string(&dp->d_name[0],direntry_len,O(pathname_encoding));
  6824. }
  6825. /* skip "." and ".." : */
  6826. if (!(equal(direntry,O(dot_string))
  6827. || equal(direntry,O(dotdot_string)))) {
  6828. pushSTACK(direntry);
  6829. /* stack layout: ..., pathname, dir_namestring, direntry.
  6830. determine, if it is a directory or a file: */
  6831. pushSTACK(STACK_1); /* Directory-Namestring */
  6832. SUBDIR_PUSHSTACK(direntry); /* direntry */
  6833. var object namestring = string_concat(2); /* concatenate */
  6834. /* get information: */
  6835. var struct stat status;
  6836. #if 1 /* just an optimization */
  6837. if (!recursively) {
  6838. /* Try to avoid calling directory_search_direntry_ok(),
  6839. since it is an expensive operation (it calls stat()). */
  6840. if (next_task < 0) {
  6841. /* match (car subdir-list) with direntry: */
  6842. if (wildcard_match(Car(STACK_(1+4+3)),STACK_0))
  6843. if (directory_search_direntry_ok(namestring,&status)) {
  6844. if (S_ISDIR(status.st_mode))
  6845. goto push_matching_subdir;
  6846. } else
  6847. switch (dsp->if_none) {
  6848. case DIR_IF_NONE_IGNORE: case DIR_IF_NONE_DISCARD: break;
  6849. case DIR_IF_NONE_ERROR:
  6850. pushSTACK(namestring);
  6851. error_file_not_exists();
  6852. case DIR_IF_NONE_KEEP:
  6853. goto push_matching_file;
  6854. default: NOTREACHED;
  6855. }
  6856. } else if (next_task > 0) { /* match name&type with direntry: */
  6857. if (wildcard_match(STACK_(2+4+3),STACK_0))
  6858. if (directory_search_direntry_ok(namestring,&status)) {
  6859. if (!S_ISDIR(status.st_mode))
  6860. goto push_matching_file;
  6861. } else
  6862. switch (dsp->if_none) {
  6863. case DIR_IF_NONE_IGNORE: case DIR_IF_NONE_DISCARD: break;
  6864. case DIR_IF_NONE_ERROR:
  6865. pushSTACK(namestring);
  6866. error_file_not_exists();
  6867. case DIR_IF_NONE_KEEP:
  6868. goto push_matching_file;
  6869. default: NOTREACHED;
  6870. }
  6871. }
  6872. goto done_direntry;
  6873. }
  6874. #endif
  6875. if (directory_search_direntry_ok(namestring,&status)) {
  6876. /* entry exists and is not unwanted. */
  6877. if (S_ISDIR(status.st_mode)) { /* is it a directory? */
  6878. /* entry is a directory. */
  6879. if (recursively) { /* all recursive subdirectories wanted? */
  6880. /* yes -> turn into a pathname and push to pathnames-to-insert
  6881. (it is later inserted in front of pathname-list-rest): */
  6882. pushSTACK(pathname_add_subdir(STACK_2/*pathname*/,STACK_0/*direntry*/));
  6883. /* push this new pathname in front of pathname-to-insert: */
  6884. PUSH_ON_STACK(0,1+3);
  6885. skipSTACK(1);
  6886. }
  6887. if (next_task<0) {
  6888. /* match (car subdir-list) with direntry: */
  6889. if (wildcard_match(Car(STACK_(1+4+3)),STACK_0)) {
  6890. push_matching_subdir:
  6891. /* subdirectory matches -> turn into a pathname
  6892. and push onto new-pathname-list: */
  6893. pushSTACK(pathname_add_subdir(STACK_2/*pathname*/,STACK_0/*direntry*/));
  6894. /* push this new pathname in front of new-pathname-list: */
  6895. PUSH_ON_STACK(0,4+3);
  6896. skipSTACK(1);
  6897. }
  6898. }
  6899. } else { /* entry is a (halfway) normal File. */
  6900. if (next_task>0) {
  6901. /* match name&type with direntry: */
  6902. if (wildcard_match(STACK_(2+4+3),STACK_0)) {
  6903. push_matching_file:
  6904. /* File matches -> turn into a pathname
  6905. and push onto result-list: */
  6906. pushSTACK(STACK_0); /* direntry */
  6907. split_name_type(1); /* split into Name and Type */
  6908. {
  6909. var object pathname = copy_pathname(STACK_(2+2));
  6910. ThePathname(pathname)->pathname_type = popSTACK(); /* insert type */
  6911. ThePathname(pathname)->pathname_name = popSTACK(); /* insert name */
  6912. ThePathname(pathname)->pathname_version = DEFAULT_VERSION;
  6913. pushSTACK(pathname);
  6914. pushSTACK(pathname);
  6915. }
  6916. /* form truename (resolve symbolic links): */
  6917. var struct file_status fs; file_status_init(&fs,&STACK_0);
  6918. assure_dir_exists(&fs,true,true);
  6919. if (!eq(nullobj,fs.fs_namestring) && file_exists(&fs)) {
  6920. /* if file (still...) exists */
  6921. if (dsp->full_p) /* :FULL wanted? */
  6922. with_stat_info(&(fs.fs_stat)); /* yes -> extend STACK_0 */
  6923. /* and push STACK_0 in front of result-list: */
  6924. PUSH_ON_STACK(0,4+4+3+2);
  6925. } else if (dsp->if_none == DIR_IF_NONE_KEEP)
  6926. PUSH_ON_STACK(1/* unresolved pathname */,4+4+3+2);
  6927. skipSTACK(2);
  6928. }
  6929. }
  6930. }
  6931. } else
  6932. switch (dsp->if_none) {
  6933. case DIR_IF_NONE_IGNORE: case DIR_IF_NONE_DISCARD: break;
  6934. case DIR_IF_NONE_ERROR:
  6935. pushSTACK(namestring);
  6936. error_file_not_exists();
  6937. case DIR_IF_NONE_KEEP:
  6938. goto push_matching_file;
  6939. default: NOTREACHED;
  6940. }
  6941. done_direntry:
  6942. skipSTACK(1); /* forget direntry */
  6943. }
  6944. }
  6945. begin_system_call();
  6946. if (CLOSEDIR(dirp)) { end_system_call(); OS_file_error(STACK_1); }
  6947. end_system_call();
  6948. clr_break_sem_4();
  6949. }
  6950. #endif
  6951. #ifdef WIN32_NATIVE
  6952. {
  6953. SUBDIR_PUSHSTACK(STACK_0); /* Directory-Name */
  6954. pushSTACK(READDIR_wildnametype_suffix); /* and concatenate */
  6955. var object namestring = string_concat(2); /* "*.*" resp. "*" */
  6956. with_sstring_0(namestring,O(pathname_encoding),namestring_asciz, {
  6957. /* scan directory, according to DOS- resp. Win32-convention: */
  6958. READDIR_var_declarations;
  6959. /* start of search, search for folders and normal files: */
  6960. begin_system_call();
  6961. do {
  6962. /* readdir in resolved directory. directory was resolved earlier */
  6963. READDIR_findfirst(namestring_asciz,{
  6964. end_system_call();
  6965. if (dsp->if_none == DIR_IF_NONE_IGNORE) {
  6966. FREE_DYNAMIC_ARRAY(namestring_asciz); return;
  6967. } else OS_file_error(STACK_1);
  6968. }, break; );
  6969. while (1) {
  6970. end_system_call();
  6971. /* convert directory-entry into string: */
  6972. var object direntry = asciz_to_string(READDIR_entry_name(),O(pathname_encoding));
  6973. /* skip "." and "..": */
  6974. if (!(equal(direntry,O(dot_string))
  6975. || equal(direntry,O(dotdot_string)))) {
  6976. var shell_shortcut_target_t rresolved = shell_shortcut_notresolved;
  6977. pushSTACK(direntry);
  6978. /* stack layout: ..., pathname, dir_namestring, direntry. */
  6979. pushSTACK(NIL); /* will become found file full pathname, */
  6980. /* changed with symbolic name for resolved (maybe nonfound) links */
  6981. pushSTACK(NIL); /* true pathname of it or whatever result to return */
  6982. pushSTACK(direntry); /* here will come filename to wildcard match */
  6983. split_name_type(1);
  6984. /* stack layout: ..., pathname, dir_namestring, direntry, NIL, NIL, direntry-name, direntry-type. */
  6985. /* make full name of found file - dir + direntry
  6986. TODO: optimize to not do it when it not needed */
  6987. if (READDIR_entry_ISDIR()) { /* pathname and direntry: */
  6988. STACK_3 = pathname_add_subdir(STACK_6,STACK_4);
  6989. } else {
  6990. STACK_(3) = copy_pathname(STACK_(6));
  6991. ThePathname(STACK_(3))->pathname_type = STACK_0;
  6992. ThePathname(STACK_(3))->pathname_name = STACK_1;
  6993. ThePathname(STACK_(3))->pathname_version = DEFAULT_VERSION;
  6994. }
  6995. /* try to resolve .lnk files */
  6996. if (!READDIR_entry_ISDIR() && !nullp(STACK_0)
  6997. && string_equal(STACK_0,O(lnk_string)))
  6998. {
  6999. var char resolved[MAX_PATH];
  7000. var char * full_resolved = resolved;
  7001. with_sstring_0(whole_namestring(STACK_(3)),O(pathname_encoding),linkfile_asciiz, {
  7002. rresolved =
  7003. resolve_shell_shortcut_more(linkfile_asciiz,resolved);
  7004. if (rresolved != shell_shortcut_notresolved) {
  7005. var char resolved_f[MAX_PATH];
  7006. if (FullName(resolved,resolved_f))
  7007. full_resolved = resolved_f;
  7008. /* hack direntry-pathname to make it a symbolic name
  7009. symbolic link names are direntry-pathnames w/o ".lnk"
  7010. so split the name again
  7011. hack it in-place since lnk filename is not need anymore */
  7012. pushSTACK(STACK_1);
  7013. split_name_type(1);
  7014. ThePathname(STACK_(3+2))->pathname_name = STACK_1;
  7015. ThePathname(STACK_(3+2))->pathname_type = STACK_0;
  7016. ThePathname(STACK_(3+2))->pathname_version = DEFAULT_VERSION;
  7017. skipSTACK(2);
  7018. /* what to use as a result */
  7019. if (rresolved == shell_shortcut_notexists)
  7020. STACK_(2) = STACK_(3); /* use symbolic names as a result when target is not found */
  7021. else {
  7022. STACK_(2) = coerce_pathname(asciz_to_string(full_resolved,O(pathname_encoding)));
  7023. ThePathname(STACK_(2))->pathname_version = DEFAULT_VERSION;
  7024. }
  7025. }
  7026. });
  7027. }
  7028. if (rresolved == shell_shortcut_notresolved) {
  7029. /* truename is the pathname itself */
  7030. STACK_(2) = STACK_(3);
  7031. /* nametomatch is direntry */
  7032. STACK_(1) = STACK_(4);
  7033. }
  7034. skipSTACK(1); /* drop direntry-type */
  7035. /* stack layout: ..., pathname, dir_namestring, direntry,
  7036. direntry-pathname, true-pathname, direntry-name-to-check. */
  7037. if (rresolved == shell_shortcut_notexists
  7038. && dsp->if_none == DIR_IF_NONE_ERROR)
  7039. error_file_not_exists();
  7040. if (rresolved != shell_shortcut_notexists
  7041. || (dsp->if_none != DIR_IF_NONE_DISCARD
  7042. && dsp->if_none != DIR_IF_NONE_IGNORE)) {
  7043. if (READDIR_entry_ISDIR() || rresolved == shell_shortcut_directory) {
  7044. /* nonfound shortcuts are treated as shortcuts to files */
  7045. if (recursively) /* all recursive subdirectories wanted? */
  7046. /* yes -> push truename onto pathnames-to-insert
  7047. (it is inserted in front of pathname-list-rest later): */
  7048. PUSH_ON_STACK(1,0+6);
  7049. if (next_task<0) {
  7050. /* match (car subdir-list) with direntry: */
  7051. if (wildcard_match(Car(STACK_(1+4+6)),STACK_0))
  7052. /* Subdirectory matches -> push truename onto new-pathname-list: */
  7053. PUSH_ON_STACK(1,3+6);
  7054. }
  7055. } else {
  7056. /* entry is a (halfway) normal file. */
  7057. if (next_task>0) {
  7058. if (wildcard_match(STACK_(2+4+6),STACK_0)) {
  7059. /* stack layout: ..., pathname, dir_namestring, direntry,
  7060. direntry-maybhacked-pathname, true-pathname,
  7061. direntry-name-to-check.
  7062. test Full-Flag and poss. get more information: */
  7063. if (dsp->full_p /* :FULL wanted? */
  7064. && rresolved != shell_shortcut_notexists) { /* treat nonexisting as :FULL NIL */
  7065. var decoded_time_t timepoint;
  7066. var off_t entry_size;
  7067. pushSTACK(STACK_(2)); /* newpathname as 1st list element */
  7068. pushSTACK(STACK_(1+1)); /* resolved pathname as 2nd list element */
  7069. /* get file attributes into timepoint & entry_size */
  7070. if (rresolved == shell_shortcut_file) {
  7071. /* need another readdir here */
  7072. get_time_size(&STACK_0,&timepoint,&entry_size);
  7073. } else { /* easy way */
  7074. READDIR_entry_timedate(&timepoint);
  7075. entry_size = READDIR_entry_size();
  7076. }
  7077. pack_full_info(&timepoint,&entry_size);
  7078. PUSH_ON_STACK(0,4+4+6+2);
  7079. skipSTACK(2); /* drop newname & full info list */
  7080. } else PUSH_ON_STACK(1,4+4+6);
  7081. }
  7082. }
  7083. }
  7084. }
  7085. skipSTACK(4); /* forget all up to dir_namestring */
  7086. }
  7087. /* next file: */
  7088. begin_system_call();
  7089. READDIR_findnext({ end_system_call(); OS_file_error(STACK_1); }, break; );
  7090. }
  7091. } while (false);
  7092. end_system_call();
  7093. READDIR_end_declarations;
  7094. });
  7095. }
  7096. #endif
  7097. }
  7098. local maygc object directory_search (object pathname, dir_search_param_t *dsp) {
  7099. pathname = use_default_dir(pathname); /* insert default-directory */
  7100. /* pathname is now new and an absolute pathname. */
  7101. pushSTACK(NIL); /* result-list := NIL */
  7102. pushSTACK(pathname);
  7103. /* if name=NIL and type/=NIL: set name := "*". */
  7104. if (nullp(ThePathname(pathname)->pathname_name)
  7105. && !nullp(ThePathname(pathname)->pathname_type))
  7106. ThePathname(pathname)->pathname_name = S(Kwild);
  7107. /* for matching: collect name and type into a string: */
  7108. if (nullp(ThePathname(pathname)->pathname_name)) {
  7109. pushSTACK(NIL); /* name=NIL -> also type=NIL -> do not search files */
  7110. } else {
  7111. var object nametype_string = file_namestring(pathname);
  7112. pathname = STACK_0;
  7113. pushSTACK(nametype_string);
  7114. }
  7115. pushSTACK(ThePathname(pathname)->pathname_directory); /* subdir-list */
  7116. /* copy pathname and thereby discard name and type and
  7117. shorten directory to (:ABSOLUTE) resp. (:ABSOLUTE :ROOT) : */
  7118. pathname = copy_pathname(pathname);
  7119. ThePathname(pathname)->pathname_name = NIL;
  7120. ThePathname(pathname)->pathname_type = NIL;
  7121. ThePathname(pathname)->pathname_version = NIL;
  7122. ThePathname(pathname)->pathname_directory = O(directory_absolute);
  7123. pushSTACK(pathname);
  7124. { /* pack into one-element list: */
  7125. var object new_cons = allocate_cons();
  7126. Car(new_cons) = STACK_0;
  7127. STACK_0 = new_cons;
  7128. }
  7129. var bool recursively = /* Flag, if the next operation has to be applied */
  7130. false; /* to all subdirectories. */
  7131. while (1) {
  7132. /* stack layout: result-list, pathname, name&type, subdir-list,
  7133. pathname-list.
  7134. result-list = list of finished pathnames/lists, reversed.
  7135. name&type = NIL or Normal-Simple-String,
  7136. against which the filenames have to be matched.
  7137. pathname-list = list of directories to be processed.
  7138. the pathnames from pathname-list contain the directory
  7139. only so deep, that afterwards work continues with (cdr subdir-list) .
  7140. process next subdir-level: */
  7141. STACK_1 = Cdr(STACK_1); /* shorten subdir-list */
  7142. var signean next_task; /* what has to be done with the Dirs from pathname-list: */
  7143. /* 0: nothing, finished
  7144. 1: look for a file of given name/type
  7145. -1: look for a subdirectory of given name
  7146. 2: look for all files, that match the given name/type
  7147. -2: look for all subdirectories, that match the given name */
  7148. if (matomp(STACK_1)) { /* subdir-list finished? */
  7149. var object nametype = STACK_2;
  7150. if (nullp(nametype)) /* name=NIL and type=NIL -> do not search files */
  7151. next_task = 0;
  7152. #if !defined(WIN32_NATIVE)
  7153. else if (!wild_p(nametype,false) && (dsp->if_none != DIR_IF_NONE_IGNORE))
  7154. /* === !(wild_p(name) || ((!nullp(type)) && wild_p(type))) */
  7155. next_task = 1; /* search file */
  7156. #endif
  7157. else
  7158. next_task = 2; /* search files with wildcards */
  7159. } else {
  7160. var object next_subdir = Car(STACK_1);
  7161. if (eq(next_subdir,S(Kwild_inferiors))) { /* '...' ? */
  7162. /* will be treated at the next run */
  7163. recursively = true; goto passed_subdir;
  7164. }
  7165. if (!wild_p(next_subdir,false))
  7166. next_task = -1; /* search subdir */
  7167. else
  7168. next_task = -2; /* search subdirs with wildcards */
  7169. }
  7170. /* traverse pathname-list and construct new list: */
  7171. { pushSTACK(NIL); }
  7172. #if defined(UNIX) || defined(WIN32_NATIVE)
  7173. if (dsp->circle_p) { /* query :CIRCLE-Flag */
  7174. /* maintain hash-table of all scanned directories so far (as
  7175. cons (dev . ino)) :
  7176. (MAKE-HASH-TABLE :KEY-TYPE '(CONS INTEGER INTEGER) :VALUE-TYPE '(EQL T)
  7177. :TEST 'EQUAL) */
  7178. pushSTACK(S(Ktest)); pushSTACK(S(equal));
  7179. funcall(L(make_hash_table),2);
  7180. pushSTACK(value1);
  7181. } else
  7182. #endif
  7183. pushSTACK(NIL);
  7184. pushSTACK(STACK_(0+2));
  7185. while (1) {
  7186. /* stack layout: ..., new-pathname-list, ht, pathname-list-rest. */
  7187. var object pathname_list_rest = STACK_0;
  7188. if (atomp(pathname_list_rest))
  7189. break;
  7190. STACK_0 = Cdr(pathname_list_rest); /* shorten list */
  7191. pushSTACK(NIL); /* pathnames-to-insert := NIL */
  7192. /* stack layout: ..., new-pathname-list, ht, pathname-list-rest,
  7193. pathnames-to-insert. */
  7194. {
  7195. var object pathname = Car(pathname_list_rest); /* next directory */
  7196. pushSTACK(pathname); /* into the stack */
  7197. /* try to shorten the task a little: */
  7198. if (!recursively) {
  7199. switch (next_task) {
  7200. case 0: { /* return this directory pathname */
  7201. ASSERT(namenullp(STACK_0));
  7202. pushSTACK(copy_pathname(STACK_0));
  7203. var struct file_status fs; file_status_init(&fs,&STACK_0);
  7204. assure_dir_exists(&fs,false,false); /* first resolve links */
  7205. if (dsp->full_p) /* assure_dir_exists does not fill fs_stat */
  7206. with_stat_info_computed(&fs);
  7207. /* and push STACK_0 in front of result-list: */
  7208. PUSH_ON_STACK(0,4+4+2);
  7209. skipSTACK(2);
  7210. } goto next_pathname;
  7211. #if !defined(WIN32_NATIVE)
  7212. case 1: { /* look in this pathname for a file */
  7213. ThePathname(pathname)->pathname_name = /* insert name (/=NIL) */
  7214. ThePathname(STACK_(3+4+1))->pathname_name;
  7215. ThePathname(pathname)->pathname_type = /* insert type */
  7216. ThePathname(STACK_(3+4+1))->pathname_type;
  7217. ThePathname(pathname)->pathname_version =
  7218. DEFAULT_VERSION; /* original may be :WILD! */
  7219. pushSTACK(pathname);
  7220. var struct file_status fs; file_status_init(&fs,&STACK_0);
  7221. assure_dir_exists(&fs,true,false); /* resolve links, stat file */
  7222. if (file_exists(&fs)) { /* if file exists */
  7223. /* extend result-list: */
  7224. if (dsp->full_p) /* :FULL wanted? */
  7225. with_stat_info(&(fs.fs_stat)); /* yes -> extend STACK_0 */
  7226. /* and push STACK_0 in front of result-list: */
  7227. PUSH_ON_STACK(0,4+4+2);
  7228. }
  7229. skipSTACK(2);
  7230. } goto next_pathname;
  7231. #endif
  7232. case -1: { /* search for a subdirectory in this pathname */
  7233. var struct file_status fs; file_status_init(&fs,&STACK_0);
  7234. assure_dir_exists(&fs,true,false); /* resolve links, directory-namestring */
  7235. pushSTACK(fs.fs_namestring); /* directory-namestring */
  7236. {
  7237. var object subdir = Car(STACK_(1+4+1+1)); /*(car subdir-list)*/
  7238. SUBDIR_PUSHSTACK(subdir);
  7239. }
  7240. #if defined(WIN32_NATIVE)
  7241. pushSTACK(O(backslash_string));
  7242. fs.fs_namestring = string_concat(3); /* concatenate */
  7243. #else
  7244. fs.fs_namestring = string_concat(2);
  7245. #endif
  7246. /* get information: */
  7247. directory_search_1subdir(Car(STACK_(1+4+1)),fs.fs_namestring);
  7248. }
  7249. skipSTACK(1);
  7250. goto next_pathname;
  7251. }
  7252. }
  7253. /* in order to finish the task, all entries in this directory
  7254. have to be scanned: */
  7255. {
  7256. var struct file_status fs; file_status_init(&fs,&STACK_0);
  7257. assure_dir_exists(&fs,true,false); /* resolve links, form directory-name */
  7258. pushSTACK(fs.fs_namestring); /* save */
  7259. }
  7260. /* stack layout: ..., pathname, dir_namestring. */
  7261. #if defined(UNIX) || defined(WIN32_NATIVE)
  7262. if (dsp->circle_p) { /* query :CIRCLE flag */
  7263. /* search pathname in the hash-table: */
  7264. var object hashcode = directory_search_hashcode();
  7265. if (eq(hashcode,nullobj)) {
  7266. /* entry does not exist, however (this can happen to us
  7267. only for symbolic links)
  7268. -> will be skipped */
  7269. skipSTACK(2); goto next_pathname;
  7270. }
  7271. /* and locate in the hash-table and store: */
  7272. if (!nullp(shifthash(STACK_(2+2),hashcode,T,true))) {
  7273. /* was already inside -> will be skipped */
  7274. skipSTACK(2); goto next_pathname;
  7275. }
  7276. }
  7277. #endif
  7278. if (next_task==0) /* push pathname STACK_1 in front of result-list: */
  7279. PUSH_ON_STACK(1,4+4+2);
  7280. directory_search_scandir(recursively,next_task,dsp);
  7281. skipSTACK(2); /* forget pathname and dir_namestring */
  7282. next_pathname: ;
  7283. }
  7284. /* stack layout: ..., new-pathname-list, ht, pathname-list-rest, pathnames-to-insert.
  7285. Before advancing with pathname-list-rest :
  7286. pathname-list-rest := (nreconc pathnames-to-insert pathname-list-rest): */
  7287. var object pathnames_to_insert = popSTACK();
  7288. STACK_0 = nreconc(pathnames_to_insert,STACK_0);
  7289. }
  7290. skipSTACK(2); /* forget empty pathname-list-rest and hash-table */
  7291. { /* reverse new-pathname-list, replaces the emptied pathname-list: */
  7292. var object new_pathname_list = popSTACK();
  7293. STACK_0 = nreverse(new_pathname_list); /* new pathname-list */
  7294. }
  7295. /* we are finished with this subdir-stage. */
  7296. if (matomp(STACK_1))
  7297. break; /* (atom subdir-list) -> finished. */
  7298. recursively = false; /* the next (preliminarily) non-recursive */
  7299. passed_subdir: ;
  7300. }
  7301. /* stack layout: result-list pathname name&type subdir-list pathname-list
  7302. subdir-list became =NIL , also pathname-list = NIL (because at the last
  7303. loop iteration next_task is always =0,1,2, so nothing
  7304. was pushed on new-pathname-list). */
  7305. skipSTACK(4);
  7306. return popSTACK(); /* result-list as result */
  7307. }
  7308. #endif /* PATHNAME_NOEXT */
  7309. /* (DIRECTORY pathname [:circle] [:full] [:if-does-not-exist]),
  7310. CLTL p. 427 */
  7311. LISPFUN(directory,seclass_read,1,0,norest,key,3,
  7312. ( kw(if_does_not_exist),kw(circle),kw(full) ))
  7313. { /* stack layout: pathname, if-does-not-exist, circle, full. */
  7314. var dir_search_param_t dsp;
  7315. if (!boundp(STACK_2) || eq(STACK_2,S(Kdiscard)))
  7316. /* :IF-DOES-NOT-EXIST defaults to :DISCARD */
  7317. dsp.if_none = DIR_IF_NONE_DISCARD;
  7318. else if (eq(STACK_2,S(Kerror)))
  7319. dsp.if_none = DIR_IF_NONE_ERROR;
  7320. else if (eq(STACK_2,S(Kkeep)))
  7321. dsp.if_none = DIR_IF_NONE_KEEP;
  7322. else if (eq(STACK_2,S(Kignore)))
  7323. dsp.if_none = DIR_IF_NONE_IGNORE;
  7324. else {
  7325. pushSTACK(STACK_2); /* TYPE-ERROR slot DATUM */
  7326. pushSTACK(O(type_directory_not_exist)); /* TYPE-ERROR slot EXPECTED-TYPE */
  7327. pushSTACK(STACK_(2+2)); /* :IF-DOES-NOT-EXIST argument */
  7328. pushSTACK(S(Kif_does_not_exist)); pushSTACK(S(directory));
  7329. error(type_error,GETTEXT("~S: illegal ~S argument ~S"));
  7330. }
  7331. dsp.circle_p = !missingp(STACK_1); /* :CIRCLE argument defaults to NIL */
  7332. dsp.full_p = !missingp(STACK_0); /* :FULL argument defaults to NIL */
  7333. skipSTACK(3);
  7334. /* check pathname-argument: */
  7335. var object pathname = merge_defaults(coerce_pathname(STACK_0));
  7336. /* let's go: */
  7337. #ifdef PATHNAME_WIN32
  7338. if (eq(ThePathname(pathname)->pathname_device,S(Kwild))) {
  7339. /* Device = :WILD ? ==> scan all devices */
  7340. STACK_0 = pathname;
  7341. pushSTACK(NIL); /* pathname-list := NIL */
  7342. { /* stack layout: pathname, pathname-list. */
  7343. var char drive;
  7344. for (drive='A'; drive<='Z'; drive++) /* traverse all drives */
  7345. if (good_drive(drive)) {
  7346. var object newpathname = copy_pathname(STACK_1);
  7347. ThePathname(newpathname)->pathname_device =
  7348. /* take over the device = one-element drive string */
  7349. n_char_to_string(&drive,1,O(pathname_encoding));
  7350. /* search within a drive: */
  7351. var object newpathnames = directory_search(newpathname,&dsp);
  7352. /* and attach pathname-list in front of STACK_0: */
  7353. STACK_0 = nreconc(newpathnames,STACK_0);
  7354. }
  7355. }
  7356. VALUES1(nreverse(STACK_0)); /* reverse pathname-list again */
  7357. skipSTACK(2);
  7358. } else
  7359. /* only one device to scan */
  7360. #endif
  7361. {
  7362. VALUES1(directory_search(pathname,&dsp)); /* form matching pathnames */
  7363. skipSTACK(1);
  7364. }
  7365. }
  7366. /* UP: make sure that the supposed directory namestring ends with a slash
  7367. returns a new string with a slash appended or the same stirng
  7368. can trigger GC */
  7369. local maygc object ensure_last_slash (object dir_string) {
  7370. ASSERT(stringp(dir_string));
  7371. var uintL len, offset;
  7372. var object str = unpack_string_ro(dir_string,&len,&offset);
  7373. var chart ch = schar(str,len+offset-1);
  7374. if (!pslashp(ch) && !lslashp(ch)) {
  7375. var char sl = (looks_logical_p(dir_string) ? ';' : slash);
  7376. with_sstring_0(str,O(pathname_encoding),asciz, {
  7377. dir_string = asciz_add_char(asciz,len,sl,O(pathname_encoding));
  7378. });
  7379. }
  7380. return dir_string;
  7381. }
  7382. /* (CD [pathname]) sets the current drive and the current directory. */
  7383. LISPFUN(cd,seclass_default,0,1,norest,nokey,0,NIL) {
  7384. var object pathname = popSTACK();
  7385. if (!boundp(pathname)) { pathname = O(empty_string); } /* "" */
  7386. else if (stringp(pathname)) /* make sure it ends with a slash */
  7387. pathname = ensure_last_slash(pathname);
  7388. pathname = merge_defaults(coerce_pathname(pathname)); /* --> pathname */
  7389. /* no need to copy: merge_defaults produces a fresh pathname
  7390. set name and type to NIL: */
  7391. ThePathname(pathname)->pathname_name = NIL;
  7392. ThePathname(pathname)->pathname_type = NIL;
  7393. pushSTACK(pathname);
  7394. var struct file_status fs; file_status_init(&fs,&STACK_0);
  7395. true_namestring(&fs,false,false); /* the directory must exist */
  7396. change_default(); /* set default drive and default directory */
  7397. VALUES1(popSTACK()); /* new pathname as the value */
  7398. }
  7399. #undef lslashp
  7400. #undef pslashp
  7401. /* UP: checks a pathname, if both name and type are =NIL ,
  7402. and if the directory "almost" exists.
  7403. shorter_directory(pathname,resolve_links)
  7404. > pathname : Pathname-Argument
  7405. > resolve_links : flag, if links have to be resolved (usually yes)
  7406. < -(STACK) : absolute pathname */
  7407. #ifdef WIN32_NATIVE
  7408. /* < result: Directory-Namestring (for the OS, without '\' at the end, Normal-Simple-String) */
  7409. #endif
  7410. #ifdef UNIX
  7411. /* < result: Directory-Namestring (for the OS, without '/' at the end, Normal-Simple-String) */
  7412. #endif
  7413. /* decrements STACK by 1.
  7414. can trigger GC */
  7415. local maygc object shorter_directory (object pathname, bool resolve_links) {
  7416. pathname = merge_defaults(coerce_pathname(pathname)); /* --> pathname */
  7417. check_no_wildcards(pathname); /* with wildcards -> error */
  7418. pathname = use_default_dir(pathname); /* insert default-directory */
  7419. check_notdir(pathname); /* ensure that Name=NIL and Type=NIL */
  7420. pushSTACK(pathname); /* save new pathname */
  7421. /* shorten the directory: */
  7422. var object subdirs = ThePathname(pathname)->pathname_directory;
  7423. if (nullp(Cdr(subdirs))) { /* root-directory ? */
  7424. /* STACK_0 = pathname, FILE-ERROR slot PATHNAME */
  7425. pushSTACK(STACK_0);
  7426. error(file_error,GETTEXT("root directory not allowed here: ~S"));
  7427. }
  7428. subdirs = reverse(subdirs); /* copy list and reverse */
  7429. pushSTACK(subdirs); /* save cons with last subdir as CAR */
  7430. subdirs = Cdr(subdirs); /* all subdirs except for the last */
  7431. subdirs = nreverse(subdirs); /* bring into right order again */
  7432. pathname = STACK_1;
  7433. ThePathname(pathname)->pathname_directory = subdirs; /* and store in the pathname */
  7434. /* this directory must exist: */
  7435. pushSTACK(pathname);
  7436. /* stack layout: pathname, subdircons, pathname. */
  7437. var struct file_status fs; file_status_init(&fs,&STACK_0);
  7438. assure_dir_exists(&fs,!resolve_links,false);
  7439. /* build subdir-string for the operating system: */
  7440. STACK_0 = fs.fs_namestring; /* directory-namestring so far as 1st String */
  7441. var uintC stringcount = /* the strings in the last subdir */
  7442. subdir_namestring_parts(STACK_1,false);
  7443. /* and no '\' at the end (for the OS)
  7444. and no '/' at the end (for the OS) */
  7445. var object dirstring = string_concat(1+stringcount); /* concatenate */
  7446. skipSTACK(1);
  7447. return dirstring;
  7448. }
  7449. LISPFUNN(make_directory,1)
  7450. { /* (MAKE-DIRECTORY pathname) makes a new subdirectory pathname. */
  7451. var object pathstring = shorter_directory(STACK_0,true);
  7452. with_sstring_0(pathstring,O(pathname_encoding),pathstring_asciz, {
  7453. make_directory(pathstring_asciz);
  7454. });
  7455. skipSTACK(2);
  7456. VALUES1(T);
  7457. }
  7458. LISPFUNN(delete_directory,1)
  7459. { /* (DELETE-DIRECTORY pathname) removes the subdirectory pathname. */
  7460. var object pathstring = shorter_directory(STACK_0,true);
  7461. with_sstring_0(pathstring,O(pathname_encoding),pathstring_asciz, {
  7462. delete_directory(pathstring_asciz);
  7463. });
  7464. skipSTACK(2);
  7465. VALUES1(T);
  7466. }
  7467. LISPFUNN(rename_directory,2)
  7468. { /* (RENAME-DIRECTORY dirname newname) removes the subdirectory pathname. */
  7469. var object newdir = shorter_directory(STACK_0,true); STACK_0 = newdir;
  7470. var object olddir = shorter_directory(STACK_2,true); STACK_2 = olddir;
  7471. with_sstring_0(STACK_2,O(pathname_encoding),oldnamestring_asciz, {
  7472. with_sstring_0(STACK_1,O(pathname_encoding),newnamestring_asciz, {
  7473. rename_existing_file(oldnamestring_asciz,newnamestring_asciz);
  7474. });
  7475. });
  7476. skipSTACK(4);
  7477. VALUES1(T);
  7478. }
  7479. /* (defun ensure-directories-exist (pathspec &key verbose)
  7480. (let* ((dir (pathname-directory pathspec))
  7481. (path (make-pathname :host (pathname-host pathspec)
  7482. :device (pathname-device pathspec)
  7483. :directory dir)))
  7484. (when (wild-pathname-p path)
  7485. (error (make-condition (quote file-error) :pathname pathspec)))
  7486. (if (directory path)
  7487. (values pathspec nil)
  7488. (loop for i from 1 upto (length dir)
  7489. for newpath = (make-pathname :host (pathname-host pathspec)
  7490. :device (pathname-device pathspec)
  7491. :directory (subseq dir 0 i))
  7492. unless (directory newpath)
  7493. do (let ((namestring (namestring newpath)))
  7494. (when verbose
  7495. (format *standard-output* "~&Creating directory: ~A~%"
  7496. namestring))
  7497. (ignore-errors (ext:make-dir namestring))
  7498. (unless (directory newpath)
  7499. (error (make-condition (quote file-error)
  7500. :pathname pathspec))))))
  7501. finally (return (values pathspec t)))) */
  7502. LISPFUN(ensure_directories_exist,seclass_default,1,0,norest,key,1,
  7503. (kw(verbose))) {
  7504. var object pathname = coerce_pathname(STACK_1);
  7505. pathname = merge_defaults(pathname); /* copy and discard name, type */
  7506. ThePathname(pathname)->pathname_name = NIL;
  7507. ThePathname(pathname)->pathname_type = NIL;
  7508. check_no_wildcards(pathname); /* with wildcards -> error */
  7509. pathname = use_default_dir(pathname); /* insert default-directory */
  7510. pushSTACK(pathname); /* save new pathname */
  7511. /* stack layout: pathspec, verbose, pathname. */
  7512. if (directory_exists(pathname)) {
  7513. skipSTACK(2); value2 = NIL; /* pathspec, NIL as values */
  7514. } else {
  7515. var object subdirs = copy_list(ThePathname(STACK_0)->pathname_directory);
  7516. pushSTACK(subdirs); pushSTACK(Cdr(subdirs));
  7517. Cdr(subdirs) = NIL;
  7518. ThePathname(STACK_2)->pathname_directory = subdirs;
  7519. /* stack layout: pathspec, verbose, pathname, (car (last subdirs)),
  7520. remaining_subdirs. */
  7521. while (mconsp(STACK_0)) {
  7522. subdirs = STACK_0;
  7523. Cdr(STACK_1) = subdirs; STACK_1 = subdirs; STACK_0 = Cdr(subdirs); Cdr(subdirs) = NIL;
  7524. if (!directory_exists(STACK_2)) {
  7525. if (!missingp(STACK_3)) { /* Verbose? */
  7526. funcall(L(fresh_line),0); /* (FRESH-LINE [*standard-output*]) */
  7527. pushSTACK(CLSTEXT("Creating directory: ")); funcall(L(write_string),1); /* (WRITE-STRING "..." [*standard-output*]) */
  7528. pushSTACK(STACK_2); funcall(L(princ),1); /* (PRINC pathname [*standard-output*]) */
  7529. funcall(L(terpri),0); /* (TERPRI [*standard-output*]) */
  7530. }
  7531. /* side remark: Do not need to resolve links here, because here we
  7532. proceed step by step starting at the root, anyway. */
  7533. var object pathstring = shorter_directory(STACK_2,false);
  7534. with_sstring_0(pathstring,O(pathname_encoding),pathstring_asciz, {
  7535. make_directory(pathstring_asciz);
  7536. });
  7537. skipSTACK(1);
  7538. }
  7539. }
  7540. skipSTACK(4); value2 = T; /* pathspec, T as values */
  7541. }
  7542. value1 = popSTACK(); mv_count=2;
  7543. }
  7544. #ifdef UNIX
  7545. /* Returns the struct passwd entry for the current user.
  7546. The return value points to static data, or is NULL upon failure. */
  7547. local struct passwd * unix_user_pwd (void) {
  7548. var const char* username;
  7549. var struct passwd * userpasswd = NULL;
  7550. /* The manpage for GETLOGIN(3V) recommends
  7551. first getpwnam(getlogin()), then getpwuid(getuid()). */
  7552. begin_system_call();
  7553. /* 1. attempt: getpwnam(getenv("USER")) */
  7554. username = getenv("USER");
  7555. if (username != NULL) {
  7556. errno = 0; userpasswd = getpwnam(username);
  7557. if (userpasswd != NULL) goto ok;
  7558. if (errno != 0) { OS_error(); }
  7559. }
  7560. /* 2. attempt: getpwnam(getlogin()) */
  7561. username = getlogin();
  7562. if (username != NULL) {
  7563. errno = 0; userpasswd = getpwnam(username);
  7564. if (userpasswd != NULL) goto ok;
  7565. if (errno != 0) { OS_error(); }
  7566. }
  7567. /* 3. attempt: getpwuid(getuid()) */
  7568. errno = 0; userpasswd = getpwuid(getuid());
  7569. if (userpasswd != NULL) goto ok;
  7570. if (errno != 0) { OS_error(); }
  7571. /* Everything fails, userpasswd == NULL. */
  7572. ok:
  7573. end_system_call();
  7574. return userpasswd;
  7575. }
  7576. #endif
  7577. /* UP: Initializes the pathname-system.
  7578. init_pathnames();
  7579. can trigger GC */
  7580. global maygc void init_pathnames (void) {
  7581. #ifdef PATHNAME_WIN32
  7582. { /* initialize default-drive: */
  7583. var char drive = default_drive();
  7584. O(default_drive) =
  7585. (drive == 0 ? NIL /* network */
  7586. : n_char_to_string(&drive,1,O(pathname_encoding))); /* local device */
  7587. }
  7588. #endif
  7589. /* initialize *DEFAULT-PATHNAME-DEFAULTS* : */
  7590. recalc_defaults_pathname();
  7591. #ifdef USER_HOMEDIR
  7592. #ifdef UNIX
  7593. /* we retrieve the home-directory and the usable shell from the
  7594. environment. It contains (almost) always at least the following variables:
  7595. LOGNAME = Username at the first login ("true" identity of the user)
  7596. USER = current username
  7597. HOME = current home-directory, fetched from /etc/passwd
  7598. SHELL = current standard-shell, fetched from /etc/passwd
  7599. PATH = search path for program call
  7600. TERM = terminal emulation
  7601. we retrieve HOME (for "~" - translation) and SHELL (for EXECUTE).
  7602. For "~username" we must scan the /etc/passwd - file. */
  7603. { /* search in the environment for variable HOME: */
  7604. begin_system_call();
  7605. var const char* homedir = getenv("HOME");
  7606. end_system_call();
  7607. if (homedir != NULL) { /* found? */
  7608. O(user_homedir) = asciz_dir_to_pathname(homedir,O(misc_encoding)); /* yes -> enter */
  7609. } else { /* no -> get home-directory from the passwort-file: */
  7610. var struct passwd * userpasswd = unix_user_pwd();
  7611. if (userpasswd != NULL) { /* no -> enter homedir as pathname */
  7612. O(user_homedir) = asciz_dir_to_pathname(userpasswd->pw_dir,O(misc_encoding));
  7613. } else { /* no -> take current directory: */
  7614. O(user_homedir) = default_directory();
  7615. }
  7616. }
  7617. }
  7618. #endif
  7619. #ifdef WIN32
  7620. /* WinNT defines HOMEDRIVE and HOMEPATH. Win95 (which is actually not a
  7621. multiuser OS) lets the user set HOME himself.
  7622. In any case, we give preference to HOME, because the user can change
  7623. this. */
  7624. {
  7625. var const char * home;
  7626. begin_system_call();
  7627. home = getenv("HOME");
  7628. if (home != NULL) {
  7629. end_system_call();
  7630. O(user_homedir) = asciz_dir_to_pathname(home,O(misc_encoding));
  7631. } else {
  7632. var const char * homedrive = getenv("HOMEDRIVE");
  7633. var const char * homepath = getenv("HOMEPATH");
  7634. end_system_call();
  7635. if (homedrive!=NULL && homepath!=NULL) {
  7636. var char* homeall = (char*)alloca(asciz_length(homedrive)+asciz_length(homepath)+1);
  7637. var char* ptr = homeall;
  7638. while ((*ptr = *homedrive) != '\0') { homedrive++; ptr++; }
  7639. while ((*ptr = *homepath) != '\0') { homepath++; ptr++; }
  7640. *ptr = '\0';
  7641. O(user_homedir) = asciz_dir_to_pathname(homeall,O(misc_encoding));
  7642. } else {
  7643. O(user_homedir) = use_default_dir(asciz_dir_to_pathname(".",Symbol_value(S(ascii))));
  7644. }
  7645. }
  7646. }
  7647. #endif
  7648. #endif
  7649. #ifdef HAVE_SHELL
  7650. #ifdef UNIX
  7651. /* the command-shell O(command_shell) remains unchanged, otherwise
  7652. we get too many portability problems. */
  7653. { /* search the environment for variable SHELL: */
  7654. begin_system_call();
  7655. var const char* shell = getenv("SHELL");
  7656. end_system_call();
  7657. if (shell != NULL) { /* found? ==> enter */
  7658. O(user_shell) = asciz_to_string(shell,O(misc_encoding));
  7659. }
  7660. /* else O(user_shell) remains on the default value "/bin/csh". */
  7661. }
  7662. #endif
  7663. #ifdef WIN32_NATIVE
  7664. { /* search in the environment for variable COMSPEC: */
  7665. begin_system_call();
  7666. var const char* shell = getenv("COMSPEC");
  7667. if (!(shell==NULL)) {
  7668. end_system_call();
  7669. O(command_shell) = asciz_to_string(shell,O(misc_encoding)); /* enter */
  7670. } else {
  7671. var OSVERSIONINFO v;
  7672. v.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  7673. if (!GetVersionEx(&v)) { OS_error(); }
  7674. if (v.dwPlatformId == VER_PLATFORM_WIN32_NT) { /* Windows NT */
  7675. shell = "cmd.exe";
  7676. } else { /* Windows 95 or else */
  7677. shell = "command.com";
  7678. }
  7679. end_system_call();
  7680. O(command_shell) = ascii_to_string(shell); /* enter */
  7681. }
  7682. }
  7683. #endif
  7684. #endif
  7685. }
  7686. LISPFUNNR(file_write_date,1)
  7687. { /* (FILE-WRITE-DATE file), CLTL p. 424 */
  7688. #ifdef UNIX
  7689. var time_t file_datetime; /* buffer for date/time of a file */
  7690. #endif
  7691. #ifdef WIN32_NATIVE
  7692. var WIN32_FIND_DATA filedata;
  7693. #endif
  7694. var object pathname = popSTACK(); /* pathname-argument */
  7695. if (builtin_stream_p(pathname)) { /* stream -> treat extra: */
  7696. /* must be file-stream: */
  7697. pathname = as_file_stream(pathname);
  7698. /* streamtype file-stream */
  7699. if ((TheStream(pathname)->strmflags & strmflags_open_B)
  7700. && (!nullp(TheStream(pathname)->strm_buffered_channel))) {
  7701. /* open file-stream
  7702. work with the handle directly: */
  7703. #ifdef UNIX
  7704. var struct stat status;
  7705. begin_system_call();
  7706. if (!( fstat(TheHandle(TheStream(pathname)->strm_buffered_channel),&status) ==0)) {
  7707. end_system_call(); OS_filestream_error(pathname);
  7708. }
  7709. end_system_call();
  7710. file_datetime = status.st_mtime;
  7711. #endif
  7712. #ifdef WIN32_NATIVE
  7713. var BY_HANDLE_FILE_INFORMATION fileinfo;
  7714. var BOOL result;
  7715. begin_system_call();
  7716. result = GetFileInformationByHandle(TheHandle(TheStream(pathname)->strm_buffered_channel),&fileinfo);
  7717. end_system_call();
  7718. if (result) {
  7719. filedata.ftCreationTime = fileinfo.ftCreationTime;
  7720. filedata.ftLastAccessTime = fileinfo.ftLastAccessTime;
  7721. filedata.ftLastWriteTime = fileinfo.ftLastWriteTime;
  7722. } else { /* If that failed, try the full pathname. */
  7723. test_file_stream_named(pathname);
  7724. pathname = TheStream(pathname)->strm_file_truename;
  7725. goto is_pathname;
  7726. }
  7727. #endif
  7728. } else {
  7729. /* closed file-stream -> use truename as pathname */
  7730. test_file_stream_named(pathname);
  7731. pathname = TheStream(pathname)->strm_file_truename;
  7732. goto is_pathname;
  7733. }
  7734. } else { /* turn into a pathname */
  7735. pathname = merge_defaults(coerce_pathname(pathname));
  7736. is_pathname: { /* pathname is now really a pathname */
  7737. pushSTACK(pathname);
  7738. var struct file_status fs; file_status_init(&fs,&STACK_0);
  7739. true_namestring(&fs,true,false);
  7740. #ifdef UNIX
  7741. if (!file_exists(&fs)) { error_file_not_exists(); } /* file must exist */
  7742. file_datetime = fs.fs_stat.st_mtime;
  7743. #endif
  7744. #ifdef WIN32_NATIVE
  7745. /* Only a directory search gives us the times. */
  7746. with_sstring_0(fs.fs_namestring,O(pathname_encoding),namestring_asciz, {
  7747. var HANDLE search_handle;
  7748. begin_system_call();
  7749. search_handle = FindFirstFile(namestring_asciz,&filedata);
  7750. if (search_handle==INVALID_HANDLE_VALUE) {
  7751. if (WIN32_ERROR_NOT_FOUND) {
  7752. end_system_call(); error_file_not_exists();
  7753. }
  7754. end_system_call(); OS_file_error(STACK_0);
  7755. } else if (!FindClose(search_handle)) {
  7756. end_system_call(); OS_file_error(STACK_0);
  7757. }
  7758. end_system_call();
  7759. });
  7760. #endif
  7761. skipSTACK(1);
  7762. }
  7763. }
  7764. /* date/time no is in the buffer file_datetime.
  7765. convert into Universal-Time-Format: */
  7766. #ifdef UNIX
  7767. VALUES1(convert_time_to_universal(&file_datetime));
  7768. #endif
  7769. #ifdef WIN32_NATIVE
  7770. var FILETIME* pftimepoint = &filedata.ftLastWriteTime;
  7771. if (pftimepoint->dwLowDateTime==0 && pftimepoint->dwHighDateTime==0)
  7772. pftimepoint = &filedata.ftCreationTime;
  7773. VALUES1(convert_time_to_universal(pftimepoint));
  7774. #endif
  7775. }
  7776. LISPFUNNR(file_author,1)
  7777. { /* (FILE-AUTHOR file), CLTL p. 424 */
  7778. var object pathname = popSTACK(); /* pathname-argument */
  7779. if (builtin_stream_p(pathname)) { /* stream -> treat extra: */
  7780. /* must be file-stream: */
  7781. pathname = as_file_stream(pathname);
  7782. /* streamtype file-stream */
  7783. if (TheStream(pathname)->strmflags & strmflags_open_B) {
  7784. /* open file-stream -> OK */
  7785. } else { /* closed file-stream -> use truename as pathname */
  7786. test_file_stream_named(pathname);
  7787. pathname = TheStream(pathname)->strm_file_truename;
  7788. goto is_pathname;
  7789. }
  7790. } else {
  7791. pathname = merge_defaults(coerce_pathname(pathname)); /* --> pathname */
  7792. is_pathname: { /* pathname is now really a pathname */
  7793. pushSTACK(pathname);
  7794. var struct file_status fs; file_status_init(&fs,&STACK_0);
  7795. true_namestring(&fs,true,false);
  7796. if (!file_exists(&fs)) { error_file_not_exists(); } /* file must exist */
  7797. skipSTACK(1);
  7798. }
  7799. }
  7800. /* file exists -> NIL as value */
  7801. VALUES1(NIL);
  7802. }
  7803. #ifdef UNIX
  7804. LISPFUN(execute,seclass_default,1,0,rest,nokey,0,NIL)
  7805. { /* (EXECUTE file arg1 arg2 ...) calls a file with the given arguments. */
  7806. var gcv_object_t* args_pointer = rest_args_pointer STACKop 1;
  7807. {
  7808. var gcv_object_t* argptr = args_pointer; /* Pointer to the arguments */
  7809. { /* check file: */
  7810. var gcv_object_t* file_ = &NEXT(argptr);
  7811. pushSTACK(coerce_pathname(*file_));
  7812. var struct file_status fs; file_status_init(&fs,&STACK_0);
  7813. true_namestring(&fs,true,false);
  7814. /* check, if the file exists: */
  7815. if (!file_exists(&fs)) { error_file_not_exists(); }
  7816. *file_ = string_to_asciz(fs.fs_namestring,O(pathname_encoding)); /* save */
  7817. skipSTACK(1);
  7818. }
  7819. { /* check the other arguments: */
  7820. var uintC count;
  7821. dotimesC(count,argcount, {
  7822. var gcv_object_t* arg_ = &NEXT(argptr);
  7823. pushSTACK(*arg_); funcall(L(string),1); /* convert next argument into a string */
  7824. *arg_ = string_to_asciz(value1,O(misc_encoding)); /* and convert ASCIZ-string */
  7825. });
  7826. }
  7827. }
  7828. { /* build up argv-Array in stack and copy strings in the stack: */
  7829. var uintL argvdata_length = 0;
  7830. {
  7831. var gcv_object_t* argptr = args_pointer;
  7832. var uintC count;
  7833. dotimespC(count,argcount+1, {
  7834. var object arg = NEXT(argptr); /* next argument, ASCIZ-string */
  7835. argvdata_length += Sbvector_length(arg);
  7836. });
  7837. }
  7838. var DYNAMIC_ARRAY(argv,char*,1+(uintL)argcount+1);
  7839. var DYNAMIC_ARRAY(argvdata,char,argvdata_length);
  7840. {
  7841. var gcv_object_t* argptr = args_pointer;
  7842. var char** argvptr = &argv[0];
  7843. var char* argvdataptr = &argvdata[0];
  7844. var uintC count;
  7845. dotimespC(count,argcount+1, {
  7846. var object arg = NEXT(argptr); /* next argument, ASCIZ-string */
  7847. var char* ptr = TheAsciz(arg);
  7848. var uintL len = Sbvector_length(arg);
  7849. *argvptr++ = argvdataptr; /* fill into argv */
  7850. dotimespL(len,len, { *argvdataptr++ = *ptr++; } ); /* and copy */
  7851. });
  7852. *argvptr = NULL; /* and conclude with nullpointer */
  7853. }
  7854. { /* start a new process: */
  7855. var int child;
  7856. begin_system_call();
  7857. begin_want_sigcld();
  7858. if ((child = vfork()) ==0) {
  7859. /* this program part is executed by the child-process: */
  7860. close_all_fd();
  7861. execv(argv[0],argv); /* call program */
  7862. _exit(-1); /* if this fails, end the child-process */
  7863. }
  7864. /* this program part is executed by the caller: */
  7865. if (child==-1) {
  7866. /* something failed, either on vfork or on execv.
  7867. in both cases errno was set. */
  7868. end_want_sigcld(); OS_error();
  7869. }
  7870. /* wait, until the child-process is finished: */
  7871. var int status = wait2(child);
  7872. /* cf. WAIT(2V) and #include <sys/wait.h> :
  7873. WIFSTOPPED(status) == ((status & 0xFF) == 0177)
  7874. WEXITSTATUS(status) == ((status & 0xFF00) >> 8) */
  7875. end_want_sigcld();
  7876. end_system_call();
  7877. /* finished. */
  7878. set_args_end_pointer(args_pointer); /* clean up STACK */
  7879. VALUES1(((status & 0xFF) == 0000) /* process ended normally (without signal, without core-dump) ? */
  7880. ? fixnum((status & 0xFF00) >> 8) /* yes -> exit-status as value: */
  7881. : NIL); /* no -> NIL as value */
  7882. }
  7883. FREE_DYNAMIC_ARRAY(argvdata);
  7884. FREE_DYNAMIC_ARRAY(argv);
  7885. }
  7886. }
  7887. #endif
  7888. /* Duplicate an open file handle.
  7889. handle_dup(oldfd)
  7890. Similar to dup(oldfd), with error checking.
  7891. To be called only inside begin/end_system_call(). */
  7892. global Handle handle_dup (Handle old_handle) {
  7893. Handle new_handle;
  7894. #if defined(UNIX)
  7895. new_handle = dup(old_handle);
  7896. if (new_handle < 0) { OS_error(); }
  7897. #elif defined(WIN32_NATIVE)
  7898. new_handle = INVALID_HANDLE_VALUE;
  7899. if (!DuplicateHandle(GetCurrentProcess(),old_handle,
  7900. GetCurrentProcess(),&new_handle,
  7901. 0, true, DUPLICATE_SAME_ACCESS))
  7902. OS_error();
  7903. #else
  7904. NOTREACHED;
  7905. #endif
  7906. return new_handle;
  7907. }
  7908. /* Duplicate an open file handle.
  7909. handle_dup2(oldfd,newfd)
  7910. Similar to dup2(oldfd,newfd), with error checking. The result may or may not
  7911. be equal to newfd.
  7912. To be called only inside begin/end_system_call(). */
  7913. global Handle handle_dup2 (Handle old_handle, Handle new_handle) {
  7914. #if defined(UNIX)
  7915. new_handle = dup2(old_handle,new_handle);
  7916. if (new_handle < 0) { OS_error(); }
  7917. #elif defined(WIN32_NATIVE)
  7918. if (!DuplicateHandle(GetCurrentProcess(),old_handle,
  7919. GetCurrentProcess(),&new_handle,
  7920. 0, true, DUPLICATE_SAME_ACCESS))
  7921. OS_error();
  7922. #else
  7923. NOTREACHED;
  7924. #endif
  7925. return new_handle;
  7926. }
  7927. #ifdef HAVE_SHELL
  7928. /* (SHELL) calls a shell.
  7929. (SHELL command) calls a shell and lets it execute a command. */
  7930. #if defined(WIN32_NATIVE)
  7931. /* (SYSTEM::SHELL-NAME) returns the name of the command shell. */
  7932. LISPFUNN(shell_name,0) {
  7933. VALUES1(O(command_shell));
  7934. }
  7935. LISPFUN(shell,seclass_default,0,1,norest,nokey,0,NIL) {
  7936. var object command = popSTACK();
  7937. if (missingp(command))
  7938. command = O(command_shell);
  7939. command = check_string(command);
  7940. var HANDLE prochandle;
  7941. with_string_0(command,O(misc_encoding),command_asciz, {
  7942. /* Start new process. */
  7943. var HANDLE stdinput;
  7944. var HANDLE stdoutput;
  7945. var HANDLE stderror;
  7946. var PROCESS_INFORMATION pinfo;
  7947. begin_system_call();
  7948. stdinput = GetStdHandle(STD_INPUT_HANDLE);
  7949. if (stdinput == INVALID_HANDLE_VALUE) { OS_error(); }
  7950. stdoutput = GetStdHandle(STD_OUTPUT_HANDLE);
  7951. if (stdoutput == INVALID_HANDLE_VALUE) { OS_error(); }
  7952. stderror = GetStdHandle(STD_ERROR_HANDLE);
  7953. if (stderror == INVALID_HANDLE_VALUE) { OS_error(); }
  7954. if (!MyCreateProcess(command_asciz,stdinput,stdoutput,stderror,&pinfo))
  7955. { OS_error(); }
  7956. if (pinfo.hThread && !CloseHandle(pinfo.hThread)) { OS_error(); }
  7957. prochandle = pinfo.hProcess;
  7958. });
  7959. /* Wait until it terminates, get its exit status code. */
  7960. var DWORD exitcode;
  7961. switch (WaitForSingleObject(prochandle,INFINITE)) {
  7962. case WAIT_FAILED:
  7963. OS_error();
  7964. case WAIT_OBJECT_0:
  7965. break;
  7966. default: NOTREACHED;
  7967. }
  7968. if (!GetExitCodeProcess(prochandle,&exitcode)) { OS_error(); }
  7969. if (!CloseHandle(prochandle)) { OS_error(); }
  7970. end_system_call();
  7971. /* utilize return value: =0 (OK) -> T, >0 (not OK) -> NIL : */
  7972. VALUES_IF(exitcode == 0);
  7973. }
  7974. #else /* UNIX || ... */
  7975. LISPFUN(shell,seclass_default,0,1,norest,nokey,0,NIL) {
  7976. var object command = popSTACK();
  7977. if (missingp(command)) {
  7978. /* execute (EXECUTE shell) : */
  7979. pushSTACK(O(user_shell)); /* Shell-Name */
  7980. funcall(L(execute),1);
  7981. } else {
  7982. /* call (EXECUTE shell "-c" command): */
  7983. pushSTACK(O(command_shell)); /* shell name */
  7984. pushSTACK(O(command_shell_option)); /* shell option "-c" */
  7985. pushSTACK(command);
  7986. funcall(L(execute),3);
  7987. }
  7988. }
  7989. #endif
  7990. #endif
  7991. /* stringlist_to_asciizlist (stringlist, encoding)
  7992. convert a stringlist to list of asciz strings
  7993. and places it on the stack.
  7994. returns total length of all asciiz strings including zeros
  7995. and listlength (if the pointer is not NULL)
  7996. adds one element to STACK
  7997. can trigger GC */
  7998. #if !defined(UNICODE)
  7999. #define stringlist_to_asciizlist(s,e,l) stringlist_to_asciizlist_(s,l)
  8000. local maygc int stringlist_to_asciizlist_ (object stringlist,uintL *listlength)
  8001. #else
  8002. local maygc int stringlist_to_asciizlist (object stringlist,
  8003. gcv_object_t *encoding_,
  8004. uintL *listlength)
  8005. #endif
  8006. {
  8007. var int length = 0;
  8008. var int listlen = 0;
  8009. pushSTACK(NIL)/*result head*/; pushSTACK(NIL)/*result tail*/;
  8010. pushSTACK(stringlist);
  8011. while (consp(STACK_0/*stringlist tail*/)) {
  8012. var object tmp = allocate_cons();
  8013. if (nullp(STACK_2/*result*/)) STACK_1 = STACK_2 = tmp;
  8014. else { Cdr(STACK_1/*result tail*/) = tmp; STACK_1 = tmp; }
  8015. tmp = check_string(Car(STACK_0));
  8016. tmp = string_to_asciz(tmp,*encoding_);
  8017. length += Sbvector_length(tmp) + 1;
  8018. Car(STACK_1) = tmp;
  8019. STACK_0 = Cdr(STACK_0);
  8020. listlen++;
  8021. }
  8022. if (listlength) *listlength = listlen;
  8023. skipSTACK(2); /* drop stringlist and result tail */
  8024. return length;
  8025. }
  8026. #ifdef WIN32_NATIVE
  8027. /* (SHELL-EXECUTE verb filename parameters defaultdir)
  8028. ShellExecute wrapper
  8029. See ShellExecute description at
  8030. http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/
  8031. platform/Shell/reference/functions/shellexecute.asp
  8032. verb: usually nil (for default),
  8033. "edit", "explore", "open", "print", "properties"
  8034. filename: filename or url to open
  8035. parameters: list of arguments
  8036. defaultdir: default directory for application (can be nil)
  8037. returns: nil, but can signal an OS error*/
  8038. LISPFUN(shell_execute,seclass_default,0,4,norest,nokey,0,NIL) {
  8039. var object verb_arg = STACK_3;
  8040. var object filename_arg = check_string(STACK_2);
  8041. var object parameters_arg = STACK_1;
  8042. var object defaultdir_arg = STACK_0;
  8043. var int verb_len = 0;
  8044. if (nullp(verb_arg)) pushSTACK(S(nil));
  8045. else {
  8046. pushSTACK(string_to_asciz(check_string(verb_arg),O(misc_encoding)));
  8047. verb_len = Sbvector_length(STACK_0);
  8048. }
  8049. var int filename_len = 0;
  8050. pushSTACK(string_to_asciz(check_string(filename_arg),
  8051. O(misc_encoding)));
  8052. filename_len = Sbvector_length(STACK_0);
  8053. var int parameters_len =
  8054. stringlist_to_asciizlist(parameters_arg,&O(misc_encoding),NULL);
  8055. /* list of asciiz strings is in the STACK */
  8056. var DYNAMIC_ARRAY(parameters,char,parameters_len*2);
  8057. var int parameter_pos = 0;
  8058. while (!nullp(STACK_0)) {
  8059. if (parameter_pos > 0) parameters[parameter_pos++] = ' ';
  8060. parameter_pos +=
  8061. shell_quote(parameters+parameter_pos,TheAsciz(Car(STACK_0)));
  8062. ASSERT(parameter_pos < parameters_len*2);
  8063. STACK_0 = Cdr(STACK_0);
  8064. }
  8065. skipSTACK(1);
  8066. var int defaultdir_len = 0;
  8067. if (nullp(defaultdir_arg)) pushSTACK(S(nil));
  8068. else {
  8069. pushSTACK(string_to_asciz(check_string(defaultdir_arg),
  8070. O(misc_encoding)));
  8071. defaultdir_len = Sbvector_length(STACK_0);
  8072. }
  8073. /* STACK: verb/nil, filename, defaultdir/nil */
  8074. var DYNAMIC_ARRAY(verb,char,1+verb_len);
  8075. var DYNAMIC_ARRAY(filename,char,1+filename_len);
  8076. var DYNAMIC_ARRAY(defaultdir,char,1+defaultdir_len);
  8077. var char *sp, *dp;
  8078. if (!nullp(STACK_2))
  8079. for (sp=TheAsciz(STACK_2),dp=verb;(*dp = *sp);sp++,dp++);
  8080. for (sp=TheAsciz(STACK_1),dp=filename;(*dp = *sp);sp++,dp++);
  8081. if (!nullp(STACK_0))
  8082. for (sp=TheAsciz(STACK_0),dp=defaultdir;(*dp = *sp);sp++,dp++);
  8083. begin_system_call();
  8084. var DWORD result = (DWORD) ShellExecute(NULL,
  8085. nullp(STACK_2)?NULL:verb,
  8086. filename,
  8087. parameters_len?parameters:NULL,
  8088. nullp(STACK_0)?NULL:defaultdir,
  8089. SW_SHOWNORMAL);
  8090. end_system_call();
  8091. if (result <= 32) OS_error();
  8092. FREE_DYNAMIC_ARRAY(defaultdir);
  8093. FREE_DYNAMIC_ARRAY(filename);
  8094. FREE_DYNAMIC_ARRAY(verb);
  8095. FREE_DYNAMIC_ARRAY(parameters);
  8096. skipSTACK(3+4);
  8097. VALUES1(S(nil));
  8098. }
  8099. #endif
  8100. #if defined(UNIX) || defined (WIN32_NATIVE)
  8101. #ifdef UNIX
  8102. /* /dev/null handle. */
  8103. local Handle nullfile (void) {
  8104. var Handle result;
  8105. begin_system_call();
  8106. result = open("/dev/null",O_RDWR);
  8107. end_system_call();
  8108. return result;
  8109. }
  8110. /* obtaining a pipe handle */
  8111. local void mkpipe (Handle * hin, bool dummy1, Handle * hout, bool dummy2) {
  8112. var int handles[2];
  8113. begin_system_call();
  8114. if (pipe(handles)) OS_error();
  8115. end_system_call();
  8116. *hin = (Handle) handles[0];
  8117. *hout = (Handle) handles[1];
  8118. }
  8119. #elif defined(WIN32_NATIVE)
  8120. /* /dev/null on NT/W95. */
  8121. local Handle nullfile (void) {
  8122. var Handle result;
  8123. begin_system_call();
  8124. result = CreateFile("NUL", GENERIC_READ | GENERIC_WRITE,
  8125. FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
  8126. OPEN_EXISTING, 0, NULL);
  8127. end_system_call();
  8128. return result;
  8129. }
  8130. /* obtaining pipe handle */
  8131. local void mkpipe (Handle * hin, bool dupinp, Handle * hout, bool dupoutp) {
  8132. begin_system_call();
  8133. if (!CreatePipe(hin,hout,NULL,0)) { OS_error(); }
  8134. if (dupinp) {/* make it inheritable */
  8135. var Handle hin1 = handle_dup(*hin);
  8136. if (!CloseHandle(*hin)) { OS_error(); }
  8137. *hin = hin1;
  8138. }
  8139. if (dupoutp) {
  8140. var Handle hout1 = handle_dup(*hout);
  8141. if (!CloseHandle(*hout)) { OS_error(); }
  8142. *hout = hout1;
  8143. }
  8144. end_system_call();
  8145. }
  8146. #endif
  8147. local maygc bool init_launch_streamarg
  8148. (gcv_object_t *streamarg, bool child_inputp, Handle stdhandle,
  8149. Handle * h, Handle * ph, Handle * hnull, bool * wait_p) {
  8150. var int handletype = 0;
  8151. *h = INVALID_HANDLE;
  8152. *ph = INVALID_HANDLE;
  8153. if (boundp(*streamarg) && eq(*streamarg,S(Kterminal))
  8154. || !boundp(*streamarg))
  8155. *h = handle_dup(stdhandle);
  8156. else if (nullp(*streamarg)) {
  8157. if (*hnull == INVALID_HANDLE)
  8158. *hnull = nullfile();
  8159. *h = handle_dup(*hnull);
  8160. } else if (eq(*streamarg,S(Kpipe))) {
  8161. if (child_inputp)
  8162. /* make an input pipe for child, ph = parent's handle */
  8163. mkpipe(h,true,ph,false);
  8164. else
  8165. /* make an output pipe for child */
  8166. mkpipe(ph,false,h,true);
  8167. if (*ph == INVALID_HANDLE || *h == INVALID_HANDLE)
  8168. return false;
  8169. *wait_p = false; /* TODO: error when wait_p */
  8170. } else {
  8171. /* child i/o direction is the same as lisp user i/o direction */
  8172. *h = handle_dup(stream_lend_handle(streamarg,child_inputp,&handletype));
  8173. if (handletype != 1)
  8174. return false;
  8175. }
  8176. return (*h != INVALID_HANDLE);
  8177. }
  8178. local maygc void make_launch_pipe
  8179. (gcv_object_t *ret, bool parent_inputp, Handle hparent_pipe, int childpid,
  8180. gcv_object_t *enc, gcv_object_t *eltype, gcv_object_t *buffered) {
  8181. if (hparent_pipe != INVALID_HANDLE) {
  8182. pushSTACK(*enc); /* encoding */
  8183. pushSTACK(*eltype); /* element-type */
  8184. pushSTACK(*buffered); /* buffered */
  8185. *ret = (parent_inputp ? mkips_from_handles : mkops_from_handles)
  8186. (hparent_pipe,childpid); /* replace :PIPE with PIPE-x-STREAM */
  8187. /* stack has been cleaned by callee */
  8188. }
  8189. }
  8190. /* on cygwin, <sigsegv.h> includes <windows.h> therefore *_PRIORITY_CLASS
  8191. macros are already defined */
  8192. #if !defined(NORMAL_PRIORITY_CLASS)
  8193. #define NORMAL_PRIORITY_CLASS 0
  8194. #define HIGH_PRIORITY_CLASS -10
  8195. #define IDLE_PRIORITY_CLASS 10
  8196. #define MY_LOCAL_PRIORITY_CLASSES
  8197. #endif
  8198. #if defined(UNIX)
  8199. #define CloseHandle(h) (close(h)==0)
  8200. #endif
  8201. /* paranoidal close */
  8202. #define ParaClose(h) if (!CloseHandle(h)) { end_system_call(); OS_error(); }
  8203. local maygc sintL interpret_launch_priority (object priority_arg) {
  8204. if (!boundp(priority_arg)) return NORMAL_PRIORITY_CLASS;
  8205. restart_priority:
  8206. if (eq(priority_arg,S(Khigh))) return HIGH_PRIORITY_CLASS;
  8207. else if (eq(priority_arg,S(Klow))) return IDLE_PRIORITY_CLASS;
  8208. else if (eq(priority_arg,S(Knormal))) return NORMAL_PRIORITY_CLASS;
  8209. else if (integerp(priority_arg)) return I_to_L(priority_arg);
  8210. pushSTACK(NIL); /* no PLACE */
  8211. pushSTACK(priority_arg); /* TYPE-ERROR slot DATUM */
  8212. pushSTACK(O(type_priority)); /* TYPE-ERROR slot EXPECTED-TYPE */
  8213. pushSTACK(priority_arg);
  8214. pushSTACK(S(Kpriority));
  8215. pushSTACK(TheSubr(subr_self)->name);
  8216. check_value(type_error,GETTEXT("~S: illegal ~S argument ~S"));
  8217. priority_arg = value1;
  8218. goto restart_priority;
  8219. }
  8220. /* (LAUNCH executable [:arguments] [:wait] [:input] [:output] [:error]
  8221. [:element-type] [:external-format] [:buffered] [:priority])
  8222. Launches a program.
  8223. :arguments : a list of strings (*MISC-ENCODING* is used)
  8224. :wait - nullp/not nullp - whether to wait for process to finish (default T)
  8225. :input, :output, :error - i/o/e streams for process. basically file-streams,
  8226. pipe streams or terminal-streams.
  8227. see stream_lend_handle() in stream.d for full list of supported streams.
  8228. Can be NIL (/dev/null), :pipe (pipe streams are created) or :terminal.
  8229. :element-type, :external-format, :buffered : parameters for created
  8230. pipe-stream if one or more of :input, :output, :error is :pipe.
  8231. :priority : :HIGH/:LOW/:NORMAL or fixnum
  8232. on UNIX - see nice(2)
  8233. on Windows - see CreateProcess dwCreationFlags parameter.
  8234. returns: value1: if wait exit code, child PID otherwise
  8235. value2: NIL or created pipe-output-stream, input stream for child
  8236. value3: NIL or created pipe-input-stream, output stream for child
  8237. value4: NIL or created pipe-input-stream, error stream for child */
  8238. LISPFUN(launch,seclass_default,1,0,norest,key,9,
  8239. (kw(element_type),kw(external_format),kw(buffered),kw(arguments),
  8240. kw(wait),kw(input),kw(output),kw(error),kw(priority))) {
  8241. STACK_9 = check_string(STACK_9); /* command_arg */
  8242. if (!boundp(STACK_5)) STACK_5 = NIL; /* arguments_arg */
  8243. else STACK_5 = check_list(STACK_5);
  8244. var long priority = interpret_launch_priority(STACK_0);
  8245. var bool wait_p = !nullp(STACK_4); /* default: do wait! */
  8246. var Handle hnull = INVALID_HANDLE;
  8247. var Handle hinput;
  8248. var Handle hparent_out; /* in case of pipe */
  8249. /* STACK_3 == input_stream_arg */
  8250. if (!init_launch_streamarg(&STACK_3, true, stdin_handle, &hinput,
  8251. &hparent_out, &hnull, &wait_p))
  8252. OS_error();
  8253. var Handle houtput, hparent_in;
  8254. /* STACK_2 == output_stream_arg */
  8255. if (!init_launch_streamarg(&STACK_2, false, stdout_handle, &houtput,
  8256. &hparent_in, &hnull, &wait_p)) {
  8257. begin_system_call();
  8258. if (hinput != INVALID_HANDLE && hinput != stdin_handle)
  8259. ParaClose(hinput);
  8260. if (hparent_out != INVALID_HANDLE)
  8261. ParaClose(hparent_out);
  8262. end_system_call();
  8263. OS_error();
  8264. }
  8265. var Handle herror, hparent_errin;
  8266. /* STACK_1 == error_stream_arg */
  8267. if (!init_launch_streamarg(&STACK_1, false, stderr_handle, &herror,
  8268. &hparent_errin, &hnull, &wait_p)) {
  8269. begin_system_call();
  8270. if (hinput != INVALID_HANDLE && hinput != stdin_handle)
  8271. ParaClose(hinput);
  8272. if (hparent_out != INVALID_HANDLE)
  8273. ParaClose(hparent_out);
  8274. if (houtput != INVALID_HANDLE && houtput != stdout_handle)
  8275. ParaClose(houtput);
  8276. if (hparent_in != INVALID_HANDLE)
  8277. ParaClose(hparent_in);
  8278. end_system_call();
  8279. OS_error();
  8280. }
  8281. if (hnull != INVALID_HANDLE) {
  8282. begin_system_call();
  8283. ParaClose(hnull);
  8284. end_system_call();
  8285. }
  8286. /* convert command and args to one asciiz string list */
  8287. pushSTACK(allocate_cons());
  8288. Car(STACK_0) = STACK_(9+1); /* command_arg */
  8289. Cdr(STACK_0) = STACK_(5+1); /* arguments_arg */
  8290. var uintL arglist_count = 0;
  8291. var uintL argbuf_len = 1 +
  8292. stringlist_to_asciizlist(STACK_0,&O(misc_encoding),&arglist_count);
  8293. /* STACK: cmdlist, ascizcmdlist */
  8294. STACK_1 = STACK_0;
  8295. skipSTACK(1);
  8296. /* STACK: ascizcmdlist */
  8297. var int child_id = 0;
  8298. #ifdef UNIX
  8299. var DYNAMIC_ARRAY(argv,char*,1+(uintL)arglist_count+1);
  8300. var DYNAMIC_ARRAY(argvdata,char,argbuf_len);
  8301. var object curcons = STACK_0;
  8302. var char** argvptr = &argv[0];
  8303. var char* argvdataptr = &argvdata[0];
  8304. while (consp(curcons)) {
  8305. var uintL len = Sbvector_length(Car(curcons));
  8306. var char* ptr = TheAsciz(Car(curcons));
  8307. *argvptr++ = argvdataptr; /* fill into argv */
  8308. dotimespL(len,len, { *argvdataptr++ = *ptr++; } ); /* and copy */
  8309. curcons = Cdr(curcons);
  8310. };
  8311. *argvptr = NULL; /* and conclude with null */
  8312. skipSTACK(1);
  8313. /* STACK: -- */
  8314. begin_system_call();
  8315. begin_want_sigcld();
  8316. child_id = vfork();
  8317. if (child_id == 0) {/* What ?! I am the clone ?! */
  8318. /* TODO: close ALL unused opened handles since unclosed handles
  8319. (to previously opened pipes) can prevent childs to end up properly */
  8320. #define CHILD_DUP(from,to) \
  8321. if (dup2(from,to) < 0) { \
  8322. fprintf(stderr,"clisp/child: cannot duplicate %d to %d: %s\n", \
  8323. from,to,strerror(errno)); \
  8324. _exit(-1); \
  8325. } \
  8326. if (from>2) \
  8327. close(from)
  8328. CHILD_DUP(hinput,0);
  8329. CHILD_DUP(houtput,1);
  8330. CHILD_DUP(herror,2);
  8331. #undef CHILD_DUP
  8332. /* close child copies of parent's handles */
  8333. if (hparent_out >= 0) close(hparent_out);
  8334. if (hparent_in >= 0) close(hparent_in);
  8335. if (hparent_errin >= 0) close(hparent_errin);
  8336. #ifdef HAVE_NICE
  8337. errno = 0; nice(priority);
  8338. if (errno) {
  8339. fprintf(stderr,"clisp/child: cannot set priority to %d: %s\n",
  8340. priority,strerror(errno));
  8341. _exit(-1);
  8342. }
  8343. #endif
  8344. close_all_fd();
  8345. execvp(*argv,argv);
  8346. fprintf(stderr,"clisp/child: execvp failed: %s\n",strerror(errno));
  8347. _exit(-1);
  8348. } else if (child_id < 0) {
  8349. /* TODO: FIXME: no easy way to be aware of dup2 or exec failures */
  8350. end_want_sigcld();
  8351. end_system_call();
  8352. OS_error();
  8353. }
  8354. var int exit_code = 0;
  8355. if (wait_p) {
  8356. var int status = wait2(child_id);
  8357. exit_code = WEXITSTATUS(status);
  8358. }
  8359. end_want_sigcld();
  8360. /* close our copies of child's handles */
  8361. if (hinput!=stdin_handle) ParaClose(hinput);
  8362. if (houtput!=stdout_handle) ParaClose(houtput);
  8363. if (herror!=stderr_handle) ParaClose(herror);
  8364. end_system_call();
  8365. FREE_DYNAMIC_ARRAY(argv);
  8366. FREE_DYNAMIC_ARRAY(argvdata);
  8367. #else /* WIN32_NATIVE */
  8368. var DYNAMIC_ARRAY(command_data,char,argbuf_len*2);
  8369. /* argbuf_len is multiplied by 2 for quoting sake */
  8370. var int command_pos = 0;
  8371. while (!nullp(STACK_0)) {
  8372. if (command_pos > 0) command_data[command_pos++] = ' ';
  8373. command_pos += shell_quote(command_data+command_pos,
  8374. TheAsciz(Car(STACK_0)));
  8375. ASSERT(command_pos < argbuf_len*2);
  8376. STACK_0 = Cdr(STACK_0);
  8377. }
  8378. skipSTACK(1);
  8379. /* STACK: -- */
  8380. /* Start new process. */
  8381. var HANDLE prochandle;
  8382. var PROCESS_INFORMATION pinfo;
  8383. var STARTUPINFO sinfo;
  8384. sinfo.cb = sizeof(STARTUPINFO);
  8385. sinfo.lpReserved = NULL;
  8386. sinfo.lpDesktop = NULL;
  8387. sinfo.lpTitle = NULL;
  8388. sinfo.cbReserved2 = 0;
  8389. sinfo.lpReserved2 = NULL;
  8390. sinfo.dwFlags = STARTF_USESTDHANDLES;
  8391. sinfo.hStdInput = hinput;
  8392. sinfo.hStdOutput = houtput;
  8393. sinfo.hStdError = herror;
  8394. begin_system_call();
  8395. if (!CreateProcess(NULL, command_data, NULL, NULL, true,
  8396. (DWORD)priority & 0x1E0,
  8397. NULL, NULL, &sinfo, &pinfo))
  8398. { end_system_call(); OS_error(); }
  8399. if (pinfo.hThread) /* zero for 16 bit programs in NT */
  8400. ParaClose(pinfo.hThread);
  8401. prochandle = pinfo.hProcess;
  8402. child_id = pinfo.dwProcessId;
  8403. FREE_DYNAMIC_ARRAY(command_data);
  8404. var DWORD exit_code = 0;
  8405. if (wait_p) {
  8406. /* Wait until it terminates, get its exit status code. */
  8407. switch (WaitForSingleObject(prochandle,INFINITE)) {
  8408. case WAIT_FAILED:
  8409. end_system_call(); OS_error();
  8410. case WAIT_OBJECT_0:
  8411. break;
  8412. default: NOTREACHED;
  8413. }
  8414. if (!GetExitCodeProcess(prochandle,(DWORD*)&exit_code))
  8415. { end_system_call(); OS_error(); }
  8416. }
  8417. /* we can safely close handle of a running process - it doesn't
  8418. lead to process termination */
  8419. ParaClose(prochandle);
  8420. /* close our copies of child's handles */
  8421. if (hinput!=stdin_handle) ParaClose(hinput);
  8422. if (houtput!=stdout_handle) ParaClose(houtput);
  8423. if (herror!=stderr_handle) ParaClose(herror);
  8424. end_system_call();
  8425. #endif
  8426. { /* make pipe-streams */
  8427. gcv_object_t *buff = &STACK_6; /* :BUFFERED */
  8428. gcv_object_t *enc = &STACK_7; /* :ENCODING */
  8429. gcv_object_t *eltype = &STACK_8; /* :ELEMENT-TYPE */
  8430. /* child's input stream, pipe-output from our side */
  8431. make_launch_pipe(&(STACK_3),false,hparent_out,child_id,enc,eltype,buff);
  8432. /* child's output stream, pipe-input from our side
  8433. double analysis of buffered, eltype,encoding
  8434. drawback: slow; advantage: simple iface with stream.d */
  8435. make_launch_pipe(&(STACK_2),true,hparent_in,child_id,enc,eltype,buff);
  8436. /* child's error stream, pipe-input from our side */
  8437. make_launch_pipe(&(STACK_1),true,hparent_errin,child_id,enc,eltype,buff);
  8438. }
  8439. value1 = wait_p ? fixnum(exit_code) : L_to_I(child_id);
  8440. value2 = (hparent_out != INVALID_HANDLE) ? (object)STACK_3 : NIL; /*INPUT*/
  8441. value3 = (hparent_in != INVALID_HANDLE) ? (object)STACK_2 : NIL; /*OUTPUT*/
  8442. value4 = (hparent_errin != INVALID_HANDLE) ? (object)STACK_1 : NIL; /*ERROR*/
  8443. mv_count = 4;
  8444. skipSTACK(10);
  8445. }
  8446. #if defined(MY_LOCAL_PRIORITY_CLASSES)
  8447. #undef MY_LOCAL_PRIORITY_CLASSES
  8448. #undef NORMAL_PRIORITY_CLASS
  8449. #undef HIGH_PRIORITY_CLASS
  8450. #undef IDLE_PRIORITY_CLASS
  8451. #endif
  8452. #if defined(UNIX)
  8453. #undef CloseHandle
  8454. #endif
  8455. #undef ParaClose
  8456. #endif
  8457. /* (SAVEMEM pathname exec-p) stores the memory image at pathname. */
  8458. LISPFUNN(savemem,2) {
  8459. var bool exec_p = !nullp(popSTACK());
  8460. /* execute (OPEN pathname :direction :output) :
  8461. pathname as 1st argument */
  8462. pushSTACK(S(Kdirection)); /* :DIRECTION as 2nd Argument */
  8463. pushSTACK(S(Koutput)); /* :OUTPUT as 3rd Argument */
  8464. #ifdef UNIX
  8465. /* On Unix with mmap() existing .mem-Files may not be simply
  8466. overwritten, because running Lisp-processes would crash due to this.
  8467. So therefore :if-exists :rename-and-delete. */
  8468. #if defined(UNIX_LINUX) && defined(SINGLEMAP_MEMORY)
  8469. /* Under Linux 1.3.20, when the mem file to be saved is on an NFS volume
  8470. and has the same filename as the mem file we started with, the GC
  8471. done by savemem (once the new mem file has been created and still has
  8472. size 0) will crash. Looks like a bug in the Linux NFS client, which
  8473. causes random pages to be mapped in instead of pages from the renamed
  8474. old mem file. Workaround: Do a full GC, forcing all the old mem file's
  8475. contents into memory immediately. */
  8476. gar_col(1);
  8477. #endif
  8478. pushSTACK(S(Kif_exists)); /* :IF-EXISTS as 4th Argument */
  8479. pushSTACK(S(Krename_and_delete)); /* :RENAME-AND-DELETE as 5th Argument */
  8480. funcall(L(open),5);
  8481. #else
  8482. funcall(L(open),3);
  8483. #endif
  8484. /* write memory image into the file:
  8485. (the stream has to be closed by function savemem(),
  8486. also in case of an error.) */
  8487. var off_t file_size = savemem(value1,exec_p);
  8488. VALUES1(off_to_I(file_size));
  8489. }
  8490. #ifdef DYNAMIC_MODULES
  8491. /* (SYSTEM::DYNLOAD-MODULES pathname stringlist)
  8492. loads a shared library, containing a number of modules. */
  8493. LISPFUNN(dynload_modules,2) {
  8494. /* convert pathname into string */
  8495. STACK_1 = coerce_pathname(STACK_1);
  8496. check_no_wildcards(STACK_1);
  8497. STACK_1 = whole_namestring(use_default_dir(STACK_1));
  8498. /* check strings and store in the stack: */
  8499. var uintL stringcount = llength(STACK_0);
  8500. var gcv_object_t* arg_ = &STACK_0;
  8501. {
  8502. var uintL count;
  8503. dotimesL(count,stringcount, {
  8504. Car(*arg_) = check_string(Car(*arg_));
  8505. pushSTACK(string_to_asciz(Car(*arg_),Symbol_value(S(ascii))));
  8506. *arg_ = Cdr(*arg_);
  8507. });
  8508. endp(*arg_); /* test for proper list */
  8509. }
  8510. {
  8511. var const char * libpath = TheAsciz(string_to_asciz(*(arg_ STACKop 1),O(pathname_encoding)));
  8512. var DYNAMIC_ARRAY(modnames,const char *,stringcount);
  8513. if (stringcount > 0) {
  8514. var uintL count;
  8515. var gcv_object_t* ptr1 = STACK STACKop stringcount;
  8516. var const char * * ptr2 = modnames;
  8517. dotimespL(count,stringcount, { *ptr2++ = TheAsciz(NEXT(ptr1)); });
  8518. }
  8519. dynload_modules(libpath,stringcount,modnames);
  8520. FREE_DYNAMIC_ARRAY(modnames);
  8521. }
  8522. skipSTACK(stringcount+1);
  8523. VALUES1(popSTACK()); /* Library-Name as value */
  8524. }
  8525. #endif
  8526. /* =================================================================== */
  8527. #include "execname.c"
  8528. LISPFUNN(program_name,0)
  8529. { /* (SYS::PROGRAM-NAME) returns the executable's name. */
  8530. VALUES1(asciz_to_string(executable_name,O(pathname_encoding)));
  8531. }
  8532. LISPFUNN(lib_directory,0)
  8533. { /* (SYS::LIB-DIRECTORY) returns CLISP's private library directory
  8534. (called $(lisplibdir) in the Makefile). */
  8535. if (!nullp(O(lib_dir))) {
  8536. VALUES1(O(lib_dir));
  8537. } else {
  8538. pushSTACK(TheSubr(subr_self)->name);
  8539. error(error_condition,GETTEXT("~S: library directory is not known, use a command line option to specify it"));
  8540. }
  8541. }
  8542. LISPFUNN(set_lib_directory,1)
  8543. { /* (SYS::SET-LIB-DIRECTORY path) sets the CLISP's private library directory
  8544. (called $(lisplibdir) in the Makefile) */
  8545. var object path = popSTACK();
  8546. if (stringp(path)) path = ensure_last_slash(path);
  8547. VALUES1(O(lib_dir) = coerce_xpathname(path));
  8548. }
  8549. /* ===================================================================== */
  8550. #ifdef DEBUG_TRANSLATE_PATHNAME
  8551. #undef DEBUG_TRANSLATE_PATHNAME
  8552. #undef DOUT
  8553. #endif