PageRenderTime 173ms CodeModel.GetById 20ms RepoModel.GetById 2ms app.codeStats 1ms

/src/c/pathname.d

https://gitlab.com/evilbinary/ecl
D | 1810 lines | 1514 code | 90 blank | 206 comment | 622 complexity | ab036b25d58663d409c96eb1cd1246d4 MD5 | raw file
Possible License(s): JSON, GPL-2.0, LGPL-2.1, LGPL-2.0

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

  1. /* -*- mode: c; c-basic-offset: 8 -*- */
  2. /*
  3. pathname.d -- Pathnames.
  4. */
  5. /*
  6. Copyright (c) 1984, Taiichi Yuasa and Masami Hagiya.
  7. Copyright (c) 1990, Giuseppe Attardi.
  8. Copyright (c) 2001, Juan Jose Garcia Ripoll.
  9. Copyright (c) 2015, Daniel Kochmański.
  10. ECL is free software; you can redistribute it and/or
  11. modify it under the terms of the GNU Library General Public
  12. License as published by the Free Software Foundation; either
  13. version 2 of the License, or (at your option) any later version.
  14. See file '../Copyright' for full details.
  15. */
  16. /*
  17. O.S. DEPENDENT
  18. This file contains those functions that interpret namestrings.
  19. */
  20. #include <ecl/ecl.h>
  21. #include <ecl/ecl-inl.h>
  22. #include <ecl/internal.h>
  23. #include <limits.h>
  24. #include <string.h>
  25. #include <ctype.h>
  26. typedef int (*delim_fn)(int);
  27. /*
  28. * Translates a string into the host's preferred case.
  29. * See CLHS 19.2.2.1.2.2 Common Case in Pathname Components.
  30. */
  31. /* We use UN*X conventions, so lower case is default.
  32. * However, this really should be conditionalised to the OS type,
  33. * and it should translate to _opposite_ of the local case.
  34. */
  35. static cl_object
  36. normalize_case(cl_object path, cl_object cas)
  37. {
  38. if (cas == @':local') {
  39. if (path->pathname.logical)
  40. return @':upcase';
  41. return @':downcase';
  42. } else if (cas == @':common' || cas == @':downcase' || cas == @':upcase') {
  43. return cas;
  44. } else {
  45. FEerror("Not a valid pathname case :~%~A", 1, cas);
  46. }
  47. }
  48. static bool
  49. in_local_case_p(cl_object str, cl_object cas)
  50. {
  51. if (cas == @':downcase')
  52. return ecl_string_case(str) < 0;
  53. return 1;
  54. }
  55. static bool
  56. in_antilocal_case_p(cl_object str, cl_object cas)
  57. {
  58. if (cas == @':downcase')
  59. return ecl_string_case(str) > 0;
  60. return 0;
  61. }
  62. static cl_object
  63. to_local_case(cl_object str, cl_object cas)
  64. {
  65. if (cas == @':downcase')
  66. return cl_string_downcase(1, str);
  67. return cl_string_upcase(1, str);
  68. }
  69. static cl_object
  70. host_case(cl_object host)
  71. {
  72. if (Null(host))
  73. return @':local';
  74. if (ecl_logical_hostname_p(host))
  75. return @':upcase';
  76. return @':downcase';
  77. }
  78. static cl_object
  79. to_antilocal_case(cl_object str, cl_object cas)
  80. {
  81. if (cas == @':downcase')
  82. return cl_string_upcase(1, str);
  83. return cl_string_upcase(1, str);
  84. }
  85. static cl_object
  86. translate_from_common(cl_object str, cl_object tocase)
  87. {
  88. int string_case = ecl_string_case(str);
  89. if (string_case > 0) { /* ALL_UPPER */
  90. return to_local_case(str, tocase);
  91. } else if (string_case < 0) { /* ALL_LOWER */
  92. return to_antilocal_case(str, tocase);
  93. } else { /* Mixed case goes unchanged */
  94. return str;
  95. }
  96. }
  97. static cl_object
  98. translate_to_common(cl_object str, cl_object fromcase)
  99. {
  100. if (in_local_case_p(str, fromcase)) {
  101. return cl_string_upcase(1, str);
  102. } else if (in_antilocal_case_p(str, fromcase)) {
  103. return cl_string_downcase(1, str);
  104. } else {
  105. return str;
  106. }
  107. }
  108. static cl_object
  109. translate_component_case(cl_object str, cl_object fromcase, cl_object tocase)
  110. {
  111. /* Pathnames may contain some other objects, such as symbols,
  112. * numbers, etc, which need not be translated */
  113. if (str == OBJNULL) {
  114. return str;
  115. } else if (!ECL_STRINGP(str)) {
  116. return str;
  117. } else if (tocase == fromcase) {
  118. return str;
  119. } else if (tocase == @':common') {
  120. return translate_to_common(str, fromcase);
  121. } else if (fromcase == @':common') {
  122. return translate_from_common(str, tocase);
  123. } else {
  124. str = translate_to_common(str, fromcase);
  125. return translate_from_common(str, tocase);
  126. }
  127. }
  128. static cl_object
  129. translate_list_case(cl_object list, cl_object fromcase, cl_object tocase)
  130. {
  131. /* If the argument is really a list, translate all strings in it and
  132. * return this new list, else assume it is a string and translate it.
  133. */
  134. if (!CONSP(list)) {
  135. return translate_component_case(list, fromcase, tocase);
  136. } else {
  137. cl_object l;
  138. list = cl_copy_list(list);
  139. for (l = list; !ecl_endp(l); l = CDR(l)) {
  140. /* It is safe to pass anything to translate_component_case,
  141. * because it will only transform strings, leaving other
  142. * object (such as symbols) unchanged.*/
  143. cl_object name = ECL_CONS_CAR(l);
  144. name = ECL_LISTP(name)?
  145. translate_list_case(name, fromcase, tocase) :
  146. translate_component_case(name, fromcase, tocase);
  147. ECL_RPLACA(l, name);
  148. }
  149. return list;
  150. }
  151. }
  152. static void
  153. push_substring(cl_object buffer, cl_object string, cl_index start, cl_index end)
  154. {
  155. string = cl_string(string);
  156. while (start < end) {
  157. ecl_string_push_extend(buffer, ecl_char(string, start));
  158. start++;
  159. }
  160. }
  161. static void
  162. push_string(cl_object buffer, cl_object string)
  163. {
  164. push_substring(buffer, string, 0, ecl_length(string));
  165. }
  166. static cl_object
  167. destructively_check_directory(cl_object directory, bool logical, bool delete_back)
  168. {
  169. /* This function performs two tasks
  170. * 1) It ensures that the list is a valid directory list
  171. * 2) It ensures that all strings in the list are valid C strings without fill pointer
  172. * All strings are copied, thus avoiding problems with the user modifying the
  173. * list that was passed to MAKE-PATHNAME.
  174. * 3) Redundant :back are removed.
  175. */
  176. /* INV: directory is always a list */
  177. cl_object ptr;
  178. int i;
  179. if (!LISTP(directory))
  180. return @':error';
  181. if (Null(directory))
  182. return directory;
  183. if (ECL_CONS_CAR(directory) != @':absolute' &&
  184. ECL_CONS_CAR(directory) != @':relative')
  185. return @':error';
  186. BEGIN:
  187. for (i=0, ptr=directory; CONSP(ptr); ptr = ECL_CONS_CDR(ptr), i++) {
  188. cl_object item = ECL_CONS_CAR(ptr);
  189. if (item == @':back') {
  190. if (i == 0)
  191. return @':error';
  192. item = ecl_nth(i-1, directory);
  193. if (item == @':absolute' || item == @':wild-inferiors')
  194. return @':error';
  195. if (delete_back && i >= 2) {
  196. cl_object next = ECL_CONS_CDR(ptr);
  197. ptr = ecl_nthcdr(i-2, directory);
  198. ECL_RPLACD(ptr, next);
  199. i = i-2;
  200. }
  201. } else if (item == @':up') {
  202. if (i == 0)
  203. return @':error';
  204. item = ecl_nth(i-1, directory);
  205. if (item == @':absolute' || item == @':wild-inferiors')
  206. return @':error';
  207. } else if (item == @':relative' || item == @':absolute') {
  208. if (i > 0)
  209. return @':error';
  210. } else if (ecl_stringp(item)) {
  211. cl_index l = ecl_length(item);
  212. item = cl_copy_seq(item);
  213. ECL_RPLACA(ptr, item);
  214. if (logical)
  215. continue;
  216. if (l && ecl_char(item,0) == '.') {
  217. if (l == 1) {
  218. /* Single dot */
  219. if (i == 0)
  220. return @':error';
  221. ECL_RPLACD(ecl_nthcdr(--i, directory),
  222. ECL_CONS_CDR(ptr));
  223. } else if (l == 2 && ecl_char(item,1) == '.') {
  224. ECL_RPLACA(ptr, @':up');
  225. goto BEGIN;
  226. }
  227. }
  228. } else if (item != @':wild' && item != @':wild-inferiors') {
  229. return @':error';
  230. }
  231. }
  232. return directory;
  233. }
  234. cl_object
  235. ecl_make_pathname(cl_object host, cl_object device, cl_object directory,
  236. cl_object name, cl_object type, cl_object version,
  237. cl_object fromcase)
  238. {
  239. cl_object x, p, component;
  240. p = ecl_alloc_object(t_pathname);
  241. if (ecl_stringp(host))
  242. p->pathname.logical = ecl_logical_hostname_p(host);
  243. else if (host == ECL_NIL)
  244. p->pathname.logical = FALSE;
  245. else {
  246. x = directory;
  247. component = @':host';
  248. goto ERROR;
  249. }
  250. if (device != ECL_NIL && device != @':unspecific' &&
  251. !(!p->pathname.logical && ecl_stringp(device))) {
  252. x = device;
  253. component = @':device';
  254. goto ERROR;
  255. }
  256. if (name != ECL_NIL && name != @':wild' && !ecl_stringp(name)) {
  257. x = name;
  258. component = @':name';
  259. goto ERROR;
  260. }
  261. if (type != ECL_NIL && type != @':unspecific' && type != @':wild' && !ecl_stringp(type)) {
  262. x = type;
  263. component = @':type';
  264. goto ERROR;
  265. }
  266. if (version != @':unspecific' && version != @':newest' &&
  267. version != @':wild' && version != ECL_NIL && !ECL_FIXNUMP(version))
  268. {
  269. x = version;
  270. component = @':version';
  271. ERROR: FEerror("~s is not a valid pathname-~a component", 2, x, component);
  272. }
  273. switch (ecl_t_of(directory)) {
  274. #ifdef ECL_UNICODE
  275. case t_string:
  276. #endif
  277. case t_base_string:
  278. directory = cl_list(2, @':absolute', directory);
  279. break;
  280. case t_symbol:
  281. if (directory == @':wild') {
  282. directory = cl_list(2, @':absolute', @':wild-inferiors');
  283. break;
  284. }
  285. x = directory;
  286. component = @':directory';
  287. goto ERROR;
  288. case t_list:
  289. directory = cl_copy_list(directory);
  290. break;
  291. default:
  292. x = directory;
  293. component = @':directory';
  294. goto ERROR;
  295. }
  296. p->pathname.host = host;
  297. {
  298. cl_object tocase = normalize_case(p, @':local');
  299. if (p->pathname.logical)
  300. fromcase = @':common';
  301. else
  302. fromcase = normalize_case(p, fromcase);
  303. p->pathname.host =
  304. translate_component_case(host, fromcase, tocase);
  305. p->pathname.device =
  306. translate_component_case(device, fromcase, tocase);
  307. p->pathname.directory =
  308. directory =
  309. translate_list_case(directory, fromcase, tocase);
  310. p->pathname.name =
  311. translate_component_case(name, fromcase, tocase);
  312. p->pathname.type =
  313. translate_component_case(type, fromcase, tocase);
  314. p->pathname.version = version;
  315. }
  316. directory = destructively_check_directory(directory, p->pathname.logical, 0);
  317. unlikely_if (directory == @':error') {
  318. cl_error(3, @'file-error', @':pathname', p);
  319. }
  320. p->pathname.directory = directory;
  321. return(p);
  322. }
  323. static cl_object
  324. tilde_expand(cl_object pathname)
  325. {
  326. /*
  327. * If the pathname is a physical one, without hostname, without device
  328. * and the first element is either a tilde '~' or '~' followed by
  329. * a user name, we merge the user homedir pathname with this one.
  330. */
  331. cl_object directory, head;
  332. if (pathname->pathname.logical || pathname->pathname.host != ECL_NIL
  333. || pathname->pathname.device != ECL_NIL) {
  334. return pathname;
  335. }
  336. directory = pathname->pathname.directory;
  337. if (!CONSP(directory) || ECL_CONS_CAR(directory) != @':relative'
  338. || ECL_CONS_CDR(directory) == ECL_NIL) {
  339. return pathname;
  340. }
  341. head = CADR(directory);
  342. if (ecl_stringp(head) && ecl_length(head) > 0 &&
  343. ecl_char(head,0) == '~') {
  344. /* Remove the tilde component */
  345. ECL_RPLACD(directory, CDDR(directory));
  346. pathname = cl_merge_pathnames(2, pathname,
  347. ecl_homedir_pathname(head));
  348. }
  349. return pathname;
  350. }
  351. #define WORD_INCLUDE_DELIM 1
  352. #define WORD_ALLOW_ASTERISK 2
  353. #define WORD_EMPTY_IS_NIL 4
  354. #define WORD_LOGICAL 8
  355. #define WORD_SEARCH_LAST_DOT 16
  356. #define WORD_ALLOW_LEADING_DOT 32
  357. #define WORD_DISALLOW_SLASH 64
  358. #define WORD_DISALLOW_SEMICOLON 128
  359. static cl_object
  360. make_one(cl_object s, cl_index start, cl_index end)
  361. {
  362. return cl_subseq(3, s, ecl_make_fixnum(start), ecl_make_fixnum(end));
  363. }
  364. static int is_colon(int c) { return c == ':'; }
  365. static int is_slash(int c) { return IS_DIR_SEPARATOR(c); }
  366. static int is_semicolon(int c) { return c == ';'; }
  367. static int is_dot(int c) { return c == '.'; }
  368. static int is_null(int c) { return c == '\0'; }
  369. /*
  370. * Parses a word from string `S' until either:
  371. * 1) character `DELIM' is found
  372. * 2) end of string is reached
  373. * 3) a non valid character is found
  374. * Output is either
  375. * 1) :error in case (3) above
  376. * 2) :wild, :wild-inferiors, :up
  377. * 3) "" or ECL_NIL when word has no elements
  378. * 5) A non empty string
  379. */
  380. static cl_object
  381. parse_word(cl_object s, delim_fn delim, int flags, cl_index start,
  382. cl_index end, cl_index *end_of_word)
  383. {
  384. cl_index i, j, last_delim = end;
  385. bool wild_inferiors = FALSE;
  386. i = j = start;
  387. for (; i < end; i++) {
  388. bool valid_char;
  389. cl_index c = ecl_char(s, i);
  390. if (delim(c)) {
  391. if ((i == start) && (flags & WORD_ALLOW_LEADING_DOT)) {
  392. /* Leading dot is included */
  393. continue;
  394. }
  395. last_delim = i;
  396. if (!(flags & WORD_SEARCH_LAST_DOT)) {
  397. break;
  398. }
  399. }
  400. if (c == '*') {
  401. if (!(flags & WORD_ALLOW_ASTERISK))
  402. valid_char = FALSE; /* Asterisks not allowed in this word */
  403. else {
  404. wild_inferiors = (i > start && ecl_char(s, i-1) == '*');
  405. valid_char = TRUE; /* single "*" */
  406. }
  407. } else if (c == ';' && (flags & WORD_DISALLOW_SEMICOLON)) {
  408. valid_char = 0;
  409. } else if (c == '/' && (flags & WORD_DISALLOW_SLASH)) {
  410. valid_char = 0;
  411. } else {
  412. valid_char = c != 0;
  413. }
  414. if (!valid_char) {
  415. *end_of_word = start;
  416. return @':error';
  417. }
  418. }
  419. if (i > last_delim) {
  420. /* Go back to the position of the last delimiter */
  421. i = last_delim;
  422. }
  423. if (i < end) {
  424. *end_of_word = i+1;
  425. } else {
  426. *end_of_word = end;
  427. /* We have reached the end of the string without finding
  428. the proper delimiter */
  429. if (flags & WORD_INCLUDE_DELIM) {
  430. *end_of_word = start;
  431. return ECL_NIL;
  432. }
  433. }
  434. switch(i-j) {
  435. case 0:
  436. if (flags & WORD_EMPTY_IS_NIL)
  437. return ECL_NIL;
  438. return cl_core.null_string;
  439. case 1:
  440. if (ecl_char(s,j) == '*')
  441. return @':wild';
  442. break;
  443. case 2: {
  444. cl_index c0 = ecl_char(s,j);
  445. cl_index c1 = ecl_char(s,j+1);
  446. if (c0 == '*' && c1 == '*')
  447. return @':wild-inferiors';
  448. if (!(flags & WORD_LOGICAL) && c0 == '.' && c1 == '.')
  449. return @':up';
  450. break;
  451. }
  452. default:
  453. if (wild_inferiors) /* '**' surrounded by other characters */
  454. return @':error';
  455. }
  456. return make_one(s, j, i);
  457. }
  458. /*
  459. * Parses a logical or physical directory tree. Output is always a
  460. * list of valid directory components, which may be just NIL.
  461. *
  462. * INV: When parsing of directory components has failed, a valid list
  463. * is also returned, and it will be later in the parsing of
  464. * pathname-name or pathname-type when the same error is detected.
  465. */
  466. static cl_object
  467. parse_directories(cl_object s, int flags, cl_index start, cl_index end,
  468. cl_index *end_of_dir)
  469. {
  470. cl_index i, j;
  471. cl_object path = ECL_NIL;
  472. delim_fn delim = (flags & WORD_LOGICAL) ? is_semicolon : is_slash;
  473. flags |= WORD_INCLUDE_DELIM | WORD_ALLOW_ASTERISK;
  474. *end_of_dir = start;
  475. for (i = j = start; i < end; j = i) {
  476. cl_object part = parse_word(s, delim, flags, j, end, &i);
  477. if (part == @':error' || part == ECL_NIL)
  478. break;
  479. if (part == cl_core.null_string) { /* "/", ";" */
  480. if (j != start) {
  481. if (flags & WORD_LOGICAL)
  482. return @':error';
  483. *end_of_dir = i;
  484. continue;
  485. }
  486. part = (flags & WORD_LOGICAL) ? @':relative' : @':absolute';
  487. }
  488. *end_of_dir = i;
  489. path = ecl_cons(part, path);
  490. }
  491. return cl_nreverse(path);
  492. }
  493. bool
  494. ecl_logical_hostname_p(cl_object host)
  495. {
  496. if (!ecl_stringp(host))
  497. return FALSE;
  498. return !Null(@assoc(4, host, cl_core.pathname_translations, @':test', @'string-equal'));
  499. }
  500. /*
  501. * Parses a lisp namestring until the whole substring is parsed or an
  502. * error is found. It returns a valid pathname or NIL, plus the place
  503. * where parsing ended in *END_OF_PARSING.
  504. *
  505. * The rules are as follows:
  506. *
  507. * 1) If a hostname is supplied it determines whether the namestring
  508. * will be parsed as logical or as physical.
  509. *
  510. * 2) If no hostname is supplied, first it tries parsing using logical
  511. * pathname rules and, if no logical hostname is found, then it
  512. * tries the physical pathname format.
  513. *
  514. * 3) Logical pathname syntax:
  515. * [logical-hostname:][;][logical-directory-component;][pathname-name][.pathname-type]
  516. *
  517. * 4) Physical pathname syntax:
  518. * [device:][[//hostname]/][directory-component/]*[pathname-name][.pathname-type]
  519. *
  520. * logical-hostname, device, hostname = word
  521. * logical-directory-component = word | wildcard-word
  522. * directory-component = word | wildcard-word | '..' | '.'
  523. * pathname-name, pathname-type = word | wildcard-word | ""
  524. *
  525. */
  526. cl_object
  527. ecl_parse_namestring(cl_object s, cl_index start, cl_index end, cl_index *ep,
  528. cl_object default_host)
  529. {
  530. cl_object host, device, path, name, type, aux, version;
  531. bool logical;
  532. if (start == end) {
  533. host = device = path = name = type = aux = version = @'nil';
  534. logical = 0;
  535. goto make_it;
  536. }
  537. /* We first try parsing as logical-pathname. In case of
  538. * failure, physical-pathname parsing is performed only when
  539. * there is no supplied *logical* host name. All other failures
  540. * result in ECL_NIL as output.
  541. */
  542. host = parse_word(s, is_colon, WORD_LOGICAL | WORD_INCLUDE_DELIM |
  543. WORD_DISALLOW_SEMICOLON, start, end, ep);
  544. if (default_host != ECL_NIL) {
  545. if (host == ECL_NIL || host == @':error')
  546. host = default_host;
  547. }
  548. if (!ecl_logical_hostname_p(host))
  549. goto physical;
  550. /*
  551. * Logical pathname format:
  552. * [logical-hostname:][;][logical-directory-component;][pathname-name][.pathname-type]
  553. */
  554. logical = TRUE;
  555. device = @':unspecific';
  556. path = parse_directories(s, WORD_LOGICAL, *ep, end, ep);
  557. if (CONSP(path)) {
  558. if (ECL_CONS_CAR(path) != @':relative' &&
  559. ECL_CONS_CAR(path) != @':absolute')
  560. path = CONS(@':absolute', path);
  561. path = destructively_check_directory(path, TRUE, FALSE);
  562. } else {
  563. path = CONS(@':absolute', path);
  564. }
  565. if (path == @':error')
  566. return ECL_NIL;
  567. name = parse_word(s, is_dot, WORD_LOGICAL | WORD_ALLOW_ASTERISK |
  568. WORD_EMPTY_IS_NIL, *ep, end, ep);
  569. if (name == @':error')
  570. return ECL_NIL;
  571. type = ECL_NIL;
  572. version = ECL_NIL;
  573. if (*ep == start || ecl_char(s, *ep-1) != '.')
  574. goto make_it;
  575. type = parse_word(s, is_dot, WORD_LOGICAL | WORD_ALLOW_ASTERISK |
  576. WORD_EMPTY_IS_NIL, *ep, end, ep);
  577. if (type == @':error')
  578. return ECL_NIL;
  579. if (*ep == start || ecl_char(s, *ep-1) != '.')
  580. goto make_it;
  581. aux = parse_word(s, is_null, WORD_LOGICAL | WORD_ALLOW_ASTERISK |
  582. WORD_EMPTY_IS_NIL, *ep, end, ep);
  583. if (aux == @':error') {
  584. return ECL_NIL;
  585. } else if (ECL_SYMBOLP(aux)) {
  586. version = aux;
  587. } else {
  588. const cl_env_ptr the_env = ecl_process_env();
  589. cl_object parsed_length;
  590. version = cl_parse_integer(3, aux, @':junk-allowed', ECL_T);
  591. parsed_length = ecl_nth_value(the_env, 1);
  592. if (ecl_fixnum(parsed_length) == ecl_length(aux) &&
  593. cl_integerp(version) != ECL_NIL && ecl_plusp(version))
  594. ;
  595. else if (cl_string_equal(2, aux, @':newest') != ECL_NIL)
  596. version = @':newest';
  597. else
  598. return ECL_NIL;
  599. }
  600. goto make_it;
  601. physical:
  602. /*
  603. * Physical pathname format:
  604. * [[device:[//hostname]]/][directory-component/]*[pathname-name][.pathname-type]
  605. */
  606. logical = FALSE;
  607. /* We only parse a hostname when the device was present. This
  608. * requisite is a bit stupid and only applies to the Unix port,
  609. * where "//home/" is equivalent to "/home" However, in Windows
  610. * we need "//FOO/" to be separately handled, for it is a shared
  611. * resource.
  612. */
  613. #if defined(ECL_MS_WINDOWS_HOST)
  614. if ((start+1 <= end) && is_slash(ecl_char(s, start))) {
  615. device = ECL_NIL;
  616. goto maybe_parse_host;
  617. }
  618. #endif
  619. device = parse_word(s, is_colon, WORD_INCLUDE_DELIM | WORD_EMPTY_IS_NIL |
  620. WORD_DISALLOW_SLASH, start, end, ep);
  621. if (device == @':error' || device == ECL_NIL) {
  622. device = ECL_NIL;
  623. host = ECL_NIL;
  624. goto done_device_and_host;
  625. }
  626. if (!ecl_stringp(device)) {
  627. return ECL_NIL;
  628. }
  629. maybe_parse_host:
  630. /* Files have no effective device. */
  631. if (@string-equal(2, device, @':file') == ECL_T)
  632. device = ECL_NIL;
  633. start = *ep;
  634. host = ECL_NIL;
  635. if ((start+2) <= end && is_slash(ecl_char(s, start)) &&
  636. is_slash(ecl_char(s, start+1)))
  637. {
  638. host = parse_word(s, is_slash, WORD_EMPTY_IS_NIL,
  639. start+2, end, ep);
  640. if (host == @':error') {
  641. host = ECL_NIL;
  642. } else if (host != ECL_NIL) {
  643. if (!ecl_stringp(host))
  644. return ECL_NIL;
  645. start = *ep;
  646. if (is_slash(ecl_char(s,--start)))
  647. *ep = start;
  648. }
  649. }
  650. if (ecl_length(device) == 0)
  651. device = ECL_NIL;
  652. done_device_and_host:
  653. path = parse_directories(s, 0, *ep, end, ep);
  654. if (CONSP(path)) {
  655. if (ECL_CONS_CAR(path) != @':relative' &&
  656. ECL_CONS_CAR(path) != @':absolute')
  657. path = CONS(@':relative', path);
  658. path = destructively_check_directory(path, FALSE, FALSE);
  659. }
  660. if (path == @':error')
  661. return ECL_NIL;
  662. start = *ep;
  663. name = parse_word(s, is_dot,
  664. WORD_ALLOW_LEADING_DOT | WORD_SEARCH_LAST_DOT |
  665. WORD_ALLOW_ASTERISK | WORD_EMPTY_IS_NIL,
  666. start, end, ep);
  667. if (name == @':error')
  668. return ECL_NIL;
  669. if ((*ep - start) <= 1 || ecl_char(s, *ep-1) != '.') {
  670. type = ECL_NIL;
  671. } else {
  672. type = parse_word(s, is_null, WORD_ALLOW_ASTERISK, *ep, end, ep);
  673. if (type == @':error')
  674. return ECL_NIL;
  675. }
  676. version = (name != ECL_NIL || type != ECL_NIL) ? @':newest' : ECL_NIL;
  677. make_it:
  678. if (*ep >= end) *ep = end;
  679. path = ecl_make_pathname(host, device, path, name, type, version,
  680. @':local');
  681. path->pathname.logical = logical;
  682. return tilde_expand(path);
  683. }
  684. cl_object
  685. si_default_pathname_defaults(void)
  686. {
  687. /* This routine outputs the value of *default-pathname-defaults*
  688. * coerced to type PATHNAME. Special care is taken so that we do
  689. * not enter an infinite loop when using PARSE-NAMESTRING, because
  690. * this routine might itself try to use the value of this variable. */
  691. cl_object path = ecl_symbol_value(@'*default-pathname-defaults*');
  692. unlikely_if (!ECL_PATHNAMEP(path)) {
  693. const cl_env_ptr the_env = ecl_process_env();
  694. ecl_bds_bind(the_env, @'*default-pathname-defaults*', si_getcwd(0));
  695. FEwrong_type_key_arg(@[pathname], @[*default-pathname-defaults*],
  696. path, @'pathname');
  697. }
  698. @(return path)
  699. }
  700. cl_object
  701. cl_pathname(cl_object x)
  702. {
  703. L:
  704. switch (ecl_t_of(x)) {
  705. #ifdef ECL_UNICODE
  706. case t_string:
  707. #endif
  708. case t_base_string:
  709. x = cl_parse_namestring(1, x);
  710. case t_pathname:
  711. break;
  712. case t_stream:
  713. switch ((enum ecl_smmode)x->stream.mode) {
  714. case ecl_smm_input:
  715. case ecl_smm_output:
  716. case ecl_smm_probe:
  717. case ecl_smm_io:
  718. case ecl_smm_input_file:
  719. case ecl_smm_output_file:
  720. case ecl_smm_io_file:
  721. x = IO_STREAM_FILENAME(x);
  722. goto L;
  723. case ecl_smm_synonym:
  724. x = SYNONYM_STREAM_STREAM(x);
  725. goto L;
  726. default:
  727. ;/* Fall through to error message */
  728. }
  729. default: {
  730. const char *type = "(OR FILE-STREAM STRING PATHNAME)";
  731. FEwrong_type_only_arg(@[pathname], x, ecl_read_from_cstring(type));
  732. }
  733. }
  734. @(return x)
  735. }
  736. cl_object
  737. cl_logical_pathname(cl_object x)
  738. {
  739. x = cl_pathname(x);
  740. if (!x->pathname.logical) {
  741. cl_error(9, @'simple-type-error', @':format-control',
  742. make_constant_base_string("~S cannot be coerced to a logical pathname."),
  743. @':format-arguments', cl_list(1, x),
  744. @':expected-type', @'logical-pathname',
  745. @':datum', x);
  746. }
  747. @(return x);
  748. }
  749. /* FIXME! WILD-PATHNAME-P is missing! */
  750. @(defun wild-pathname-p (pathname &optional component)
  751. bool checked = 0;
  752. @
  753. pathname = cl_pathname(pathname);
  754. if (component == ECL_NIL || component == @':host') {
  755. if (pathname->pathname.host == @':wild')
  756. @(return ECL_T);
  757. checked = 1;
  758. }
  759. if (component == ECL_NIL || component == @':device') {
  760. if (pathname->pathname.device == @':wild')
  761. @(return ECL_T);
  762. checked = 1;
  763. }
  764. if (component == ECL_NIL || component == @':version') {
  765. if (pathname->pathname.version == @':wild')
  766. @(return ECL_T);
  767. checked = 1;
  768. }
  769. if (component == ECL_NIL || component == @':name') {
  770. cl_object name = pathname->pathname.name;
  771. if (name != ECL_NIL &&
  772. (name == @':wild' || ecl_wild_string_p(name)))
  773. @(return ECL_T);
  774. checked = 1;
  775. }
  776. if (component == ECL_NIL || component == @':type') {
  777. cl_object name = pathname->pathname.type;
  778. if (name != ECL_NIL &&
  779. (name == @':wild' || ecl_wild_string_p(name)))
  780. @(return ECL_T);
  781. checked = 1;
  782. }
  783. if (component == ECL_NIL || component == @':directory') {
  784. cl_object list = pathname->pathname.directory;
  785. checked = 1;
  786. loop_for_on_unsafe(list) {
  787. cl_object name = ECL_CONS_CAR(list);
  788. if (name != ECL_NIL &&
  789. (name == @':wild' || name == @':wild-inferiors' ||
  790. ecl_wild_string_p(name)))
  791. {
  792. @(return ECL_T)
  793. }
  794. } end_loop_for_on_unsafe(list);
  795. }
  796. if (checked == 0) {
  797. FEerror("~A is not a valid pathname component", 1, component);
  798. }
  799. @(return ECL_NIL)
  800. @)
  801. /*
  802. * coerce_to_file_pathname(P) converts P to a physical pathname,
  803. * for a file which is accesible in our filesystem.
  804. * INV: Wildcards are allowed.
  805. * INV: A fresh new copy of the pathname is created.
  806. * INV: The pathname is absolute.
  807. */
  808. cl_object
  809. coerce_to_file_pathname(cl_object pathname)
  810. {
  811. pathname = coerce_to_physical_pathname(pathname);
  812. pathname = cl_merge_pathnames(1, pathname);
  813. #if 0
  814. #if !defined(cygwin) && !defined(ECL_MS_WINDOWS_HOST)
  815. if (pathname->pathname.device != ECL_NIL)
  816. FEerror("Device ~S not yet supported.", 1,
  817. pathname->pathname.device);
  818. if (pathname->pathname.host != ECL_NIL)
  819. FEerror("Access to remote files not yet supported.", 0);
  820. #endif
  821. #endif
  822. if (pathname->pathname.directory == ECL_NIL ||
  823. ECL_CONS_CAR(pathname->pathname.directory) == @':relative') {
  824. pathname = cl_merge_pathnames(2, pathname, si_getcwd(0));
  825. }
  826. return pathname;
  827. }
  828. /*
  829. * coerce_to_physical_pathname(P) converts P to a physical pathname,
  830. * performing the appropiate transformation if P was a logical pathname.
  831. */
  832. cl_object
  833. coerce_to_physical_pathname(cl_object x)
  834. {
  835. x = cl_pathname(x);
  836. if (x->pathname.logical)
  837. return cl_translate_logical_pathname(1, x);
  838. return x;
  839. }
  840. /*
  841. * si_coerce_to_filename(P) converts P to a physical pathname and then to
  842. * a namestring. The output must always be a new simple-string which can
  843. * be used by the C library.
  844. * INV: No wildcards are allowed.
  845. */
  846. cl_object
  847. si_coerce_to_filename(cl_object pathname_orig)
  848. {
  849. cl_object namestring, pathname;
  850. /* We always go through the pathname representation and thus
  851. * cl_namestring() always outputs a fresh new string */
  852. pathname = coerce_to_file_pathname(pathname_orig);
  853. if (cl_wild_pathname_p(1,pathname) != ECL_NIL)
  854. cl_error(3, @'file-error', @':pathname', pathname_orig);
  855. namestring = ecl_namestring(pathname,
  856. ECL_NAMESTRING_TRUNCATE_IF_ERROR |
  857. ECL_NAMESTRING_FORCE_BASE_STRING);
  858. if (namestring == ECL_NIL) {
  859. FEerror("Pathname without a physical namestring:"
  860. "~% :HOST ~A"
  861. "~% :DEVICE ~A"
  862. "~% :DIRECTORY ~A"
  863. "~% :NAME ~A"
  864. "~% :TYPE ~A"
  865. "~% :VERSION ~A",
  866. 6, pathname_orig->pathname.host,
  867. pathname_orig->pathname.device,
  868. pathname_orig->pathname.directory,
  869. pathname_orig->pathname.name,
  870. pathname_orig->pathname.type,
  871. pathname_orig->pathname.version);
  872. }
  873. if (cl_core.path_max != -1 &&
  874. ecl_length(namestring) >= cl_core.path_max - 16)
  875. FEerror("Too long filename: ~S.", 1, namestring);
  876. return namestring;
  877. }
  878. #define default_device(host) ECL_NIL
  879. cl_object
  880. ecl_merge_pathnames(cl_object path, cl_object defaults, cl_object default_version)
  881. {
  882. cl_object host, device, directory, name, type, version;
  883. cl_object tocase;
  884. defaults = cl_pathname(defaults);
  885. path = cl_parse_namestring(1, path, ECL_NIL, defaults);
  886. if (Null(host = path->pathname.host))
  887. host = defaults->pathname.host;
  888. tocase = host_case(host);
  889. if (Null(path->pathname.device)) {
  890. if (Null(path->pathname.host))
  891. device = cl_pathname_device(3, defaults, @':case', tocase);
  892. else if (path->pathname.host == defaults->pathname.host)
  893. device = defaults->pathname.device;
  894. else
  895. device = default_device(path->pathname.host);
  896. } else {
  897. device = path->pathname.device;
  898. }
  899. if (Null(path->pathname.directory)) {
  900. directory = cl_pathname_directory(3, defaults, @':case', tocase);
  901. } else if (ECL_CONS_CAR(path->pathname.directory) == @':absolute') {
  902. directory = path->pathname.directory;
  903. } else if (!Null(defaults->pathname.directory)) {
  904. directory = ecl_append(cl_pathname_directory(3, defaults,
  905. @':case', tocase),
  906. CDR(path->pathname.directory));
  907. /* Eliminate redundant :back */
  908. directory = destructively_check_directory(directory, TRUE, TRUE);
  909. } else {
  910. directory = path->pathname.directory;
  911. }
  912. if (Null(name = path->pathname.name)) {
  913. name = cl_pathname_name(3, defaults, @':case', tocase);
  914. }
  915. if (Null(type = path->pathname.type)) {
  916. type = cl_pathname_type(3, defaults, @':case', tocase);
  917. }
  918. version = path->pathname.version;
  919. if (Null(path->pathname.name)) {
  920. if (Null(version))
  921. version = defaults->pathname.version;
  922. }
  923. if (Null(version)) {
  924. version = default_version;
  925. }
  926. if (default_version == @':default') {
  927. if (Null(name) && Null(type)) {
  928. version = ECL_NIL;
  929. } else {
  930. version = @':newest';
  931. }
  932. }
  933. /*
  934. In this implementation, version is not considered
  935. */
  936. defaults = ecl_make_pathname(host, device, directory, name,
  937. type, version, tocase);
  938. return defaults;
  939. }
  940. /*
  941. ecl_namestring(x, flag) converts a pathname to a namestring.
  942. if flag is true, then the pathname may be coerced to the requirements
  943. of the filesystem, removing fields that have no meaning (such as
  944. version, or type, etc); otherwise, when it is not possible to
  945. produce a readable representation of the pathname, NIL is returned.
  946. */
  947. cl_object
  948. ecl_namestring(cl_object x, int flags)
  949. {
  950. bool logical;
  951. cl_object l, y;
  952. cl_object buffer, host;
  953. bool truncate_if_unreadable = flags & ECL_NAMESTRING_TRUNCATE_IF_ERROR;
  954. x = cl_pathname(x);
  955. /* INV: Pathnames can only be created by mergin, parsing namestrings
  956. * or using ecl_make_pathname(). In all of these cases ECL will complain
  957. * at creation time if the pathname has wrong components.
  958. */
  959. buffer = ecl_make_string_output_stream(128, 1);
  960. logical = x->pathname.logical;
  961. host = x->pathname.host;
  962. if (logical) {
  963. if ((y = x->pathname.device) != @':unspecific' &&
  964. truncate_if_unreadable)
  965. return ECL_NIL;
  966. if (host != ECL_NIL) {
  967. si_do_write_sequence(host, buffer, ecl_make_fixnum(0), ECL_NIL);
  968. writestr_stream(":", buffer);
  969. }
  970. } else {
  971. if ((y = x->pathname.device) != ECL_NIL) {
  972. si_do_write_sequence(y, buffer, ecl_make_fixnum(0), ECL_NIL);
  973. writestr_stream(":", buffer);
  974. }
  975. if (host != ECL_NIL) {
  976. #if !defined(ECL_MS_WINDOWS_HOST)
  977. if (y == ECL_NIL) {
  978. writestr_stream("file:", buffer);
  979. }
  980. #endif
  981. writestr_stream("//", buffer);
  982. si_do_write_sequence(host, buffer, ecl_make_fixnum(0), ECL_NIL);
  983. }
  984. }
  985. l = x->pathname.directory;
  986. if (ecl_endp(l))
  987. goto NO_DIRECTORY;
  988. y = ECL_CONS_CAR(l);
  989. if (y == @':relative') {
  990. if (logical)
  991. ecl_write_char(';', buffer);
  992. } else {
  993. if (!logical)
  994. ecl_write_char(DIR_SEPARATOR, buffer);
  995. }
  996. l = ECL_CONS_CDR(l);
  997. loop_for_in(l) {
  998. y = ECL_CONS_CAR(l);
  999. if (y == @':up') {
  1000. writestr_stream("..", buffer);
  1001. } else if (y == @':wild') {
  1002. writestr_stream("*", buffer);
  1003. } else if (y == @':wild-inferiors') {
  1004. writestr_stream("**", buffer);
  1005. } else if (y != @':back') {
  1006. si_do_write_sequence(y, buffer, ecl_make_fixnum(0), ECL_NIL);
  1007. } else {
  1008. /* Directory :back has no namestring representation */
  1009. return ECL_NIL;
  1010. }
  1011. ecl_write_char(logical? ';' : DIR_SEPARATOR, buffer);
  1012. } end_loop_for_in;
  1013. NO_DIRECTORY:
  1014. if (ecl_file_position(buffer) == ecl_make_fixnum(0)) {
  1015. if ((ecl_stringp(x->pathname.name) &&
  1016. ecl_member_char(':', x->pathname.name)) ||
  1017. (ecl_stringp(x->pathname.type) &&
  1018. ecl_member_char(':', x->pathname.type)))
  1019. writestr_stream(":", buffer);
  1020. }
  1021. y = x->pathname.name;
  1022. if (y != ECL_NIL) {
  1023. if (y == @':wild') {
  1024. writestr_stream("*", buffer);
  1025. } else {
  1026. si_do_write_sequence(y, buffer, ecl_make_fixnum(0), ECL_NIL);
  1027. }
  1028. } else if (!logical && !Null(x->pathname.type)) {
  1029. /* #P".txt" is :NAME = ".txt" :TYPE = NIL and
  1030. hence :NAME = NIL and :TYPE != NIL does not have
  1031. a printed representation */
  1032. return ECL_NIL;
  1033. }
  1034. y = x->pathname.type;
  1035. if (y == @':unspecific') {
  1036. return ECL_NIL;
  1037. } else if (y != ECL_NIL) {
  1038. if (y == @':wild') {
  1039. writestr_stream(".*", buffer);
  1040. } else {
  1041. writestr_stream(".", buffer);
  1042. si_do_write_sequence(y, buffer, ecl_make_fixnum(0), ECL_NIL);
  1043. }
  1044. }
  1045. y = x->pathname.version;
  1046. if (logical) {
  1047. if (y != ECL_NIL) {
  1048. writestr_stream(".", buffer);
  1049. if (y == @':wild') {
  1050. writestr_stream("*", buffer);
  1051. } else if (y == @':newest') {
  1052. si_do_write_sequence(ecl_symbol_name(y), buffer,
  1053. ecl_make_fixnum(0), ECL_NIL);
  1054. } else {
  1055. /* Since the printer is not reentrant,
  1056. * we cannot use cl_write and friends.
  1057. */
  1058. int n = ecl_fixnum(y), i;
  1059. char b[FIXNUM_BITS/2];
  1060. for (i = 0; n; i++) {
  1061. b[i] = n%10 + '0';
  1062. n = n/10;
  1063. }
  1064. if (i == 0)
  1065. b[i++] = '0';
  1066. while (i--) {
  1067. ecl_write_char(b[i], buffer);
  1068. }
  1069. }
  1070. }
  1071. } else if (!truncate_if_unreadable) {
  1072. /* Namestrings of physical pathnames have restrictions... */
  1073. if (Null(x->pathname.name) && Null(x->pathname.type)) {
  1074. /* Directories cannot have a version number */
  1075. if (y != ECL_NIL)
  1076. return ECL_NIL;
  1077. } else if (y != @':newest') {
  1078. /* Filenames have an implicit version :newest */
  1079. return ECL_NIL;
  1080. }
  1081. }
  1082. buffer = cl_get_output_stream_string(buffer);
  1083. #ifdef ECL_UNICODE
  1084. if (ECL_EXTENDED_STRING_P(buffer) &&
  1085. (flags & ECL_NAMESTRING_FORCE_BASE_STRING)) {
  1086. unlikely_if (!ecl_fits_in_base_string(buffer))
  1087. FEerror("The filesystem does not accept filenames "
  1088. "with extended characters: ~S",
  1089. 1, buffer);
  1090. buffer = si_copy_to_simple_base_string(buffer);
  1091. }
  1092. #endif
  1093. return buffer;
  1094. }
  1095. cl_object
  1096. cl_namestring(cl_object x)
  1097. {
  1098. @(return ecl_namestring(x, ECL_NAMESTRING_TRUNCATE_IF_ERROR))
  1099. }
  1100. @(defun parse_namestring (thing
  1101. &o host (defaults si_default_pathname_defaults())
  1102. &k (start ecl_make_fixnum(0)) end junk_allowed
  1103. &a output)
  1104. @
  1105. if (host != ECL_NIL) {
  1106. host = cl_string(host);
  1107. }
  1108. if (!ecl_stringp(thing)) {
  1109. output = cl_pathname(thing);
  1110. } else {
  1111. cl_object default_host = host;
  1112. cl_index_pair p;
  1113. cl_index ee;
  1114. if (default_host == ECL_NIL && defaults != ECL_NIL) {
  1115. defaults = cl_pathname(defaults);
  1116. default_host = defaults->pathname.host;
  1117. }
  1118. p = ecl_vector_start_end(@[parse-namestring], thing, start, end);
  1119. output = ecl_parse_namestring(thing, p.start, p.end, &ee, default_host);
  1120. start = ecl_make_fixnum(ee);
  1121. if (output == ECL_NIL || ee != p.end) {
  1122. if (Null(junk_allowed)) {
  1123. FEparse_error("Cannot parse the namestring ~S~%"
  1124. "from ~S to ~S.", ECL_NIL,
  1125. 3, thing, start, end);
  1126. }
  1127. goto OUTPUT;
  1128. }
  1129. }
  1130. if (host != ECL_NIL && !ecl_equal(output->pathname.host, host)) {
  1131. FEerror("The pathname ~S does not contain the required host ~S.",
  1132. 2, thing, host);
  1133. }
  1134. OUTPUT:
  1135. @(return output start)
  1136. @)
  1137. @(defun merge_pathnames (path
  1138. &o (defaults si_default_pathname_defaults())
  1139. (default_version @':newest'))
  1140. @
  1141. path = cl_pathname(path);
  1142. defaults = cl_pathname(defaults);
  1143. @(return ecl_merge_pathnames(path, defaults, default_version))
  1144. @)
  1145. @(defun make_pathname (&key (host ECL_NIL hostp) (device ECL_NIL devicep)
  1146. (directory ECL_NIL directoryp)
  1147. (name ECL_NIL namep) (type ECL_NIL typep) (version ECL_NIL versionp)
  1148. ((:case scase) @':local')
  1149. defaults
  1150. &aux x)
  1151. @
  1152. if (Null(defaults)) {
  1153. defaults = si_default_pathname_defaults();
  1154. defaults = ecl_make_pathname(defaults->pathname.host,
  1155. ECL_NIL, ECL_NIL, ECL_NIL, ECL_NIL, ECL_NIL,
  1156. @':local');
  1157. } else {
  1158. defaults = cl_pathname(defaults);
  1159. }
  1160. if (!hostp) host = defaults->pathname.host;
  1161. x = ecl_make_pathname(host, device, directory, name, type, version, scase);
  1162. if (!devicep) x->pathname.device = defaults->pathname.device;
  1163. if (!directoryp) x->pathname.directory = defaults->pathname.directory;
  1164. if (!namep) x->pathname.name = defaults->pathname.name;
  1165. if (!typep) x->pathname.type = defaults->pathname.type;
  1166. if (!versionp) x->pathname.version = defaults->pathname.version;
  1167. @(return x)
  1168. @)
  1169. cl_object
  1170. cl_pathnamep(cl_object pname)
  1171. {
  1172. @(return (ECL_PATHNAMEP(pname) ? ECL_T : ECL_NIL))
  1173. }
  1174. cl_object
  1175. si_logical_pathname_p(cl_object pname)
  1176. {
  1177. @(return ((ECL_PATHNAMEP(pname) && pname->pathname.logical)?
  1178. ECL_T : ECL_NIL))
  1179. }
  1180. @(defun pathname_host (pname &key ((:case scase) @':local'))
  1181. @
  1182. pname = cl_pathname(pname);
  1183. @(return translate_component_case(pname->pathname.host,
  1184. normalize_case(pname, @':local'),
  1185. normalize_case(pname, scase)))
  1186. @)
  1187. @(defun pathname_device (pname &key ((:case scase) @':local'))
  1188. @
  1189. pname = cl_pathname(pname);
  1190. @(return translate_component_case(pname->pathname.device,
  1191. normalize_case(pname, @':local'),
  1192. normalize_case(pname, scase)))
  1193. @)
  1194. @(defun pathname_directory (pname &key ((:case scase) @':local'))
  1195. @
  1196. pname = cl_pathname(pname);
  1197. @(return translate_list_case(pname->pathname.directory,
  1198. normalize_case(pname, @':local'),
  1199. normalize_case(pname, scase)))
  1200. @)
  1201. @(defun pathname_name(pname &key ((:case scase) @':local'))
  1202. @
  1203. pname = cl_pathname(pname);
  1204. @(return translate_component_case(pname->pathname.name,
  1205. normalize_case(pname, @':local'),
  1206. normalize_case(pname, scase)))
  1207. @)
  1208. @(defun pathname_type(pname &key ((:case scase) @':local'))
  1209. @
  1210. pname = cl_pathname(pname);
  1211. @(return translate_component_case(pname->pathname.type,
  1212. normalize_case(pname, @':local'),
  1213. normalize_case(pname, scase)))
  1214. @)
  1215. cl_object
  1216. cl_pathname_version(cl_object pname)
  1217. {
  1218. pname = cl_pathname(pname);
  1219. @(return pname->pathname.version)
  1220. }
  1221. cl_object
  1222. cl_file_namestring(cl_object pname)
  1223. {
  1224. pname = cl_pathname(pname);
  1225. @(return ecl_namestring(ecl_make_pathname(ECL_NIL, ECL_NIL, ECL_NIL,
  1226. pname->pathname.name,
  1227. pname->pathname.type,
  1228. pname->pathname.version,
  1229. @':local'),
  1230. ECL_NAMESTRING_TRUNCATE_IF_ERROR))
  1231. }
  1232. cl_object
  1233. cl_directory_namestring(cl_object pname)
  1234. {
  1235. pname = cl_pathname(pname);
  1236. @(return ecl_namestring(ecl_make_pathname(ECL_NIL, ECL_NIL,
  1237. pname->pathname.directory,
  1238. ECL_NIL, ECL_NIL, ECL_NIL,
  1239. @':local'),
  1240. ECL_NAMESTRING_TRUNCATE_IF_ERROR))
  1241. }
  1242. cl_object
  1243. cl_host_namestring(cl_object pname)
  1244. {
  1245. pname = cl_pathname(pname);
  1246. pname = pname->pathname.host;
  1247. if (Null(pname) || pname == @':wild')
  1248. pname = cl_core.null_string;

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