PageRenderTime 67ms CodeModel.GetById 20ms 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

Large files files are truncated, but you can click here to view the full file

  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. STA

Large files files are truncated, but you can click here to view the full file