home *** CD-ROM | disk | FTP | other *** search
/ Amiga ISO Collection / AmigaUtilCD2.iso / Programming / Misc / CLISP-1.LHA / CLISP960530-sr.lha / src / pathname.d < prev    next >
Encoding:
Text File  |  1996-08-01  |  444.8 KB  |  10,197 lines

  1. # Pathnames für CLISP
  2. # Bruno Haible 23.6.1995
  3. # Logical Pathnames: Marcus Daniels 16.9.1994
  4.  
  5. #include "lispbibl.c"
  6.  
  7. #ifdef HAVE_DISASSEMBLER
  8.   #include <string.h> # declares strlen()
  9.   #ifdef RETSTRLENTYPE # unless strlen() is a macro
  10.     extern RETSTRLENTYPE strlen (STRLEN_CONST char* s);
  11.   #endif
  12. #endif
  13.  
  14. #if defined(UNIX) || defined(WIN32_UNIX)
  15.   # Library-Funktion realpath implementieren:
  16.   # [Copyright: SUN Microsystems, B. Haible]
  17.   # TITLE
  18.   #   REALPATH(3)
  19.   # SYNOPSIS
  20.   #   char* realpath (const char* path, char resolved_path[MAXPATHLEN]);
  21.   # DESCRIPTION
  22.   #   realpath() expands all symbolic links  and  resolves  refer-
  23.   #   ences  to '/./', '/../' and extra '/' characters in the null
  24.   #   terminated string named by path and stores the canonicalized
  25.   #   absolute pathname in the buffer named by resolved_path.  The
  26.   #   resulting path will have no symbolic links  components,  nor
  27.   #   any '/./' or '/../' components.
  28.   # RETURN VALUES
  29.   #   realpath() returns a pointer to the  resolved_path  on  suc-
  30.   #   cess.   On  failure, it returns NULL, sets errno to indicate
  31.   #   the error, and places in resolved_path the absolute pathname
  32.   #   of the path component which could not be resolved.
  33.   #define realpath  my_realpath  # Consensys deklariert realpath() bereits...
  34.   local char* realpath (const char* path, char* resolved_path);
  35.   # Methode: benutze getwd und readlink.
  36.   local char* realpath(path,resolved_path)
  37.     const char* path;
  38.     char* resolved_path;
  39.     { char mypath[MAXPATHLEN];
  40.       int symlinkcount = 0; # Anzahl bisher aufgetretener symbolischer Links
  41.       char* resolved_limit = &resolved_path[MAXPATHLEN-1];
  42.       # Gültige Pointer sind die mit resolved_path <= ptr <= resolved_limit.
  43.       # In *resolved_limit darf höchstens noch ein Nullbyte stehen.
  44.       # (Analog mit mypath.)
  45.       char* resolve_start;
  46.       { char* resolved_ptr = resolved_path; # (bleibt stets <= resolved_limit)
  47.         # evtl. Working-Directory benutzen:
  48.         if (!(path[0]=='/')) # kein absoluter Pathname ?
  49.           { if (getwd(resolved_path) == NULL) { return NULL; }
  50.             resolved_ptr = resolved_path;
  51.             while (*resolved_ptr) { resolved_ptr++; }
  52.             if (resolved_ptr < resolved_limit) { *resolved_ptr++ = '/'; }
  53.             resolve_start = resolved_ptr;
  54.           }
  55.           else
  56.           { resolve_start = resolved_ptr = &resolved_path[0]; }
  57.         # Dann path selber einkopieren:
  58.        {const char* path_ptr = path;
  59.         while ((resolved_ptr < resolved_limit) && *path_ptr)
  60.           { *resolved_ptr++ = *path_ptr++; }
  61.         # Mit '/' und einem Nullbyte abschließen:
  62.         if (resolved_ptr < resolved_limit) { *resolved_ptr++ = '/'; }
  63.         *resolved_ptr = 0;
  64.       }}
  65.       # Los geht's nun in resolved_path ab resolve_start.
  66.       { char* from_ptr = resolve_start;
  67.         char* to_ptr = resolve_start;
  68.         while ((to_ptr < resolved_limit) && (*from_ptr))
  69.           # Bis hierher hat der Pfad in  resolved_path[0]...to_ptr[-1]
  70.           # die Gestalt '/subdir1/subdir2/.../txt',
  71.           # wobei 'txt' evtl. leer, aber kein subdir leer.
  72.           { char next = *from_ptr++; *to_ptr++ = next;
  73.             if ((next == '/') && (to_ptr > resolved_path+1))
  74.               # to_ptr[-1]='/'  ->  Directory ...to_ptr[-2] auflösen:
  75.               { char* last_subdir_end = &to_ptr[-2];
  76.                 switch (*last_subdir_end)
  77.                   { case '/':
  78.                       # '//' wird zu '/' vereinfacht:
  79.                       to_ptr--;
  80.                       break;
  81.                     case '.':
  82.                       { char* last_subdir_ptr = &last_subdir_end[-1];
  83.                         if (to_ptr > resolved_path+2)
  84.                           { if (*last_subdir_ptr == '.')
  85.                               { if ((to_ptr > resolved_path+4) && (*--last_subdir_ptr == '/'))
  86.                                   # letztes subdir war '/../'
  87.                                   # Dafür das subdir davor entfernen:
  88.                                   { while ((last_subdir_ptr > resolved_path) && !(*--last_subdir_ptr == '/'));
  89.                                     to_ptr = last_subdir_ptr+1;
  90.                               }   }
  91.                             elif (*last_subdir_ptr == '/')
  92.                               { # letztes subdir war '/./'
  93.                                 # entfernen:
  94.                                 to_ptr = last_subdir_end;
  95.                       }   }   }
  96.                       break;
  97.                     default:
  98.                       # nach einem normalen subdir
  99.                       #ifdef HAVE_READLINK
  100.                       # symbolischen Link lesen:
  101.                       to_ptr[-1]=0; # '/' durch 0 ersetzen
  102.                       { int linklen = readlink(resolved_path,mypath,sizeof(mypath)-1);
  103.                         if (linklen >=0)
  104.                           # war ein symbolisches Link
  105.                           { if (++symlinkcount > MAXSYMLINKS) { errno = ELOOP_VALUE; return NULL; }
  106.                             # noch aufzulösenden path-Anteil an den Link-Inhalt anhängen:
  107.                             { char* mypath_ptr = &mypath[linklen]; # ab hier ist Platz
  108.                               char* mypath_limit = &mypath[MAXPATHLEN-1]; # bis hierher
  109.                               if (mypath_ptr < mypath_limit) { *mypath_ptr++ = '/'; } # erst ein '/' anhängen
  110.                               # dann den Rest:
  111.                               while ((mypath_ptr <= mypath_limit) && (*mypath_ptr = *from_ptr++)) { mypath_ptr++; }
  112.                               *mypath_ptr = 0; # und mit 0 abschließen
  113.                             }
  114.                             # Dies ersetzt bzw. ergänzt den path:
  115.                             if (mypath[0] == '/')
  116.                               # ersetzt den path:
  117.                               { from_ptr = &mypath[0]; to_ptr = resolved_path;
  118.                                 while (*to_ptr++ = *from_ptr++);
  119.                                 from_ptr = resolved_path;
  120.                               }
  121.                               else
  122.                               # ergänzt den path:
  123.                               { # Linknamen streichen. Dazu bis zum letzten '/' suchen:
  124.                                 { char* ptr = &to_ptr[-1];
  125.                                   while ((ptr > resolved_path) && !(ptr[-1] == '/')) { ptr--; }
  126.                                   from_ptr = ptr;
  127.                                 }
  128.                                 { char* mypath_ptr = &mypath[0]; to_ptr = from_ptr;
  129.                                   while ((to_ptr <= resolved_limit) && (*to_ptr++ = *mypath_ptr++));
  130.                               } }
  131.                             to_ptr = from_ptr;
  132.                           }
  133.                           else
  134.                           #if defined(UNIX_IRIX)
  135.                           if ((errno == EINVAL) || (errno == ENXIO))
  136.                           #elif defined(UNIX_MINT)
  137.                           if ((errno == EINVAL) || (errno == EACCESS))
  138.                           #else
  139.                           if (errno == EINVAL)
  140.                           #endif
  141.                             # kein symbolisches Link
  142.                             { to_ptr[-1] = '/'; } # wieder den '/' eintragen
  143.                           else
  144.                             { return NULL; } # Fehler
  145.                       }
  146.                       #endif
  147.                       break;
  148.               }   }
  149.           } # dann zum nächsten subdir
  150.         # ein '/' am Ende streichen:
  151.         if ((to_ptr[-1] == '/') && (to_ptr > resolved_path+1)) { to_ptr--; }
  152.         to_ptr[0] = 0; # durch 0 abschließen
  153.         return resolved_path; # fertig
  154.     } }
  155. #endif
  156. #ifdef RISCOS
  157.   # SYNOPSIS
  158.   #   char* realpath (char* path, char resolved_path[MAXPATHLEN]);
  159.   # RETURN VALUES
  160.   #   realpath() returns a pointer to the resolved_path on success.
  161.   #   On failure, it returns NULL and sets errno to indicate the error.
  162.   local char* realpath (char* path, char* resolved_path);
  163.   #include <sys/os.h>
  164.   local char* realpath(path,resolved_path)
  165.     var char* path;
  166.     var char* resolved_path;
  167.     { var int handle;
  168.       var int r[10];
  169.       #if 0 # Both of these implementations should work.
  170.       if (os_fopen(0x40,path,&handle)) { return NULL; }
  171.       r[0] = 7; r[1] = handle; r[2] = (long)resolved_path; r[5] = MAXPATHLEN;
  172.       os_swi(9,r);
  173.       os_fclose(handle);
  174.       #else
  175.       var os_error* err;
  176.       r[0] = 37; r[1] = (long)path; r[2] = (long)resolved_path;
  177.       r[3] = 0; r[4] = 0; r[5] = MAXPATHLEN;
  178.       err = os_swi(0x29,r);
  179.       if (err) { __seterr(err); return NULL; }
  180.       #endif
  181.       if (r[5] <= 0)
  182.         { errno = ENOMEM /* ENAMETOOLONG would be better, but does not yet exist */;
  183.           return NULL;
  184.         }
  185.       return resolved_path;
  186.     }
  187. #endif
  188.  
  189.  
  190. # ==============================================================================
  191. #                         P A T H N A M E S
  192.  
  193. #ifdef PATHNAME_MSDOS
  194. # Komponenten:
  195. # HOST          stets NIL
  196. # DEVICE        NIL oder :WILD oder "A"|...|"Z"
  197. # DIRECTORY     (Startpoint . Subdirs) wobei
  198. #                Startpoint = :RELATIVE | :ABSOLUTE
  199. #                Subdirs = () | (subdir . Subdirs)
  200. #                subdir = :CURRENT (bedeutet ".") oder
  201. #                subdir = :PARENT (bedeutet "..") oder
  202. #                subdir = :WILD-INFERIORS (bedeutet "...", alle Subdirectories) oder
  203. #                subdir = (name . type)
  204. #                 name = :WILD oder Simple-String mit max. 8 Zeichen
  205. #                 type = :WILD oder Simple-String mit max. 3 Zeichen
  206. # NAME          NIL oder :WILD oder Simple-String mit max. 8 Zeichen
  207. # TYPE          NIL oder :WILD oder Simple-String mit max. 3 Zeichen
  208. # VERSION       stets NIL (auch :WILD oder :NEWEST bei Eingabe)
  209. # Wenn ein Pathname vollständig spezifiziert sein muß (keine Wildcards),
  210. # ist :WILD, :WILD-INFERIORS nicht erlaubt, bei NAME evtl. auch nicht NIL.
  211. # Externe Notation:       A:\sub1.typ\sub2.typ\name.typ
  212. # mit Defaults:             \sub1.typ\sub2.typ\name.typ
  213. # oder                                         name.typ
  214. # oder                    *:\sub1.typ\*.*\name.*
  215. # oder Ähnliches.
  216. # Statt '\' ist - wie unter DOS üblich - auch '/' erlaubt.
  217. #endif
  218.  
  219. #ifdef PATHNAME_AMIGAOS
  220. # Komponenten:
  221. # HOST          stets NIL
  222. # DEVICE        NIL oder Simple-String
  223. # DIRECTORY     (Startpoint . Subdirs) wobei
  224. #                Startpoint = :RELATIVE | :ABSOLUTE
  225. #                Subdirs = () | (subdir . Subdirs)
  226. #                subdir = :WILD-INFERIORS (bedeutet "**" oder "...", alle Subdirectories) oder
  227. #                subdir = :PARENT (bedeutet "/" statt "subdir/") oder
  228. #                subdir = Simple-String, evtl. mit Wildcard-Zeichen ? und *
  229. # NAME          NIL oder
  230. #               Simple-String, evtl. mit Wildcard-Zeichen ? und *
  231. #               (auch :WILD bei der Eingabe)
  232. # TYPE          NIL oder
  233. #               Simple-String, evtl. mit Wildcard-Zeichen ? und *
  234. #               (auch :WILD bei der Eingabe)
  235. # VERSION       stets NIL (auch :WILD oder :NEWEST bei Eingabe)
  236. # Constraint: Startpoint = :RELATIVE nur, falls Device = NIL;
  237. #             bei angegebenem Device gibt es also nur absolute Pathnames!
  238. # Ein AMIGAOS-Filename wird folgendermaßen in Name und Typ aufgespalten:
  239. #   falls kein '.' im Filename: Name = alles, Typ = NIL,
  240. #   falls '.' im Filename: Name = alles vor, Typ = alles nach dem letzten '.' .
  241. # Groß-/Klein-Schreibung innerhalb der Strings wird bei Vergleichen ignoriert,
  242. # aber ansonsten findet keine Groß/Klein-Umwandlung statt.
  243. # Wenn ein Pathname vollständig spezifiziert sein muß (keine Wildcards),
  244. # ist :WILD, :WILD-INFERIORS nicht erlaubt, keine Wildcard-Zeichen in den
  245. # Strings, bei NAME evtl. auch nicht NIL.
  246. # Externe Notation:  device:sub1.typ/sub2.typ/name.typ
  247. # mit Defaults:             sub1.typ/sub2.typ/name.typ
  248. # oder                                        name.typ
  249. # oder                      sub1.typ/ ** /sub3.typ/x*.lsp  (ohne Spaces!)
  250. # oder Ähnliches.
  251. # Formal:
  252. #   ch ::= beliebgiges Character außer ':','/' und '*','?'
  253. #   name ::= {ch}+
  254. #   device ::= [ <leer> | ':' | name ':' ]
  255. #              ; leer = aktuelles Device, relativ ab aktuellem Directory
  256. #              ; ':'  = aktuelles Device, absolut (ab root bei Disks)
  257. #              ; name ':' = angegebenes Device, absolut (ab root bei Disks)
  258. #   subdir ::= [ <leer> | name ]                ; leer = '..'
  259. #   pathname ::= device { subdir '/' }* name
  260. # Beispiele:
  261. #   String        Device    Directory                unser Pathname
  262. #   ------        ------    ---------                --------------
  263. #   'c:foo'       'C',     device->foo               "c" (:ABSOLUTE "foo")
  264. #   'c:foo/'      'C',     device->foo               "c" (:ABSOLUTE "foo")
  265. #   'c:foo/bar'   'C',     device->foo->bar          "c" (:ABSOLUTE "foo" "bar")
  266. #   'c:/foo'      'C',     device->up->foo           "c" (:ABSOLUTE :PARENT "foo")
  267. #   'c:'          'C',     device                    "c" (:ABSOLUTE)
  268. #   ':foo'        current, device->root->foo         NIL (:ABSOLUTE "foo")
  269. #   'foo'         current, device->foo               NIL (:RELATIVE "foo")
  270. #   '/foo'        current, device->up->foo           NIL (:RELATIVE :PARENT "foo")
  271. #   '//foo/bar'   current, device->up->up->foo->bar  NIL (:RELATIVE :PARENT :PARENT "foo" "bar")
  272. #   ''            current, device                    NIL (:RELATIVE)
  273. # An einen Pathstring, der nichtleer ist und der nicht mit ':' oder '/'
  274. # endet, kann ein '/' angehängt werden, ohne seine Semantik zu verändern.
  275. # Dieser '/' muß angehängt werden, bevor man eine weitere nichtleere
  276. # Komponente anhängen kann.
  277. # An einen Pathstring, der leer ist oder mit ':' oder '/' endet, ein '/'
  278. # anzuhängen, bedeutet aber, zum Parent Directory aufzusteigen!
  279. # Bei uns wird jeder Pathstring, der leer ist oder mit ':' oder '/' endet,
  280. # als Directory-Pathname (mit Name=NIL und Type=NIL) interpretiert.
  281. #endif
  282.  
  283. #ifdef PATHNAME_UNIX
  284. # Komponenten:
  285. # HOST          stets NIL
  286. # DEVICE        stets NIL
  287. # DIRECTORY     (Startpoint . Subdirs) wobei
  288. #                Startpoint = :RELATIVE | :ABSOLUTE
  289. #                Subdirs = () | (subdir . Subdirs)
  290. #                subdir = :WILD-INFERIORS (bedeutet "**" oder "...", alle Subdirectories) oder
  291. #                subdir = Simple-String, evtl. mit Wildcard-Zeichen ? und *
  292. # NAME          NIL oder
  293. #               Simple-String, evtl. mit Wildcard-Zeichen ? und *
  294. #               (auch :WILD bei der Eingabe)
  295. # TYPE          NIL oder
  296. #               Simple-String, evtl. mit Wildcard-Zeichen ? und *
  297. #               (auch :WILD bei der Eingabe)
  298. # VERSION       stets NIL (auch :WILD oder :NEWEST bei Eingabe)
  299. # Ein UNIX-Filename wird folgendermaßen in Name und Typ aufgespalten:
  300. #   falls kein '.' im Filename: Name = alles, Typ = NIL,
  301. #   falls '.' im Filename: Name = alles vor, Typ = alles nach dem letzten '.' .
  302. # Wenn ein Pathname vollständig spezifiziert sein muß (keine Wildcards),
  303. # ist :WILD, :WILD-INFERIORS nicht erlaubt, keine Wildcard-Zeichen in den
  304. # Strings, bei NAME evtl. auch nicht NIL.
  305. # Externe Notation:  server:/sub1.typ/sub2.typ/name.typ
  306. # mit Defaults:             /sub1.typ/sub2.typ/name.typ
  307. # oder                                         name.typ
  308. # oder                      /sub1.typ/ ** /sub3.typ/x*.lsp  (ohne Spaces!)
  309. # oder Ähnliches.
  310. #endif
  311.  
  312. #ifdef PATHNAME_OS2
  313. # Komponenten:
  314. # HOST          stets NIL
  315. # DEVICE        NIL oder :WILD oder "A"|...|"Z"
  316. # DIRECTORY     (Startpoint . Subdirs) wobei
  317. #                Startpoint = :RELATIVE | :ABSOLUTE
  318. #                Subdirs = () | (subdir . Subdirs)
  319. #                subdir = :WILD-INFERIORS (bedeutet "**" oder "...", alle Subdirectories) oder
  320. #                subdir = Simple-String, evtl. mit Wildcard-Zeichen ? und *
  321. # NAME          NIL oder
  322. #               Simple-String, evtl. mit Wildcard-Zeichen ? und *
  323. #               (auch :WILD bei der Eingabe)
  324. # TYPE          NIL oder
  325. #               Simple-String, evtl. mit Wildcard-Zeichen ? und *
  326. #               (auch :WILD bei der Eingabe)
  327. # VERSION       stets NIL (auch :WILD oder :NEWEST bei Eingabe)
  328. # Ein OS/2-Filename wird folgendermaßen in Name und Typ aufgespalten:
  329. #   falls kein '.' im Filename: Name = alles, Typ = NIL,
  330. #   falls '.' im Filename: Name = alles vor, Typ = alles nach dem letzten '.' .
  331. # Wenn ein Pathname vollständig spezifiziert sein muß (keine Wildcards),
  332. # ist :WILD, :WILD-INFERIORS nicht erlaubt, keine Wildcard-Zeichen in den
  333. # Strings, bei NAME evtl. auch nicht NIL.
  334. # Externe Notation:       A:\sub1.typ\sub2.typ\name.typ
  335. # mit Defaults:             \sub1.typ\sub2.typ\name.typ
  336. # oder                                         name.typ
  337. # oder                    *:\sub1.typ\**\sub3.typ\x*.lsp
  338. # oder Ähnliches.
  339. # Statt '\' ist - wie unter DOS üblich - auch '/' erlaubt.
  340. #endif
  341.  
  342. #ifdef PATHNAME_RISCOS
  343. #
  344. # Peter Burwood <clisp@arcangel.demon.co.uk> writes:
  345. #
  346. # RISC OS provides several filing systems as standard (ADFS, IDEFS, NetFS,
  347. # RamFS, NetPrint) and support for extra filing systems (DOSFS, ResourceFS and
  348. # DeviceFS).
  349. #
  350. # A module called FileSwitch is at the centre of all filing system operation
  351. # in RISC OS. FileSwitch provides a common core of functions used by all
  352. # filing systems. It only provides the parts of these services that are device
  353. # independent. The device dependant services that control the hardware are
  354. # provided by separate modules, which are the actual filing systems.
  355. # FileSwitch keeps track of active filing systems and switches betwen them as
  356. # necessary.
  357. #
  358. # One of the filing system modules that RISC OS provides is FileCore. It takes
  359. # the normal calls that FileSwitch sends to a filing system module, and
  360. # converts them to a simpler set of calls to modules that control the
  361. # hardware. Unlike FileSwitch it creates a fresh instantiation of itself for
  362. # each module that it supports. Using FileCore to build filing system modules
  363. # imposes a more rigid structure on it, as more of the filing system is
  364. # predefined.
  365. #
  366. # As well as standard filing systems, FileSwitch supports image filing
  367. # systems. These provide facilities for RISC OS to handle media in foreign
  368. # formats, and to support `image files' (or partitions) in those formats.
  369. # Rather than accessing the hardware directly they rely on standard RISC OS
  370. # filing systems to do so. DOSFS is an example of an image filing system used
  371. # to handle DOS format discs.
  372. #
  373. # Terminology
  374. #
  375. # A pathname may include a filing system name, a special field, a media name
  376. # (e.g., a disc name), directory name(s), and the name of the object itself;
  377. # each of these parts of a pathname is known as an `element' of the pathname.
  378. #
  379. # Filenames
  380. #
  381. # Filename `elements' may be up to ten characters in length on FileCore-based
  382. # filing systems and on NetFS. These characters may be digits or letters.
  383. # FileSwitch makes no distinction between upper and lower case, although
  384. # filing systems can do so. As a general rule, you should not use top-bit-set
  385. # characters in filenames, although some filing systems (such as
  386. # FileCore-based ones) support them. Other characters may be used provided
  387. # they do not have a special significance. Those that do are listed below :
  388. #
  389. #    .   Separates directory specifications, e.g., $.fred
  390. #    :   Introduces a drive or disc specification, e.g., :0, :bigdisc. It also
  391. #        marks the end of a filing system name, e.g., adfs:
  392. #    *   Acts as a `wildcard' to match zero or more characters.
  393. #    #   Acts as a `wildcard' to match any single character.
  394. #    $   is the name of the root directory of the disc.
  395. #    &   is the user root directory (URD)
  396. #    @   is the currently selected directory (CSD)
  397. #    ^   is the `parent' directory
  398. #    %   is the currently selected library (CSL)
  399. #    \   is the previously selected directory (PSD)
  400. #
  401. # Directories
  402. #
  403. # The root directory, $, forms the top of the directory hierarchy
  404. # of the media which contains the CSD. $ does not have a parent directory,
  405. # trying to access its parent will just access $. Each directory name is
  406. # separated by a '.' character. For example:
  407. #
  408. #    $.Documents.Memos
  409. #    %.cc
  410. #
  411. # Filing Systems
  412. #
  413. # Files may also be accessed on filing systems other than the current one by
  414. # prefixing the filename with a filing system specification. A filing system
  415. # name may appear between '-' characters, or suffixed by a ':', though the
  416. # latter is advised since '-' can also be used to introduce a parameter on a
  417. # command line, or as part of a file name. For example:
  418. #
  419. #    -net-$.SystemMesg
  420. #    adfs:%.aasm
  421. #
  422. # Special Fields
  423. #
  424. # Special fields are used to supply more information to the filing system than
  425. # you can using standard path names; for example NetFS and NetPrint use them
  426. # to specify server addresses or names. They are introduced by a '#'
  427. # character; a variety of syntaxes are possible:
  428. #
  429. #    net#MJHardy::disc1.mike
  430. #       #MJHardy::disc1.mike
  431. #   -net#MJHardy-:disc1.mike
  432. #      -#MJHardy-:disc1.mike
  433. #
  434. # The special fields here are all MJHardy, and give the name of the fileserver
  435. # to use. Special fields may use any character except for control characters,
  436. # double quote '"', solidus '|' and space. If a special field contains a hypen
  437. # you may only use the first two syntaxes given above.
  438. #
  439. # File$Path and Run$Path
  440. #
  441. # These two special variables control exactly where a file will be looked for,
  442. # according to the operation being performed on it.
  443. #
  444. #    File$Path   for read operations
  445. #    Run$Path    for execute operations
  446. #
  447. # The contents of each variable should expand to a list or prefixes, separated
  448. # by commas. When a read operation is performed then the prefixes in File$Path
  449. # are used in the order in which they are listed. The first object that
  450. # matches is used, whether it be a file or directory. Similarly any execute
  451. # operation uses the prefixes in Run$Path. These search paths are only used
  452. # when the pathname does not contain an explicit filing system reference,
  453. # e.g., executing adfs:file will not use Run$Path.
  454. #
  455. # Other path variables
  456. #
  457. # You can set up other path variables and use them as pseudo filing systems.
  458. # For example if you typed:
  459. #
  460. #    *Set Source$Path adfs:$.src.,adfs:$.public.src.
  461. #
  462. # you could then refer to the pseudo filing system as Source: or (less
  463. # preferable) as -Source-. These path variables work in the same was as
  464. # File$Path and Run$Path.
  465. #
  466. # NOTE: Path variables are not implemented in this version of CLISP. A
  467. # workaround for this is to use "<Foo$Path>" instead of "Foo:" until they are
  468. # made available.
  469. #
  470. #
  471. # from Lisp-string notation to internal representation
  472. # ----------------------------------------------------
  473. # NO swapping. "foo.lsp" means file type "lsp" and file name "foo".
  474. # This is pseudo-BNF:
  475. #
  476. # legal character ::= any ISO latin-1 graphic character >= ' ' except
  477. #                     '.' ':' '*' '#' '$' '&' '@' '^' '%' '\' '?'
  478. #
  479. # extended legal character ::= any ISO latin-1 graphic character >= ' ' except
  480. #                              ':' '"' '|'
  481. #
  482. # legal-wild char ::= legal char | '*' | '#' | '?'
  483. #
  484. # host ::=   '-' { extended legal char except '-' }+ '-'
  485. #          | { extended legal char except '-' } { extended legal char }* ':'
  486. #          | empty
  487. #
  488. # device ::=   ':' { legal char }+ '.'
  489. #            | empty
  490. #
  491. # directory ::=   { '$' | '&' | '@' | '%' | '\' } '.' { subdirectory }*
  492. #               | { subdirectory }+
  493. #               | empty
  494. #
  495. # '$' -> :ABSOLUTE :ROOT, '&' -> :ABSOLUTE :HOME, '@' -> :ABSOLUTE :CURRENT,
  496. # '%' -> :ABSOLUTE :LIBRARY, '\' -> :ABSOLUTE :PREVIOUS, else :RELATIVE.
  497. #
  498. # subdirectory ::= { '^' | { legal-wild char }+ } '.'
  499. #                  '^' -> :PARENT
  500. #
  501. # filename ::= { { legal-wild char }+ | empty }
  502. #
  503. # filetype ::= { '.' { legal-wild char }+ | empty }
  504. #
  505. # pathname ::= host device directory filename filetype
  506. #
  507. # Examples:
  508. # String                          Hostname Device  Directory            Name         Type
  509. # -net-$.SystemMesg                "net"   NIL     (:ABSOLUTE :ROOT)    "SystemMesg" NIL
  510. # net#MJHardy::disc1.mike    "net#MJHardy" "disc1" (:ABSOLUTE :ROOT)    "mike"       NIL
  511. # #MJHardy::disc1.mike          "#MJHardy" "disc1" (:ABSOLUTE :ROOT)    "mike"       NIL
  512. # -net#MJHardy-:disc1.mike   "net#MJHardy" "disc1" (:ABSOLUTE :ROOT)    "mike"       NIL
  513. # -#MJHardy-:disc1.mike         "#MJHardy" "disc1" (:ABSOLUTE :ROOT)    "mike"       NIL
  514. # @.foo                            NIL     NIL     (:ABSOLUTE :CURRENT) "foo"        NIL
  515. # foo                              NIL     NIL     (:RELATIVE)          "foo"        NIL
  516. # ^.                               NIL     NIL     (:RELATIVE :PARENT)  NIL          NIL
  517. # @.^.                             NIL     NIL     (:ABSOLUTE :CURRENT :PARENT) NIL  NIL
  518. # foo.bar                          NIL     NIL     (:RELATIVE)          "foo"        "bar"
  519. # foo.bar.baz                      NIL     NIL     (:RELATIVE "foo")    "bar"        "baz"
  520. # foo.bar.                         NIL     NIL     (:RELATIVE "foo" "bar") NIL       NIL
  521. # foo.@.                       illegal
  522. #
  523. # from internal representation to RISCOS string
  524. # ---------------------------------------------
  525. #
  526. # with swapping _only_ of name/type components.
  527. #
  528. # Hostname    Device  Directory                   Name    Type      RISCOS String
  529. #
  530. # "net"       "disc1" (:ABSOLUTE :ROOT)           "foo"   NIL       "net::disc1.$.foo"
  531. # "net#MJ"    "disc1" (:ABSOLUTE :ROOT "foo")     "bar"   "baz"     "net#MJ::disc1.$.foo.baz.bar"
  532. # "adfs"      "4"     (:ABSOLUTE :ROOT "foo" "bar") NIL   NIL       "adfs::4.$.foo.bar"
  533. # NIL         "disc1" (:ABSOLUTE :ROOT "foo")     "bar"   NIL       ":disc1.$.foo.bar"
  534. # NIL         "disc1" (:ABSOLUTE :CURRENT)        NIL     NIL       illegal here
  535. # NIL         "disc1" (:RELATIVE)                 NIL     NIL       ":disc1."
  536. # NIL         "disc1" NIL                         NIL     NIL       ":disc1."
  537. # NIL         NIL     (:ABSOLUTE :ROOT)           "foo"   NIL       "$.foo"
  538. # NIL         NIL     (:ABSOLUTE :CURRENT)        "foo"   NIL       "@.foo"
  539. # NIL         NIL     (:RELATIVE)                 "foo"   "bar"     "bar.foo"
  540. # NIL         NIL     (:RELATIVE "foo")           "bar"   "baz"     "foo.baz.bar"
  541. # NIL         NIL     (:ABSOLUTE :LIBRARY)        "bar"   NIL       "%.bar"
  542. # NIL         NIL     (:ABSOLUTE :LIBRARY "foo")  "bar"   NIL       "%.foo.bar"
  543. # NIL         NIL     (:RELATIVE)                 "foo"   "bar"     "bar.foo"
  544. # NIL         NIL     (:RELATIVE "foo")           "bar"   NIL       "foo.bar"
  545. # NIL         NIL     (:RELATIVE "foo")           NIL     "bar"     illegal here
  546. #
  547. # That is, the RISCOS string is the flattenation-concatenation of
  548. #   (append
  549. #     (if (null hostname) "" (append hostname ":"))
  550. #     (if (null device) "" (append ":" device "."))
  551. #     (case (pop directory)
  552. #       (:ABSOLUTE (case (pop directory)
  553. #                          (:ROOT "$.")
  554. #                          (:HOME "&.")
  555. #                          (:CURRENT "@.")
  556. #                          (:LIBRARY "%.")
  557. #                          (:PREVIOUS "\\.")
  558. #       )          )
  559. #       (:RELATIVE "")
  560. #     )
  561. #     (mapcar (lambda (subdir) (append subdir ".")) directory)
  562. #     (if (null name)
  563. #       (if (null type) "" (error "type with name illegal here"))
  564. #       (if (null type)
  565. #         name
  566. #         (append type "." name)
  567. #   ) ) )
  568. #
  569. # internal representation
  570. # -----------------------
  571. #
  572. # Pathname components:
  573. # HOST          Simple-String or NIL
  574. # DEVICE        Simple-String or NIL
  575. # DIRECTORY     (Startpoint . Subdirs) where
  576. #                Startpoint = :RELATIVE | :ABSOLUTE anchor
  577. #                anchor = :ROOT | :HOME | :CURRENT | :LIBRARY | :PREVIOUS
  578. #                Subdirs = () | (subdir . Subdirs)
  579. #                subdir = :PARENT or
  580. #                subdir = simple string, may contain wildcard characters ?,# and *
  581. # NAME          NIL or
  582. #               simple string, may contain wildcard characters ?,# and *
  583. #               (may also be specified as :WILD)
  584. # TYPE          NIL or
  585. #               simple string, may contain wildcard characters ?,# and *
  586. #               (may also be specified as :WILD)
  587. # VERSION       always NIL (may also be specified as :WILD or :NEWEST)
  588. # Constraint: startpoint /= :ABSOLUTE :ROOT only if device = NIL. If the device
  589. # is specified, the pathname must be :ABSOLUTE :ROOT.
  590. #
  591. # Komponenten:
  592. # HOST          Simple-String oder NIL
  593. # DEVICE        Simple-String oder NIL
  594. # DIRECTORY     (Startpoint . Subdirs) wobei
  595. #                Startpoint = :RELATIVE | :ABSOLUTE Anker
  596. #                Anker = :ROOT | :HOME | :CURRENT | :LIBRARY | :PREVIOUS
  597. #                Subdirs = () | (subdir . Subdirs)
  598. #                subdir = :PARENT oder
  599. #                subdir = Simple-String, evtl. mit Wildcard-Zeichen ?,# und *
  600. # NAME          NIL oder
  601. #               Simple-String, evtl. mit Wildcard-Zeichen ?,# und *
  602. #               (auch :WILD bei der Eingabe)
  603. # TYPE          NIL oder
  604. #               Simple-String, evtl. mit Wildcard-Zeichen ?,# und *
  605. #               (auch :WILD bei der Eingabe)
  606. # VERSION       stets NIL (auch :WILD oder :NEWEST bei Eingabe)
  607. #
  608. #endif
  609.  
  610. #ifdef LOGICAL_PATHNAMES
  611. # Komponenten von Logical Pathnames:
  612. # HOST          Simple-String oder NIL
  613. # DEVICE        stets NIL
  614. # DIRECTORY     (Startpoint . Subdirs) wobei
  615. #                Startpoint = :RELATIVE | :ABSOLUTE
  616. #                Subdirs = () | (subdir . Subdirs)
  617. #               subdir = :WILD-INFERIORS (bedeutet "**", alle Subdirectories) oder
  618. #               subdir = :WILD (bedeutet "*") oder
  619. #               subdir = Simple-String, evtl. mit Wildcard-Zeichen *
  620. # NAME          NIL oder
  621. #               :WILD (bedeutet "*") oder
  622. #               Simple-String, evtl. mit Wildcard-Zeichen *
  623. # TYPE          NIL oder
  624. #               :WILD (bedeutet "*") oder
  625. #               Simple-String, evtl. mit Wildcard-Zeichen *
  626. # VERSION       NIL oder :NEWEST oder :WILD oder Integer
  627. # Externe Notation: siehe CLtl2 S. 628-629.
  628. #endif
  629.  
  630. # Wandelt Groß-/Klein-Schreibung zwischen :LOCAL und :COMMON um.
  631. # common_case(string)
  632. # > string: Simple-String oder Symbol/Zahl
  633. # < ergebnis: umgewandelter Simple-String oder dasselbe Symbol/Zahl
  634. # kann GC auslösen
  635.   local object common_case (object string);
  636. # Dasselbe, rekursiv wie mit SUBST:
  637.   local object subst_common_case (object obj);
  638. #if defined(PATHNAME_UNIX) || defined(PATHNAME_OS2) || defined(PATHNAME_RISCOS) || defined(PATHNAME_AMIGAOS)
  639.   # Betriebssystem mit Vorzug für Kleinbuchstaben oder Capitalize
  640.   local object common_case(string)
  641.     var reg6 object string;
  642.     { if (!simple_string_p(string))
  643.         return string;
  644.      {var reg7 uintL len = TheSstring(string)->length;
  645.       # Suche, ob Groß- oder Kleinbuchstaben (oder beides) vorkommen:
  646.       var reg5 boolean all_upper = TRUE;
  647.       var reg4 boolean all_lower = TRUE;
  648.       { var reg2 uintB* ptr = &TheSstring(string)->data[0];
  649.         var reg3 uintL count;
  650.         dotimesL(count,len,
  651.           { var reg1 uintB ch = *ptr++;
  652.             if (!(ch == up_case(ch))) { all_upper = FALSE; }
  653.             if (!(ch == down_case(ch))) { all_lower = FALSE; }
  654.             if (!all_upper && !all_lower) break;
  655.           });
  656.       }
  657.       if (all_upper == all_lower)
  658.         # all_upper = all_lower = TRUE: Nichts zu konvertieren.
  659.         # all_upper = all_lower = FALSE: "Mixed case represents itself."
  660.         return string;
  661.       if (all_upper)
  662.         # all_upper = TRUE, all_lower = FALSE: STRING-DOWNCASE
  663.         return string_downcase(string);
  664.         else
  665.         # all_upper = FALSE, all_lower = TRUE: STRING-UPCASE
  666.         return string_upcase(string);
  667.     }}
  668.   local object subst_common_case(obj)
  669.     var reg1 object obj;
  670.     { if (atomp(obj))
  671.         { return common_case(obj); }
  672.       check_STACK(); check_SP();
  673.       pushSTACK(obj);
  674.       # rekursiv für den CAR aufrufen:
  675.       { var reg2 object new_car = subst_common_case(Car(obj));
  676.         pushSTACK(new_car);
  677.       }
  678.       # rekursiv für den CDR aufrufen:
  679.       { var reg2 object new_cdr = subst_common_case(Cdr(STACK_1));
  680.         if (eq(new_cdr,Cdr(STACK_1)) && eq(STACK_0,Car(STACK_1)))
  681.           { obj = STACK_1; skipSTACK(2); return obj; }
  682.           else
  683.           # (CONS new_car new_cdr)
  684.           { STACK_1 = new_cdr;
  685.            {var reg1 object new_cons = allocate_cons();
  686.             Car(new_cons) = popSTACK(); Cdr(new_cons) = popSTACK();
  687.             return new_cons;
  688.     } }   }}
  689. #else # defined(PATHNAME_MSDOS)
  690.   # Betriebssystem mit Vorzug für Großbuchstaben
  691.   #define common_case(string)  string
  692.   #define subst_common_case(obj)  obj
  693. #endif
  694.  
  695. #ifdef LOGICAL_PATHNAMES
  696.  
  697. local boolean legal_logical_word_char (uintB ch);
  698. local boolean legal_logical_word_char(ch)
  699.   var reg1 uintB ch;
  700.   { ch = up_case(ch);
  701.     if (((ch >= 'A') && (ch <= 'Z'))
  702.         || ((ch >= '0') && (ch <= '9'))
  703.         || (ch == '-')
  704.        )
  705.       return TRUE;
  706.       else
  707.       return FALSE;
  708.   }
  709.  
  710. #endif
  711.  
  712. #if HAS_HOST
  713.  
  714. # UP: Stellt fest, ob ein Character als Zeichen im Host-Teil eines Namestring
  715. # erlaubt ist.
  716. # legal_hostchar(ch)
  717. # > uintB ch: Character-Code
  718. # < ergebnis: TRUE falls erlaubt, FALSE sonst
  719.   local boolean legal_hostchar (uintB ch);
  720. # NB: legal_logical_word_char(ch) impliziert legal_hostchar(ch).
  721.   local boolean legal_hostchar(ch)
  722.     var reg1 uintB ch;
  723.     {
  724.       #ifdef PATHNAME_RISCOS
  725.       return (graphic_char_p(ch) && !(ch==':') && !(ch=='"') && !(ch=='|'));
  726.       #else
  727.       return alphanumericp(ch) || (ch=='-');
  728.       #endif
  729.     }
  730.  
  731. # UP: Überprüft ein optionales Host-Argument.
  732. # test_optional_host(host,convert)
  733. # > host: Host-Argument
  734. # > convert: Flag, ob Case-Konversion erwünscht ist
  735. # > subr_self: Aufrufer (ein SUBR)
  736. # < ergebnis: gültige Host-Komponente
  737. # kann GC auslösen
  738.   local object test_optional_host (object host, boolean convert);
  739.   local object test_optional_host(host,convert)
  740.     var reg4 object host;
  741.     var reg5 boolean convert;
  742.     { if (eq(host,unbound)) { return NIL; } # nicht angegeben -> NIL
  743.       if (nullp(host)) goto OK; # NIL ist OK
  744.       # Sonst muß host ein String sein, dessen Zeichen alphanumerisch sind:
  745.       if (!stringp(host))
  746.         { pushSTACK(host); # Wert für Slot DATUM von TYPE-ERROR
  747.           pushSTACK(O(type_host)); # Wert für Slot EXPECTED-TYPE von TYPE-ERROR
  748.           pushSTACK(host);
  749.           pushSTACK(TheSubr(subr_self)->name);
  750.           //: DEUTSCH "~: Host muß NIL oder ein String sein, nicht ~"
  751.           //: ENGLISH "~: host should be NIL or a string, not ~"
  752.           //: FRANCAIS "~ : Le nom de machine hôte doit être NIL ou de type STRING et non ~"
  753.           fehler(type_error, GETTEXT("~: host should be NIL or a string, not ~"));
  754.         }
  755.       host = coerce_ss(host); # als Simple-String
  756.       if (convert) { host = common_case(host); }
  757.       { var reg3 uintL len = TheSstring(host)->length;
  758.         var reg2 uintB* charptr = &TheSstring(host)->data[0];
  759.         dotimesL(len,len,
  760.           { var reg1 uintB ch = *charptr++;
  761.             if (!legal_hostchar(ch)) goto badhost;
  762.           });
  763.       }
  764.       OK: return host;
  765.       badhost:
  766.         { pushSTACK(host);
  767.           pushSTACK(TheSubr(subr_self)->name);
  768.           //: DEUTSCH "~: syntaktisch illegaler Hostname ~"
  769.           //: ENGLISH "~: illegal hostname ~"
  770.           //: FRANCAIS "~ : Syntaxe incorrecte pour un nom de machine hôte: ~"
  771.           fehler(error, GETTEXT("~: illegal hostname ~"));
  772.         }
  773.     }
  774.  
  775. #else
  776.  
  777. #ifdef LOGICAL_PATHNAMES
  778.  
  779. # UP: Überprüft ein optionales Host-Argument.
  780. # test_optional_host(host)
  781. # > host: Host-Argument
  782. # > subr_self: Aufrufer (ein SUBR)
  783. # < ergebnis: gültige Host-Komponente
  784. # kann GC auslösen
  785.   local object test_optional_host (object host);
  786.   local object test_optional_host(host)
  787.     var reg4 object host;
  788.     { if (eq(host,unbound)) { return NIL; } # nicht angegeben -> NIL
  789.       if (nullp(host)) goto OK; # NIL ist OK
  790.       # Sonst muß host ein String sein, dessen Zeichen alphanumerisch sind:
  791.       if (!stringp(host))
  792.         { pushSTACK(host); # Wert für Slot DATUM von TYPE-ERROR
  793.           pushSTACK(O(type_host)); # Wert für Slot EXPECTED-TYPE von TYPE-ERROR
  794.           pushSTACK(host);
  795.           pushSTACK(TheSubr(subr_self)->name);
  796.           //: DEUTSCH "~: Host muß NIL oder ein String sein, nicht ~"
  797.           //: ENGLISH "~: host should be NIL or a string, not ~"
  798.           //: FRANCAIS "~ : Le nom de machine hôte doit être NIL ou de type STRING et non ~"
  799.           fehler(type_error, GETTEXT("~: host should be NIL or a string, not ~"));
  800.         }
  801.       host = coerce_ss(host); # als Simple-String
  802.       { var reg3 uintL len = TheSstring(host)->length;
  803.         var reg2 uintB* charptr = &TheSstring(host)->data[0];
  804.         dotimesL(len,len,
  805.           { var reg1 uintB ch = *charptr++;
  806.             if (!legal_logical_word_char(ch)) goto badhost;
  807.           });
  808.       }
  809.       OK: return host;
  810.       badhost:
  811.         { pushSTACK(host);
  812.           pushSTACK(TheSubr(subr_self)->name);
  813.           //: DEUTSCH "~: syntaktisch illegaler Hostname ~"
  814.           //: ENGLISH "~: illegal hostname ~"
  815.           //: FRANCAIS "~ : Syntaxe incorrecte pour un nom de machine hôte: ~"
  816.           fehler(error, GETTEXT("~: illegal hostname ~"));
  817.         }
  818.     }
  819.  
  820. #else
  821.  
  822. # UP: Überprüft ein optionales Host-Argument.
  823. # test_optional_host(host);
  824. # > host: Host-Argument
  825. # > subr_self: Aufrufer (ein SUBR)
  826. # < ergebnis: gültige Host-Komponente
  827.   local object test_optional_host (object host);
  828.   local object test_optional_host(host)
  829.     var reg1 object host;
  830.     { if (!eq(host,unbound)) # nicht angegeben -> OK
  831.         { if (!nullp(host)) # angegeben -> sollte =NIL sein
  832.             { pushSTACK(host); # Wert für Slot DATUM von TYPE-ERROR
  833.               pushSTACK(S(null)); # Wert für Slot EXPECTED-TYPE von TYPE-ERROR
  834.               pushSTACK(host);
  835.               pushSTACK(TheSubr(subr_self)->name);
  836.               //: DEUTSCH "~: Host muß NIL sein, nicht ~"
  837.               //: ENGLISH "~: host should be NIL, not ~"
  838.               //: FRANCAIS "~ : Le nom de machine hôte doit être NIL et non ~"
  839.               fehler(type_error, GETTEXT("~: host should be NIL, not ~"));
  840.             }
  841.         }
  842.       return NIL;
  843.     }
  844.  
  845. #endif
  846.  
  847. #endif
  848.  
  849. # Stellt fest, ob zwei Characters als Zeichen in Pathnames als gleich gelten.
  850. # equal_pathchar(ch1,ch2)
  851. # > uintB ch1,ch2: Character-Codes
  852. # < ergebnis: TRUE falls gleich, FALSE sonst
  853.   #if !(defined(PATHNAME_AMIGAOS) || defined(PATHNAME_OS2))
  854.     #define equal_pathchar(ch1,ch2)  ((ch1)==(ch2))
  855.   #else # defined(PATHNAME_AMIGAOS) || defined(PATHNAME_OS2)
  856.     # Case-insensitive, aber normalerweise ohne Konversion
  857.     #define equal_pathchar(ch1,ch2)  (up_case(ch1)==up_case(ch2))
  858.   #endif
  859.  
  860. # UP: Stellt fest, ob ein Character als Zeichen im Namens-/Typ-Teil eines
  861. # Namestring erlaubt ist.
  862. # legal_namechar(ch)
  863. # > uintB ch: Character-Code
  864. # < ergebnis: TRUE falls erlaubt, FALSE sonst
  865.   local boolean legal_namechar (uintB ch);
  866.   local boolean legal_namechar(ch)
  867.     var reg1 uintB ch;
  868.     {
  869.       #ifdef VALID_FILENAME_CHAR # defined in unixconf.h
  870.       return VALID_FILENAME_CHAR;
  871.       #else
  872.       #if defined(PATHNAME_MSDOS)
  873.       return ((ch=='_') || (ch=='-') || alphanumericp(ch));
  874.       #endif
  875.       #ifdef PATHNAME_AMIGAOS
  876.       return (graphic_char_p(ch) && !(ch=='/') && !(ch==':'));
  877.       #endif
  878.       #ifdef PATHNAME_UNIX
  879.       return ((ch>=' ') && (ch<='~') && !(ch=='/'));
  880.       #endif
  881.       #ifdef PATHNAME_OS2
  882.       return (graphic_char_p(ch) && !(ch=='\\') && !(ch=='/') && !(ch==':'));
  883.       #endif
  884.       #ifdef PATHNAME_RISCOS
  885.       return (graphic_char_p(ch) && !(ch==':') && !(ch=='.')
  886.               && !(ch=='$') && !(ch=='&') && !(ch=='@')
  887.               && !(ch=='^') && !(ch=='%') && !(ch=='\\')
  888.               # Wild Characters '*' '#' '?' sind hier erlaubt.
  889.              );
  890.       #endif
  891.       #endif
  892.     }
  893.  
  894. # Stellt fest, ob ein Character ein Wildcard-Platzhalter für ein einzelnes
  895. # Zeichen ist.
  896. # singlewild_char_p(ch)
  897. # > uintB ch: Character-Code
  898. # < ergebnis: TRUE falls ja, FALSE sonst
  899.   #if !defined(PATHNAME_RISCOS)
  900.     #define singlewild_char_p(ch)  ((ch)=='?')
  901.   #else # defined(PATHNAME_RISCOS)
  902.     #define singlewild_char_p(ch)  (((ch)=='?') || ((ch)=='#'))
  903.   #endif
  904.  
  905. # Wandelt ein Objekt in einen Pathname um.
  906.   local object coerce_xpathname (object obj); # später
  907.  
  908. # Wandelt ein Objekt in einen nicht-Logical Pathname um.
  909.   local object coerce_pathname (object obj); # später
  910. #if !defined(LOGICAL_PATHNAMES)
  911.   #define coerce_pathname(obj)  coerce_xpathname(obj)
  912. #endif
  913.  
  914. # Liefert den Default-Pathname.
  915.   local object defaults_pathname (void); # später
  916.  
  917. # Überprüft einen Default-Pathname.
  918. # test_default_pathname(defaults)
  919. # > defaults: Defaults-Argument
  920. # < ergebnis: Wert des Defaults-Arguments, ein Pathname
  921. # kann GC auslösen
  922.   local object test_default_pathname (object defaults);
  923.   local object test_default_pathname(defaults)
  924.     var reg1 object defaults;
  925.     { if (eq(defaults,unbound))
  926.         # nicht angegeben -> Wert von *DEFAULT-PATHNAME-DEFAULTS* nehmen:
  927.         { return defaults_pathname(); }
  928.         else
  929.         # angegeben -> zu einem Pathname machen:
  930.         { return coerce_xpathname(defaults); }
  931.     }
  932.  
  933. # Fehlermeldung wegen illegalem Pathname-Argument.
  934. # fehler_thing(thing);
  935. # > thing: (fehlerhaftes) Argument
  936. # > subr_self: Aufrufer (ein SUBR)
  937.   nonreturning_function(local, fehler_thing, (object thing));
  938.   local void fehler_thing(thing)
  939.     var reg1 object thing;
  940.     { pushSTACK(thing);
  941.       pushSTACK(TheSubr(subr_self)->name);
  942.       # type_error ??
  943.       //: DEUTSCH "~: Argument muß ein String, Symbol, File-Stream oder Pathname sein, nicht ~"
  944.       //: ENGLISH "~: argument should be a string, symbol, file stream or pathname, not ~"
  945.       //: FRANCAIS "~ : L'argument doit être une chaîne, un symbole, un «stream» de fichier ou un «pathname» et non ~"
  946.       fehler(error, GETTEXT("~: argument should be a string, symbol, file stream or pathname, not ~"));
  947.     }
  948.  
  949. # Verfolgt eine Kette von Synonym-Streams, solange bis bei einem File-Stream
  950. # angelangt.
  951. # as_file_stream(stream)
  952. # > stream: Stream
  953. # < stream: File-Stream
  954.   local object as_file_stream (object stream);
  955.   local object as_file_stream(stream)
  956.     var reg2 object stream;
  957.     { var reg1 object s = stream;
  958.       loop
  959.         { if_strm_file_p(s, { return s; } , ; );
  960.           if (!(TheStream(s)->strmtype == strmtype_synonym)) break;
  961.           s = Symbol_value(TheStream(stream)->strm_synonym_symbol);
  962.           if (!streamp(s)) break;
  963.         }
  964.       fehler_thing(stream);
  965.     }
  966.  
  967. #if defined(UNIX) || defined(MSDOS) || defined(RISCOS) || defined(WIN32_UNIX)
  968.  
  969. #if defined(UNIX) || defined(MSDOS) || defined(WIN32_UNIX)
  970.   #define slash  '/'
  971. #endif
  972. #ifdef RISCOS
  973.   #define slash  '.'
  974. #endif
  975.  
  976. # UP: Wandelt eine Unix-Directory-Angabe in ein Pathname um.
  977. # asciz_dir_to_pathname(path)
  978. # > const char* path: path als ASCIZ-String
  979. # < ergebnis: als Pathname ohne Name und Typ
  980.   local object asciz_dir_to_pathname (const char* path);
  981.   local object asciz_dir_to_pathname(path)
  982.     var reg4 const char* path;
  983.      { var reg1 const char* pathptr = path;
  984.        var reg2 uintL len = 0; # Stringlänge
  985.        until (*pathptr == 0) { pathptr++; len++; } # ASCIZ-Stringende suchen
  986.        # Sofern der String nicht schon mit '/' endet, wird ein '/' angefügt:
  987.        if (!((len>0) && (pathptr[-1]==slash))) { len++; }
  988.        # und in einen String umwandeln:
  989.       {var reg3 object pathname = make_string((const uintB*)path,len);
  990.        TheSstring(pathname)->data[len-1] = slash; # abschließendes '/' unterbringen
  991.        # und in ein Pathname umwandeln:
  992.        return coerce_pathname(pathname);
  993.      }}
  994.  
  995. #endif
  996.  
  997. # Typ für PARSE-NAMESTRING:
  998. # Der String wird durchlaufen.
  999.   typedef struct { uintL index; # Index (incl. Offset)
  1000.                    object FNindex; # Index als Fixnum
  1001.                    uintL count; # Anzahl der verbleibenden Characters
  1002.                  }
  1003.           zustand;
  1004.  
  1005. #ifdef LOGICAL_PATHNAMES
  1006.  
  1007. # Parst einen Logical-Pathname.
  1008. # parse_logical_pathnamestring(z)
  1009. # > STACK_1: Datenvektor
  1010. # > STACK_0: neuer Logical Pathname
  1011. # > zustand z: Start-Zustand
  1012. # < STACK_0: selber Logical Pathname, ausgefüllt
  1013. # < ergebnis: Anzahl der übriggebliebenen Zeichen
  1014. # kann GC auslösen
  1015. local uintL parse_logical_pathnamestring (zustand z);
  1016.  
  1017. # Trennzeichen zwischen subdirs
  1018. #define slashp(c)  ((c) == ';')
  1019.  
  1020. # Parst Name/Type/Version-Teil (subdirp=FALSE) bzw. subdir-Teil (subdirp=TRUE).
  1021. # Liefert Simple-String oder :WILD oder :WILD-INFERIORS oder NIL.
  1022. local object parse_logical_word (zustand* z, boolean subdirp);
  1023. local object parse_logical_word(z,subdirp)
  1024.   var reg1 zustand* z;
  1025.   var reg7 boolean subdirp;
  1026.   { var zustand startz; startz = *z; # Start-Zustand
  1027.    {var reg4 uintB ch;
  1028.     # Kommt eine Folge von alphanumerischen Zeichen oder '*',
  1029.     # keine zwei '*' adjazent (ausgenommen "**", falls subdirp),
  1030.     # und, falls subdirp, ein ';' ?
  1031.     var reg8 boolean last_was_star = FALSE;
  1032.     var reg9 boolean seen_starstar = FALSE;
  1033.     loop
  1034.       { if (z->count == 0) break;
  1035.         ch = TheSstring(STACK_2)->data[z->index]; # nächstes Character
  1036.         if (!legal_logical_word_char(ch))
  1037.           { if (ch == '*')
  1038.               { if (last_was_star)
  1039.                   { if (subdirp && (z->index - startz.index == 1))
  1040.                       seen_starstar = TRUE;
  1041.                       else
  1042.                       break; # adjazente '*' sind verboten
  1043.                   }
  1044.                   else
  1045.                   last_was_star = TRUE;
  1046.               }
  1047.               else
  1048.               break;
  1049.           }
  1050.         # Character übergehen:
  1051.         z->index++; z->FNindex = fixnum_inc(z->FNindex,1); z->count--;
  1052.       }
  1053.    {var reg5 uintL len = z->index - startz.index;
  1054.     if (subdirp)
  1055.       { if ((z->count == 0) || !slashp(ch))
  1056.           { *z = startz; return NIL; } # kein ';' -> kein subdir
  1057.         # Character ';' übergehen:
  1058.         z->index++; z->FNindex = fixnum_inc(z->FNindex,1); z->count--;
  1059.       }
  1060.     if (len==0)
  1061.       { return NIL; }
  1062.     elif ((len==1) && (TheSstring(STACK_2)->data[startz.index]=='*'))
  1063.       { return S(Kwild); }
  1064.     elif ((len==2) && seen_starstar)
  1065.       { return S(Kwild_inferiors); }
  1066.     else # String bilden:
  1067.       { var reg6 object result = allocate_string(len);
  1068.         # und füllen:
  1069.         {var reg2 uintB* ptr1 = &TheSstring(STACK_2)->data[startz.index];
  1070.          var reg3 uintB* ptr2 = &TheSstring(result)->data[0];
  1071.          dotimespL(len,len, { *ptr2++ = up_case(*ptr1++); });
  1072.         }
  1073.         return result;
  1074.       }
  1075.    }
  1076.   }}
  1077.  
  1078. # Test auf Ziffernfolge:
  1079. local boolean all_digits (object string);
  1080. local boolean all_digits(string)
  1081.   var reg4 object string;
  1082.   { var reg2 uintB* charptr = &TheSstring(string)->data[0];
  1083.     var reg3 uintL len = TheSstring(string)->length;
  1084.     dotimesL(len,len,
  1085.       { var reg1 uintB ch = *charptr++;
  1086.         if (!((ch >= '0') && (ch <= '9'))) return FALSE;
  1087.       });
  1088.     return TRUE;
  1089.   }
  1090.  
  1091. local uintL parse_logical_pathnamestring(z)
  1092.   var zustand z;
  1093.   { # Host-Specification parsen:
  1094.     { var reg5 object host;
  1095.       var zustand startz; startz = z; # Start-Zustand
  1096.      {var reg1 uintB ch;
  1097.       # Kommt eine Folge von alphanumerischen Zeichen und dann ein ':' ?
  1098.       loop
  1099.         { if (z.count==0) goto no_hostspec; # String schon zu Ende -> kein Host
  1100.           ch = TheSstring(STACK_1)->data[z.index]; # nächstes Character
  1101.           if (!legal_logical_word_char(ch)) break;
  1102.           # alphanumerisches Character übergehen:
  1103.           z.index++; z.FNindex = fixnum_inc(z.FNindex,1); z.count--;
  1104.         }
  1105.       if (!(ch==':')) goto no_hostspec; # kein ':' -> kein Host
  1106.       # Host-String bilden:
  1107.       { var reg4 uintL len = z.index - startz.index;
  1108.         host = allocate_string(len);
  1109.         # und füllen:
  1110.        {var reg2 uintB* ptr1 = &TheSstring(STACK_1)->data[startz.index];
  1111.         var reg3 uintB* ptr2 = &TheSstring(host)->data[0];
  1112.         dotimesL(len,len, { *ptr2++ = up_case(*ptr1++); });
  1113.       }}
  1114.       # Character ':' übergehen:
  1115.       z.index++; z.FNindex = fixnum_inc(z.FNindex,1); z.count--;
  1116.       goto hostspec_ok;
  1117.      no_hostspec: # keine Host-Specification
  1118.       z = startz; # zum Start zurück
  1119.       host = STACK_(3+2); # Default-Host
  1120.      hostspec_ok:
  1121.       # Host eintragen:
  1122.       TheLogpathname(STACK_0)->pathname_host = host;
  1123.     }}
  1124.     # Directory-Start eintragen:
  1125.     { var reg1 object new_cons = allocate_cons(); # neues Cons für Startpoint
  1126.       TheLogpathname(STACK_0)->pathname_directory = new_cons;
  1127.       pushSTACK(new_cons); # neues (last (pathname-directory Pathname))
  1128.     }
  1129.     # Stackaufbau: Datenvektor, Pathname, (last (pathname-directory Pathname)).
  1130.     # Subdirectories parsen:
  1131.     # Falls sofort ein ';' kommt, wird er übergangen, und es kommt :RELATIVE
  1132.     # (sonst :ABSOLUTE) als erstes subdir:
  1133.     if ((!(z.count == 0)) && slashp(TheSstring(STACK_2)->data[z.index]))
  1134.       # Es kommt sofort ein ';'.
  1135.       { # Character übergehen:
  1136.         z.index++; z.FNindex = fixnum_inc(z.FNindex,1); z.count--;
  1137.         Car(STACK_0) = S(Krelative); # Startpoint = :RELATIVE
  1138.       }
  1139.       else
  1140.       # Es kommt nicht sofort ein ';'.
  1141.       { Car(STACK_0) = S(Kabsolute); } # Startpoint = :ABSOLUTE
  1142.     loop
  1143.       { # Versuche, ein weiteres Unterdirectory zu parsen.
  1144.         var reg2 object subdir = parse_logical_word(&z,TRUE);
  1145.         if (nullp(subdir)) break;
  1146.         # (pathname-directory pathname) um Subdir verlängern:
  1147.         pushSTACK(subdir);
  1148.        {var reg1 object new_cons = allocate_cons(); # neues Cons
  1149.         Car(new_cons) = popSTACK(); # = (cons subdir NIL)
  1150.         Cdr(STACK_0) = new_cons; # verlängert (pathname-directory Pathname)
  1151.         STACK_0 = new_cons; # neues (last (pathname-directory Pathname))
  1152.       }}
  1153.     # Name parsen:
  1154.     { var reg1 object name = parse_logical_word(&z,FALSE);
  1155.       TheLogpathname(STACK_1)->pathname_name = name;
  1156.       if ((z.count > 0) && (TheSstring(STACK_2)->data[z.index]=='.'))
  1157.         { var zustand z_name; z_name = z;
  1158.           # Character '.' übergehen:
  1159.           z.index++; z.FNindex = fixnum_inc(z.FNindex,1); z.count--;
  1160.           # Typ parsen:
  1161.          {var reg2 object type = parse_logical_word(&z,FALSE);
  1162.           TheLogpathname(STACK_1)->pathname_type = type;
  1163.           if (!nullp(type))
  1164.             { if ((z.count > 0) && (TheSstring(STACK_2)->data[z.index]=='.'))
  1165.                 { var zustand z_type; z_type = z;
  1166.                   # Character '.' übergehen:
  1167.                   z.index++; z.FNindex = fixnum_inc(z.FNindex,1); z.count--;
  1168.                   # Version parsen:
  1169.                  {var reg3 object version = parse_logical_word(&z,FALSE);
  1170.                   if (eq(version,S(Kwild))) { }
  1171.                   elif (equal(version,Symbol_name(S(Knewest))))
  1172.                     { version = S(Knewest); }
  1173.                   elif (all_digits(version))
  1174.                     { pushSTACK(version); funcall(L(parse_integer),1); # version in Integer umwandeln
  1175.                       version = value1;
  1176.                     }
  1177.                   else
  1178.                     { version = NIL; }
  1179.                   TheLogpathname(STACK_1)->pathname_version = version;
  1180.                   if (nullp(version))
  1181.                     { z = z_type; } # Character '.' wieder zurück
  1182.                 }}
  1183.                 else
  1184.                 { TheLogpathname(STACK_1)->pathname_version = NIL; }
  1185.             }
  1186.             else
  1187.             { z = z_name; # Character '.' wieder zurück
  1188.               TheLogpathname(STACK_1)->pathname_version = NIL;
  1189.             }
  1190.         }}
  1191.         else
  1192.         { TheLogpathname(STACK_1)->pathname_type = NIL;
  1193.           TheLogpathname(STACK_1)->pathname_version = NIL;
  1194.         }
  1195.     }
  1196.     skipSTACK(1);
  1197.     return z.count;
  1198.   }
  1199.  
  1200. #undef slashp
  1201.  
  1202. # Erkennung eines logischen Hosts, vgl. CLtL2 S. 631
  1203. # (defun logical-host-p (host)
  1204. #   (and (simple-string-p host)
  1205. #        (gethash host sys::*logical-pathname-translations*)
  1206. #        t
  1207. # ) )
  1208.   local boolean logical_host_p (object host);
  1209.   local boolean logical_host_p(host)
  1210.     var reg1 object host;
  1211.     { return (simple_string_p(host)
  1212.               # Fehlt host = string_upcase(host); ??
  1213.               && !eq(gethash(host,Symbol_value(S(logpathname_translations))),
  1214.                      nullobj
  1215.              )      );
  1216.     }
  1217.  
  1218. #endif
  1219.  
  1220. #ifdef PATHNAME_EXT83
  1221. # Hilfsfunktion für PARSE-NAMESTRING:
  1222. # Parst einen Namens- oder Typteil.
  1223. # parse_name_or_type(&z,stdlen,def)
  1224. # > stdlen: Standard-Länge des Teils
  1225. # > def: Defaultwert
  1226. # > STACK_3: Datenvektor des Strings
  1227. # > z: Zustand
  1228. # < z: Zustand
  1229. # < ergebnis: Namens- oder Typteil (=default, falls leer)
  1230. # kann GC auslösen
  1231.   local object parse_name_or_type (zustand* z, uintL stdlen, object def);
  1232.   local object parse_name_or_type(z,stdlen,def)
  1233.     var reg1 zustand* z;
  1234.     var reg5 uintL stdlen;
  1235.     var reg6 object def;
  1236.     { var reg3 uintL z_start_index = z->index; # Index beim Start des Namens
  1237.       loop
  1238.         { var reg2 uintB ch;
  1239.           if (z->count == 0) break;
  1240.           ch = TheSstring(STACK_3)->data[z->index]; # nächstes Character
  1241.           ch = up_case(ch); # als Großbuchstabe
  1242.           if (ch == '.') break;
  1243.           if (ch == '*')
  1244.             # '*' angetroffen.
  1245.             { # nicht am Anfang des Namens -> beendet den Namen:
  1246.               if (!(z->index == z_start_index)) break;
  1247.               # Character übergehen:
  1248.               z->index++; z->FNindex = fixnum_inc(z->FNindex,1); z->count--;
  1249.               return S(Kwild); # Name := :WILD
  1250.             }
  1251.           if (!legal_namechar(ch)) break; # gültiges Character ?
  1252.           # ja -> Teil des Namens
  1253.           # Character übergehen:
  1254.           z->index++; z->FNindex = fixnum_inc(z->FNindex,1); z->count--;
  1255.         }
  1256.       # Ende des Namens erreicht.
  1257.       # Name := Teilstring von STACK_3 von z_start_index (einschließlich)
  1258.       #                                bis z->index (ausschließlich).
  1259.      {var reg3 uintL len = z->index - z_start_index;
  1260.       # kein Name angegeben -> default zurück:
  1261.       if (len==0) { return def; }
  1262.       #ifndef EMUNIX_PORTABEL # unter OS/2 gilt die 8+3-Regel nicht mehr
  1263.       # bei len > stdlen setze len:=stdlen :
  1264.       if (len > stdlen) { len = stdlen; }
  1265.       #endif
  1266.       {var reg4 object string = allocate_string(len); # String der Länge len
  1267.        # füllen:
  1268.        var reg1 uintB* ptr1 = &TheSstring(STACK_3)->data[z_start_index];
  1269.        var reg2 uintB* ptr2 = &TheSstring(string)->data[0];
  1270.        dotimespL(len,len, { *ptr2++ = up_case(*ptr1++); });
  1271.        # Name fertig.
  1272.        return string;
  1273.     }}}
  1274. #endif
  1275.  
  1276. #ifdef PATHNAME_NOEXT
  1277. # Hilfsfunktion für PARSE-NAMESTRING:
  1278. # Spaltet einen String (beim letzten Punkt) in Name und Typ auf.
  1279. # split_name_type(skip);
  1280. # > STACK_0: Simple-String
  1281. # > skip: 1 falls ein Punkt an erster Stelle nicht aufspaltend wirken soll, 0 sonst
  1282. # < STACK_1: Name
  1283. # < STACK_0: Typ
  1284. # Erniedrigt STACK um 1
  1285. # kann GC auslösen
  1286.   local void split_name_type (uintL skip);
  1287.   local void split_name_type(skip)
  1288.     var reg2 uintL skip;
  1289.     { var reg5 object string = STACK_0;
  1290.       var reg7 uintL length = TheSstring(string)->length;
  1291.       # Nach dem letzten Punkt suchen:
  1292.       var reg4 uintL index = length;
  1293.       { var reg1 uintB* ptr = &TheSstring(string)->data[index];
  1294.         while (index>skip)
  1295.           { --ptr;
  1296.             if (*ptr == '.') goto punkt;
  1297.             index--;
  1298.       }   }
  1299.       # kein Punkt gefunden -> Typ := NIL
  1300.       pushSTACK(NIL);
  1301.       goto name_type_ok;
  1302.       punkt: # Punkt bei index gefunden
  1303.       { # type := (substring string index)
  1304.         var reg3 uintL count = length-index;
  1305.         var reg6 object type = allocate_string(count);
  1306.         var reg1 uintB* ptr2 = &TheSstring(type)->data[0];
  1307.         var reg2 uintB* ptr1 = &TheSstring(STACK_0)->data[index];
  1308.         dotimesL(count,count, { *ptr2++ = *ptr1++; } );
  1309.         pushSTACK(type);
  1310.       }
  1311.       { # name := (substring string 0 (1- index))
  1312.         var reg3 uintL count = index-1;
  1313.         var reg6 object name = allocate_string(count);
  1314.         var reg1 uintB* ptr2 = &TheSstring(name)->data[0];
  1315.         var reg2 uintB* ptr1 = &TheSstring(STACK_1)->data[0];
  1316.         dotimesL(count,count, { *ptr2++ = *ptr1++; } );
  1317.         STACK_1 = name;
  1318.       }
  1319.       name_type_ok: ;
  1320.     }
  1321. #endif
  1322.  
  1323. LISPFUN(parse_namestring,1,2,norest,key,3,\
  1324.         (kw(start),kw(end),kw(junk_allowed)) )
  1325. # (PARSE-NAMESTRING thing [host [defaults [:start] [:end] [:junk-allowed]]]),
  1326. # CLTL S. 414
  1327.   { # Stackaufbau: thing, host, defaults, start, end, junk-allowed.
  1328.     var reg6 boolean junk_allowed;
  1329.     var reg7 boolean parse_logical = FALSE;
  1330.     # 1. junk-allowed überprüfen:
  1331.     { var reg1 object obj = popSTACK(); # junk-allowed-Argument
  1332.       if (eq(obj,unbound))
  1333.         { junk_allowed = FALSE; }
  1334.         else
  1335.         if (nullp(obj)) { junk_allowed = FALSE; } else { junk_allowed = TRUE; }
  1336.     }
  1337.     # Stackaufbau: thing, host, defaults, start, end.
  1338.     # 2. Default-Wert für start ist 0:
  1339.     { if (eq(STACK_1,unbound)) { STACK_1 = Fixnum_0; }}
  1340.     # 3. host überprüfen:
  1341.     #if HAS_HOST || defined(LOGICAL_PATHNAMES)
  1342.     { var reg2 object host;
  1343.       #if HAS_HOST
  1344.       host = test_optional_host(STACK_3,FALSE);
  1345.       #else
  1346.       host = test_optional_host(STACK_3);
  1347.       #endif
  1348.       if (nullp(host))
  1349.         { # host := (PATHNAME-HOST defaults)
  1350.           var reg1 object defaults = test_default_pathname(STACK_2);
  1351.           #ifdef LOGICAL_PATHNAMES
  1352.           if (logpathnamep(defaults))
  1353.             { parse_logical = TRUE; host = TheLogpathname(defaults)->pathname_host; }
  1354.             else
  1355.           #endif
  1356.             {
  1357.               #if HAS_HOST
  1358.               host = ThePathname(defaults)->pathname_host;
  1359.               #else
  1360.               host = NIL;
  1361.               #endif
  1362.             }
  1363.         }
  1364.       #ifdef LOGICAL_PATHNAMES
  1365.       elif (logical_host_p(host))
  1366.         { parse_logical = TRUE; host = string_upcase(host); }
  1367.       #endif
  1368.       STACK_3 = host;
  1369.     }
  1370.     #else
  1371.     { test_optional_host(STACK_3); }
  1372.     #endif
  1373.     # 4. thing muß ein String sein:
  1374.     { var reg5 object thing = STACK_4;
  1375.       if (xpathnamep(thing)) # Pathname?
  1376.         { value1 = thing; # 1. Wert thing
  1377.           fertig:
  1378.           value2 = STACK_1; mv_count=2; # 2. Wert start
  1379.           skipSTACK(5); return;
  1380.         }
  1381.       if (streamp(thing)) # Stream?
  1382.         { thing = as_file_stream(thing);
  1383.           value1 = TheStream(thing)->strm_file_name; # 1. Wert: Filename
  1384.           goto fertig; # 2. Wert wie oben
  1385.         }
  1386.       # thing sollte nun wenigstens ein String oder Symbol sein:
  1387.       if (!stringp(thing))
  1388.         { if (!symbolp(thing)) { fehler_thing(thing); }
  1389.           thing = Symbol_name(thing); # Symbol -> Symbolname verwenden
  1390.           if (!parse_logical)
  1391.             {
  1392.               #if defined(PATHNAME_UNIX) || defined(PATHNAME_OS2) || defined(PATHNAME_RISCOS)
  1393.               # Betriebssystem mit Vorzug für Kleinbuchstaben
  1394.               thing = copy_string(thing); # ja -> mit STRING-DOWNCASE umwandeln
  1395.               nstring_downcase(&TheSstring(thing)->data[0],TheSstring(thing)->length);
  1396.               #endif
  1397.               #ifdef PATHNAME_AMIGAOS
  1398.               # Betriebssystem mit Vorzug für Capitalize
  1399.               thing = copy_string(thing); # ja -> mit STRING-CAPITALIZE umwandeln
  1400.               nstring_capitalize(&TheSstring(thing)->data[0],TheSstring(thing)->length);
  1401.               #endif
  1402.             }
  1403.           STACK_4 = thing; # und in den Stack zurückschreiben
  1404.         }
  1405.       # thing = STACK_4 ist jetzt ein String.
  1406.       { # Er wird durchlaufen.
  1407.         var zustand z; # laufender Zustand
  1408.         #ifdef PATHNAME_RISCOS
  1409.         # Hilfsvariablen zur Umsetzung eines new_thing-relativen FNindex
  1410.         # in einen thing-relativen FNindex.
  1411.         var object FNindex_limit = Fixnum_0;
  1412.         var sintL FNindex_offset = 0;
  1413.         var object FNindex_fallback;
  1414.         #endif
  1415.        {var object string; # String thing
  1416.         # Grenzen überprüfen, mit thing, start, end als Argumenten:
  1417.         pushSTACK(thing); pushSTACK(STACK_(1+1)); pushSTACK(STACK_(0+2));
  1418.         test_string_limits(&string,&z.index,&z.count);
  1419.         # z.index = Wert des start-Arguments,
  1420.         # z.count = Anzahl der Characters.
  1421.         z.FNindex = fixnum(z.index);
  1422.         # z.FNindex = start-Index als Fixnum.
  1423.         string = array_displace_check(string,z.count,&z.index); # Datenvektor holen,
  1424.         # z.index = Offset + Startindex = Startoffset
  1425.         pushSTACK(string);
  1426.        }
  1427.         #ifdef LOGICAL_PATHNAMES
  1428.         if (parse_logical)
  1429.           { pushSTACK(allocate_logpathname());
  1430.             # Stackaufbau: ..., Datenvektor, Pathname.
  1431.            {var reg1 uintL remaining = parse_logical_pathnamestring(z);
  1432.             z.index += z.count-remaining; z.FNindex = fixnum_inc(z.FNindex,z.count-remaining); z.count = remaining;
  1433.           }}
  1434.           else
  1435.         #endif
  1436.           {
  1437.             #ifdef PATHNAME_RISCOS
  1438.               # If the string starts with a system variable in <...> syntax,
  1439.               # then perform the substitution
  1440.               # (string-concat "<" var ">" tail) --> (string-concat (sys::getenv var) tail).
  1441.               if ((!(z.count==0)) && (TheSstring(STACK_0)->data[z.index] == '<'))
  1442.                 { var zustand startz = z; # Start-Zustand
  1443.                   var reg1 uintB ch;
  1444.                   # Character '<' übergehen:
  1445.                   z.index++; z.FNindex = fixnum_inc(z.FNindex,1); z.count--;
  1446.                   loop
  1447.                     { if (z.count==0) goto no_envvar;
  1448.                       ch = TheSstring(STACK_0)->data[z.index]; # nächstes Character
  1449.                       if (ch=='>') break;
  1450.                       if (!(graphic_char_p(ch) && !(ch=='*') && !(ch=='#'))) goto no_envvar;
  1451.                       # gültiges Character übergehen:
  1452.                       z.index++; z.FNindex = fixnum_inc(z.FNindex,1); z.count--;
  1453.                     }
  1454.                   FNindex_fallback = z.FNindex;
  1455.                   # Character '>' übergehen:
  1456.                   z.index++; z.FNindex = fixnum_inc(z.FNindex,1); z.count--;
  1457.                   # Environment-Variable als ASCIZ-String bauen:
  1458.                  {var reg4 object envvar;
  1459.                   { var reg3 uintL len = z.index - startz.index - 2;
  1460.                     if (len==0) goto no_envvar;
  1461.                     envvar = allocate_string(len+1);
  1462.                     # und füllen:
  1463.                    {var reg2 uintB* ptr1 = &TheSstring(STACK_0)->data[startz.index+1];
  1464.                     var reg1 uintB* ptr2 = &TheSstring(envvar)->data[0];
  1465.                     dotimesL(len,len, { *ptr2++ = *ptr1++; });
  1466.                     *ptr2 = '\0';
  1467.                   }}
  1468.                   # Dessen Wert holen:
  1469.                    begin_system_call();
  1470.                   {var reg1 const char* envval = getenv(TheAsciz(envvar));
  1471.                    end_system_call();
  1472.                    if (envval==NULL)
  1473.                      { pushSTACK(envvar);
  1474.                        pushSTACK(S(parse_namestring));
  1475.                        //: DEUTSCH "~: Es gibt keine Environment-Variable ~."
  1476.                        //: ENGLISH "~: there is no environment variable ~"
  1477.                        //: FRANCAIS "~ : Il n'y a pas de variable ~ dans l'environnement."
  1478.                        fehler(error, GETTEXT("~: there is no environment variable ~"));
  1479.                      }
  1480.                    pushSTACK(asciz_to_string(envval)); # Wert der Variablen als String
  1481.                  }}
  1482.                   # Reststück bilden:
  1483.                   { var reg4 uintL len = z.count;
  1484.                     var reg3 object tail = allocate_string(len);
  1485.                     var reg2 uintB* ptr1 = &TheSstring(STACK_1)->data[z.index];
  1486.                     var reg1 uintB* ptr2 = &TheSstring(tail)->data[0];
  1487.                     dotimesL(len,len, { *ptr2++ = *ptr1++; } );
  1488.                     pushSTACK(tail);
  1489.                   }
  1490.                   # Beides zusammenhängen, thing ersetzen:
  1491.                   { var reg2 uintL envval_len = TheSstring(STACK_1)->length;
  1492.                     var reg1 object new_thing = string_concat(2);
  1493.                     STACK_(4+1) = STACK_0 = new_thing;
  1494.                     # Der 2. Wert FNindex muß nachher noch modifiziert werden.
  1495.                     FNindex_limit = fixnum(envval_len);
  1496.                     FNindex_offset = (sintL)posfixnum_to_L(z.FNindex) - (sintL)envval_len;
  1497.                     z.index = 0; z.count = TheSstring(new_thing)->length; z.FNindex = Fixnum_0;
  1498.                   }
  1499.                   goto envvar_ok;
  1500.                  no_envvar: # keine Environment-Variable
  1501.                   z = startz; # zum Start zurück
  1502.                 }
  1503.               envvar_ok: ;
  1504.             #endif
  1505.             pushSTACK(allocate_pathname());
  1506.             # Stackaufbau: ..., Datenvektor, Pathname.
  1507.             #if HAS_HOST
  1508.               # Host-Specification parsen:
  1509.               {var reg3 object host;
  1510.                { var zustand startz = z; # Start-Zustand
  1511.                  var reg1 uintB ch;
  1512.                  #if defined(PATHNAME_RISCOS)
  1513.                    # Kommt eine Folge von Zeichen, eingeschlossen in '-',
  1514.                    # oder eine Folge von Zeichen und dann eine ':' ?
  1515.                    if (z.count==0) goto no_hostspec; # String schon zu Ende -> kein Host
  1516.                    ch = TheSstring(STACK_1)->data[z.index]; # nächstes Character
  1517.                    if (ch=='-')
  1518.                      { # '-' übergehen:
  1519.                        z.index++; z.FNindex = fixnum_inc(z.FNindex,1); z.count--;
  1520.                        loop
  1521.                          { if (z.count==0) goto no_hostspec; # String schon zu Ende -> kein Host
  1522.                            ch = TheSstring(STACK_1)->data[z.index]; # nächstes Character
  1523.                            if (ch=='-') break;
  1524.                            if (!legal_hostchar(ch)) goto no_hostspec;
  1525.                            # gültiges Character übergehen:
  1526.                            z.index++; z.FNindex = fixnum_inc(z.FNindex,1); z.count--;
  1527.                          }
  1528.                        # Host-String bilden:
  1529.                        { var reg4 uintL len = z.index - startz.index - 1;
  1530.                          if (len==0) goto no_hostspec;
  1531.                          host = allocate_string(len);
  1532.                          # und füllen:
  1533.                         {var reg2 uintB* ptr1 = &TheSstring(STACK_1)->data[startz.index+1];
  1534.                          var reg3 uintB* ptr2 = &TheSstring(host)->data[0];
  1535.                          dotimesL(len,len, { *ptr2++ = *ptr1++; });
  1536.                        }}
  1537.                      }
  1538.                      else
  1539.                      { loop
  1540.                          { if (!legal_hostchar(ch)) goto no_hostspec;
  1541.                            # gültiges Character übergehen:
  1542.                            z.index++; z.FNindex = fixnum_inc(z.FNindex,1); z.count--;
  1543.                            if (z.count==0) goto no_hostspec; # String schon zu Ende -> kein Host
  1544.                            ch = TheSstring(STACK_1)->data[z.index]; # nächstes Character
  1545.                            if (ch==':') break;
  1546.                          }
  1547.                        # Host-String bilden:
  1548.                        { var reg4 uintL len = z.index - startz.index;
  1549.                          host = allocate_string(len);
  1550.                          # und füllen:
  1551.                         {var reg2 uintB* ptr1 = &TheSstring(STACK_1)->data[startz.index];
  1552.                          var reg3 uintB* ptr2 = &TheSstring(host)->data[0];
  1553.                          dotimesL(len,len, { *ptr2++ = *ptr1++; });
  1554.                        }}
  1555.                      }
  1556.                    # Character '-' bzw. ':' übergehen:
  1557.                    z.index++; z.FNindex = fixnum_inc(z.FNindex,1); z.count--;
  1558.                    goto hostspec_ok;
  1559.                  #else
  1560.                    # Kommt eine Folge von alphanumerischen Zeichen und dann ein ':' bzw. '::' ?
  1561.                    loop
  1562.                      { if (z.count==0) goto no_hostspec; # String schon zu Ende -> kein Host
  1563.                        ch = TheSstring(STACK_1)->data[z.index]; # nächstes Character
  1564.                        if (!alphanumericp(ch)) break;
  1565.                        # alphanumerisches Character übergehen:
  1566.                        z.index++; z.FNindex = fixnum_inc(z.FNindex,1); z.count--;
  1567.                      }
  1568.                    if (!(ch==':')) goto no_hostspec; # kein ':' -> kein Host
  1569.                    # Host-String bilden:
  1570.                    { var reg4 uintL len = z.index - startz.index;
  1571.                      host = allocate_string(len);
  1572.                      # und füllen:
  1573.                     {var reg2 uintB* ptr1 = &TheSstring(STACK_1)->data[startz.index];
  1574.                      var reg3 uintB* ptr2 = &TheSstring(host)->data[0];
  1575.                      dotimesL(len,len, { *ptr2++ = *ptr1++; });
  1576.                    }}
  1577.                    # Character ':' übergehen:
  1578.                    z.index++; z.FNindex = fixnum_inc(z.FNindex,1); z.count--;
  1579.                    goto hostspec_ok;
  1580.                  #endif
  1581.                  no_hostspec: # keine Host-Specification
  1582.                    z = startz; # zum Start zurück
  1583.                    host = STACK_(3+2); # Default-Host
  1584.                }
  1585.                hostspec_ok:
  1586.                # Host eintragen:
  1587.                ThePathname(STACK_0)->pathname_host = host;
  1588.               }
  1589.             #endif
  1590.             #if HAS_DEVICE
  1591.              #if defined(PATHNAME_MSDOS) || defined(PATHNAME_OS2)
  1592.               # Einbuchstabige Device-Specification und evtl. Seriennummer parsen:
  1593.               {var reg3 object device = NIL; # Device := NIL
  1594.                # Drive-Specification parsen:
  1595.                # Kommt evtl. ein Buchstabe ('*','A'-'Z','a'-'z'), evtl. eine
  1596.                # Seriennummer (Integer >=0, <2^24) und dann ein ':' ?
  1597.                { var zustand startz = z; # Start-Zustand
  1598.                  var reg1 uintB ch;
  1599.                  if (z.count==0) goto no_drivespec; # String schon zu Ende ?
  1600.                  ch = TheSstring(STACK_1)->data[z.index]; # nächstes Character
  1601.                  ch = up_case(ch); # als Großbuchstabe
  1602.                  if (ch == '*')
  1603.                    # ch = '*' -> Device := :WILD
  1604.                    { device = S(Kwild); }
  1605.                  elif ((ch >= 'A') && (ch <= 'Z'))
  1606.                    # 'A' <= ch <= 'Z' -> Device := "ch"
  1607.                    { var reg1 object string = allocate_string(1); # String der Länge 1
  1608.                      TheSstring(string)->data[0] = ch; # mit ch als einzigem Buchstaben
  1609.                      device = string;
  1610.                    }
  1611.                  else goto no_device;
  1612.                  # Device OK, Character übergehen:
  1613.                  z.index++; z.FNindex = fixnum_inc(z.FNindex,1); z.count--;
  1614.                  if (z.count==0) goto no_drivespec; # String schon zu Ende ?
  1615.                  ch = TheSstring(STACK_1)->data[z.index]; # nächstes Character
  1616.                  ch = up_case(ch); # als Großbuchstabe
  1617.                  no_device:
  1618.                  # mit Doppelpunkt abgeschlossen?
  1619.                  if (!(ch == ':')) goto no_drivespec;
  1620.                  # Character übergehen:
  1621.                  z.index++; z.FNindex = fixnum_inc(z.FNindex,1); z.count--;
  1622.                  goto drivespec_ok;
  1623.                  no_drivespec:
  1624.                  # Es ist nicht gelungen, eine Drive-Specification zu parsen.
  1625.                  z = startz; # Start-Zustand wiederherstellen
  1626.                  device = NIL; # Device := NIL
  1627.                }
  1628.                drivespec_ok:
  1629.                ThePathname(STACK_0)->pathname_device = device; # Device eintragen
  1630.               }
  1631.              #endif
  1632.              #ifdef PATHNAME_AMIGAOS
  1633.               # Device-Specification parsen:
  1634.               {var reg3 object device;
  1635.                # Kommt eine nichtleere Folge von alphanumerischen Zeichen und dann ein ':' ?
  1636.                { var zustand startz = z; # Start-Zustand
  1637.                  var reg1 uintB ch;
  1638.                  loop
  1639.                    { if (z.count==0) goto no_devicespec; # String schon zu Ende -> kein Device
  1640.                      ch = TheSstring(STACK_1)->data[z.index]; # nächstes Character
  1641.                      if (!legal_namechar(ch)) break;
  1642.                      # alphanumerisches Character übergehen:
  1643.                      z.index++; z.FNindex = fixnum_inc(z.FNindex,1); z.count--;
  1644.                    }
  1645.                  if (!(ch==':')) goto no_devicespec; # kein ':' -> kein Device
  1646.                  if (z.index==startz.index) goto no_devicespec; # ':' am Anfang ist kein Device
  1647.                  # Device-String bilden:
  1648.                  { var reg4 uintL len = z.index - startz.index;
  1649.                    device = allocate_string(len);
  1650.                    # und füllen:
  1651.                   {var reg2 uintB* ptr1 = &TheSstring(STACK_1)->data[startz.index];
  1652.                    var reg3 uintB* ptr2 = &TheSstring(device)->data[0];
  1653.                    dotimesL(len,len, { *ptr2++ = *ptr1++; });
  1654.                  }}
  1655.                  # Character ':' nicht übergehen; das ergibt dann :ABSOLUTE.
  1656.                  goto devicespec_ok;
  1657.                  no_devicespec: # keine Device-Specification
  1658.                    z = startz; # zum Start zurück
  1659.                    device = NIL; # Device NIL
  1660.                }
  1661.                devicespec_ok:
  1662.                # Device eintragen:
  1663.                ThePathname(STACK_0)->pathname_device = device;
  1664.               }
  1665.              #endif
  1666.              #ifdef PATHNAME_RISCOS
  1667.               # Device-Specification parsen:
  1668.               {var reg3 object device;
  1669.                # Kommt ein ':', eine nichtleere Folge von Zeichen und dann ein '.' ?
  1670.                { var zustand startz = z; # Start-Zustand
  1671.                  var reg1 uintB ch;
  1672.                  if (z.count==0) goto no_devicespec; # String schon zu Ende -> kein Device
  1673.                  ch = TheSstring(STACK_1)->data[z.index]; # nächstes Character
  1674.                  if (!(ch==':')) goto no_devicespec; # kein ':' -> kein Device
  1675.                  # Character ':' übergehen:
  1676.                  z.index++; z.FNindex = fixnum_inc(z.FNindex,1); z.count--;
  1677.                  loop
  1678.                    { if (z.count==0) goto no_devicespec; # String schon zu Ende -> kein Device
  1679.                      ch = TheSstring(STACK_1)->data[z.index]; # nächstes Character
  1680.                      if (!(legal_namechar(ch) && !(ch=='*') && !singlewild_char_p(ch))) break;
  1681.                      # gültiges Character übergehen:
  1682.                      z.index++; z.FNindex = fixnum_inc(z.FNindex,1); z.count--;
  1683.                    }
  1684.                  if (!(ch=='.')) goto no_devicespec; # kein '.' -> kein Device
  1685.                  # Device-String bilden:
  1686.                  { var reg4 uintL len = z.index - startz.index - 1;
  1687.                    if (len==0) goto no_devicespec;
  1688.                    device = allocate_string(len);
  1689.                    # und füllen:
  1690.                   {var reg2 uintB* ptr1 = &TheSstring(STACK_1)->data[startz.index+1];
  1691.                    var reg3 uintB* ptr2 = &TheSstring(device)->data[0];
  1692.                    dotimesL(len,len, { *ptr2++ = *ptr1++; });
  1693.                  }}
  1694.                  # Character '.' übergehen:
  1695.                  z.index++; z.FNindex = fixnum_inc(z.FNindex,1); z.count--;
  1696.                  goto devicespec_ok;
  1697.                  no_devicespec: # keine Device-Specification
  1698.                    z = startz; # zum Start zurück
  1699.                    device = NIL; # Device NIL
  1700.                }
  1701.                devicespec_ok:
  1702.                # Device eintragen:
  1703.                ThePathname(STACK_0)->pathname_device = device;
  1704.               }
  1705.              #endif
  1706.             #endif
  1707.             # Directory-Start eintragen:
  1708.             { var reg1 object new_cons = allocate_cons(); # neues Cons für Startpoint
  1709.               ThePathname(STACK_0)->pathname_directory = new_cons;
  1710.               pushSTACK(new_cons); # neues (last (pathname-directory Pathname))
  1711.             }
  1712.             # Stackaufbau: ..., Datenvektor, Pathname, (last (pathname-directory Pathname)).
  1713.             # Subdirectories parsen:
  1714.             # Trennzeichen zwischen subdirs ist unter MSDOS sowohl '\' als auch '/':
  1715.             #if defined(PATHNAME_MSDOS) || defined(PATHNAME_OS2)
  1716.              #define slashp(c)  (((c) == '\\') || ((c) == '/'))
  1717.             #endif
  1718.             #if defined(PATHNAME_UNIX) || defined(PATHNAME_AMIGAOS)
  1719.              #define slashp(c)  ((c) == '/')
  1720.             #endif
  1721.             #ifdef PATHNAME_RISCOS
  1722.              #define slashp(c)  ((c) == '.')
  1723.             #endif
  1724.             {
  1725.               #if defined(USER_HOMEDIR) && defined(PATHNAME_UNIX)
  1726.               # Falls sofort ein '~' kommt, wird bis zum nächsten '/' oder Stringende
  1727.               # ein Username gelesen und das Home-Directory dieses Users eingesetzt:
  1728.               if ((!(z.count == 0)) && (TheSstring(STACK_2)->data[z.index] == '~'))
  1729.                 # Es kommt sofort ein '~'.
  1730.                 { # Character übergehen:
  1731.                   z.index++; z.FNindex = fixnum_inc(z.FNindex,1); z.count--;
  1732.                  {var reg6 object userhomedir; # Pathname des User-Homedir
  1733.                   # nächsten '/' suchen:
  1734.                   var reg2 uintB* charptr = &TheSstring(STACK_2)->data[z.index];
  1735.                   var reg3 uintL charcount = 0;
  1736.                   { var reg4 uintL count;
  1737.                     dotimesL(count,z.count,
  1738.                       { if (*charptr++ == '/') break;
  1739.                         charcount++;
  1740.                       });
  1741.                   }
  1742.                   # Username hat charcount Zeichen
  1743.                   if (charcount==0)
  1744.                     { userhomedir = O(user_homedir); } # nur '~' -> User-Homedir
  1745.                     else
  1746.                     { # Username als ASCIZ-String bauen:
  1747.                       var reg5 object username = allocate_string(charcount+1);
  1748.                       { var reg1 uintB* charptr2 = &TheSstring(username)->data[0];
  1749.                         var reg4 uintL count;
  1750.                         charptr = &TheSstring(STACK_2)->data[z.index];
  1751.                         dotimespL(count,charcount, { *charptr2++ = *charptr++; } );
  1752.                         *charptr2 = '\0';
  1753.                       }
  1754.                       # Dessen Home-Directory aus dem Passwort-File holen:
  1755.                       begin_system_call();
  1756.                       errno = 0;
  1757.                      {var reg1 struct passwd * userpasswd = getpwnam(TheAsciz(username));
  1758.                       end_system_call();
  1759.                       if (userpasswd == (struct passwd *)NULL) # erfolglos?
  1760.                         { if (!(errno==0)) { OS_error(); } # Error melden
  1761.                           # sonst: Fehler
  1762.                           pushSTACK(username);
  1763.                           pushSTACK(S(parse_namestring));
  1764.                           //: DEUTSCH "~: Es gibt keinen Benutzer mit Namen ~."
  1765.                           //: ENGLISH "~: there is no user named ~"
  1766.                           //: FRANCAIS "~ : Il n'y a pas d'utilisateur de nom ~."
  1767.                           fehler(error, GETTEXT("~: there is no user named ~"));
  1768.                         }
  1769.                       userhomedir = asciz_dir_to_pathname(userpasswd->pw_dir); # Homedir als Pathname
  1770.                     }}
  1771.                   # Directory aus dem Pathname userhomedir kopieren:
  1772.                   # (copy-list dir) = (nreconc (reverse dir) nil),
  1773.                   # dabei dessen letztes Cons merken.
  1774.                   userhomedir = reverse(ThePathname(userhomedir)->pathname_directory);
  1775.                   STACK_0 = userhomedir; userhomedir = nreconc(userhomedir,NIL);
  1776.                   ThePathname(STACK_1)->pathname_directory = userhomedir;
  1777.                   # username-Characters übergehen:
  1778.                   z.index += charcount; z.FNindex = fixnum_inc(z.FNindex,charcount); z.count -= charcount;
  1779.                   # Falls der String zu Ende ist: fertig,
  1780.                   # sonst kommt sofort ein '/', es wird übergangen:
  1781.                   if (z.count==0)
  1782.                     { pushSTACK(NIL); pushSTACK(NIL); goto after_name_type; } # Name und Typ := NIL
  1783.                   # Character übergehen:
  1784.                   z.index++; z.FNindex = fixnum_inc(z.FNindex,1); z.count--;
  1785.                 }}
  1786.               else
  1787.               #endif
  1788.               #if defined(PATHNAME_UNIX) && 0 # Wozu braucht man das, außer für $HOME ?
  1789.               # Falls sofort ein '$' kommt, wird bis zum nächsten '/' oder Stringende
  1790.               # eine Environment-Variable gelesen und ihr Wert eingesetzt:
  1791.               if ((!(z.count == 0)) && (TheSstring(STACK_2)->data[z.index] == '$'))
  1792.                 # Es kommt sofort ein '$'.
  1793.                 { # Character übergehen:
  1794.                   z.index++; z.FNindex = fixnum_inc(z.FNindex,1); z.count--;
  1795.                  {var reg6 object envval_dir;
  1796.                   # nächsten '/' suchen:
  1797.                   var reg2 uintB* charptr = &TheSstring(STACK_2)->data[z.index];
  1798.                   var reg3 uintL charcount = 0;
  1799.                   { var reg4 uintL count;
  1800.                     dotimesL(count,z.count,
  1801.                       { if (*charptr++ == '/') break;
  1802.                         charcount++;
  1803.                       });
  1804.                   }
  1805.                   # Environment-Variable hat charcount Zeichen,
  1806.                   # als ASCIZ-String bauen:
  1807.                   { var reg5 object envvar = allocate_string(charcount+1);
  1808.                     { var reg1 uintB* charptr2 = &TheSstring(envvar)->data[0];
  1809.                       var reg4 uintL count;
  1810.                       charptr = &TheSstring(STACK_2)->data[z.index];
  1811.                       dotimesL(count,charcount, { *charptr2++ = *charptr++; } );
  1812.                       *charptr2 = '\0';
  1813.                     }
  1814.                     # Dessen Wert holen:
  1815.                     begin_system_call();
  1816.                    {var reg1 const char* envval = getenv(TheAsciz(envvar));
  1817.                     end_system_call();
  1818.                     if (envval==NULL)
  1819.                       { pushSTACK(envvar);
  1820.                         pushSTACK(S(parse_namestring));
  1821.                         //: DEUTSCH "~: Es gibt keine Environment-Variable ~."
  1822.                         //: ENGLISH "~: there is no environment variable ~"
  1823.                         //: FRANCAIS "~ : Il n'y a pas de variable ~ dans l'environnement."
  1824.                         fehler(error, GETTEXT("~: there is no environment variable ~"));
  1825.                       }
  1826.                     envval_dir = asciz_dir_to_pathname(envval); # Wert der Variablen als Pathname
  1827.                   }}
  1828.                   # Directory aus dem Pathname envval_dir kopieren:
  1829.                   # (copy-list dir) = (nreconc (reverse dir) nil),
  1830.                   # dabei dessen letztes Cons merken.
  1831.                   envval_dir = reverse(ThePathname(envval_dir)->pathname_directory);
  1832.                   STACK_0 = envval_dir; envval_dir = nreconc(envval_dir,NIL);
  1833.                   ThePathname(STACK_1)->pathname_directory = envval_dir;
  1834.                   # envvar-Characters übergehen:
  1835.                   z.index += charcount; z.FNindex = fixnum_inc(z.FNindex,charcount); z.count -= charcount;
  1836.                   # Falls der String zu Ende ist: fertig,
  1837.                   # sonst kommt sofort ein '/', es wird übergangen:
  1838.                   if (z.count==0)
  1839.                     { pushSTACK(NIL); pushSTACK(NIL); goto after_name_type; } # Name und Typ := NIL
  1840.                   # Character übergehen:
  1841.                   z.index++; z.FNindex = fixnum_inc(z.FNindex,1); z.count--;
  1842.                 }}
  1843.               else
  1844.               #endif
  1845.               #if defined(PATHNAME_UNIX) || defined(PATHNAME_MSDOS) || defined(PATHNAME_OS2)
  1846.               # Falls sofort ein '\' bzw. '/' kommt, wird er übergangen, und es kommt
  1847.               # :ABSOLUTE (sonst :RELATIVE) als erstes subdir:
  1848.               if ((!(z.count == 0)) && slashp(TheSstring(STACK_2)->data[z.index]))
  1849.                 # Es kommt sofort ein '\' bzw. '/'.
  1850.                 { # Character übergehen:
  1851.                   z.index++; z.FNindex = fixnum_inc(z.FNindex,1); z.count--;
  1852.                   Car(STACK_0) = S(Kabsolute); # Startpoint = :ABSOLUTE
  1853.                 }
  1854.                 else
  1855.                 # Es kommt nicht sofort ein '\' bzw. '/'.
  1856.                 { Car(STACK_0) = S(Krelative); } # Startpoint = :RELATIVE
  1857.               #endif
  1858.               #ifdef PATHNAME_AMIGAOS
  1859.               # Falls sofort ein ':' kommt, wird er übergangen, und es kommt
  1860.               # :ABSOLUTE (sonst :RELATIVE) als erstes subdir:
  1861.               if ((!(z.count == 0)) && (TheSstring(STACK_2)->data[z.index] == ':'))
  1862.                 # Es kommt sofort ein ':'.
  1863.                 { # Character übergehen:
  1864.                   z.index++; z.FNindex = fixnum_inc(z.FNindex,1); z.count--;
  1865.                   Car(STACK_0) = S(Kabsolute); # directory = (:ABSOLUTE)
  1866.                 }
  1867.                 else
  1868.                 # Es kommt nicht sofort ein ':'.
  1869.                 { Car(STACK_0) = S(Krelative); } # directory = (:RELATIVE)
  1870.               #endif
  1871.               #ifdef PATHNAME_RISCOS
  1872.               # Präfix '$.' oder '&.' oder '@.' oder '%.' oder '\.' parsen.
  1873.               if ((z.count >= 2) && (TheSstring(STACK_2)->data[z.index+1] == '.'))
  1874.                 { switch (TheSstring(STACK_2)->data[z.index])
  1875.                     { case '$': Car(STACK_0) = S(Kroot); break; # directory = (:ABSOLUTE :ROOT)
  1876.                       case '&': Car(STACK_0) = S(Khome); break; # directory = (:ABSOLUTE :HOME)
  1877.                       case '@': Car(STACK_0) = S(Kcurrent); break; # directory = (:ABSOLUTE :CURRENT)
  1878.                       case '%': Car(STACK_0) = S(Klibrary); break; # directory = (:ABSOLUTE :LIBRARY)
  1879.                       case '\\': Car(STACK_0) = S(Kprevious); break; # directory = (:ABSOLUTE :PREVIOUS)
  1880.                       default: goto prefix_relative;
  1881.                     }
  1882.                   # Präfix übergehen:
  1883.                   z.index+=2; z.FNindex = fixnum_inc(z.FNindex,2); z.count-=2;
  1884.                   # (pathname-directory pathname) um ein Cons (:ABSOLUTE) verlängern:
  1885.                  {var reg1 object new_cons = allocate_cons(); # neues Cons
  1886.                   Car(new_cons) = S(Kabsolute); Cdr(new_cons) = STACK_0;
  1887.                   ThePathname(STACK_1)->pathname_directory = new_cons;
  1888.                 }}
  1889.                 else
  1890.                 prefix_relative:
  1891.                 { Car(STACK_0) = S(Krelative); } # directory = (:RELATIVE)
  1892.               #endif
  1893.               #if !defined(PATHNAME_RISCOS)
  1894.               loop
  1895.                 { # Versuche, ein weiteres Unterdirectory zu parsen.
  1896.                   #ifdef PATHNAME_EXT83
  1897.                     # Kommt '.\' oder '..\' oder '...\' ?
  1898.                     if ((!(z.count == 0)) && (TheSstring(STACK_2)->data[z.index] == '.'))
  1899.                       { # nächstes Character ist ein '.'.
  1900.                         var zustand subdirz = z; # Zustand beim Start des Subdirectories
  1901.                         # Character übergehen:
  1902.                         z.index++; z.FNindex = fixnum_inc(z.FNindex,1); z.count--;
  1903.                         if (z.count == 0) goto no_dots; # String schon zu Ende ?
  1904.                        {var reg1 uintB ch = TheSstring(STACK_2)->data[z.index]; # nächstes Character
  1905.                         if (slashp(ch))
  1906.                           # '.\' angetroffen -> (cons :CURRENT NIL) bauen
  1907.                           { pushSTACK(S(Kcurrent)); goto dots; }
  1908.                         if (!(ch == '.')) goto no_dots;
  1909.                         # zweites Character war auch ein '.'.
  1910.                         # Character übergehen:
  1911.                         z.index++; z.FNindex = fixnum_inc(z.FNindex,1); z.count--;
  1912.                         if (z.count == 0) goto no_dots; # String schon zu Ende ?
  1913.                         ch = TheSstring(STACK_2)->data[z.index]; # nächstes Character
  1914.                         if (slashp(ch))
  1915.                           # '..\' angetroffen -> (cons :PARENT NIL) bauen
  1916.                           { pushSTACK(S(Kparent)); goto dots; }
  1917.                         if (!(ch == '.')) goto no_dots;
  1918.                         # drittes Character war auch ein '.'.
  1919.                         # Character übergehen:
  1920.                         z.index++; z.FNindex = fixnum_inc(z.FNindex,1); z.count--;
  1921.                         if (z.count == 0) goto no_dots; # String schon zu Ende ?
  1922.                         ch = TheSstring(STACK_2)->data[z.index]; # nächstes Character
  1923.                         if (slashp(ch))
  1924.                           # '...\' angetroffen -> (cons :WILD-INFERIORS NIL) bauen
  1925.                           { pushSTACK(S(Kwild_inferiors)); goto dots; }
  1926.                         goto no_dots;
  1927.                        }
  1928.                         dots:
  1929.                         # '.\' oder '..\' oder '...\' angetroffen, Keyword im Stack.
  1930.                         # Character '\' übergehen:
  1931.                         z.index++; z.FNindex = fixnum_inc(z.FNindex,1); z.count--;
  1932.                         goto subdir_ok;
  1933.                         no_dots:
  1934.                         z = subdirz; # Zustand wiederherstellen
  1935.                       }
  1936.                     # Versuche, normale 'name.typ'-Syntax zu parsen:
  1937.                     pushSTACK(NIL); # dummy
  1938.                     { # Name, hat max. 8 Buchstaben:
  1939.                       var reg1 object name = parse_name_or_type(&z,8,NIL);
  1940.                       STACK_0 = name;
  1941.                     }
  1942.                     # Versuche, '.typ'-Syntax zu parsen:
  1943.                     { var reg1 object type;
  1944.                       if ((!(z.count==0)) && (TheSstring(STACK_3)->data[z.index] == '.'))
  1945.                         { # Es kommt ein '.'. Character übergehen:
  1946.                           z.index++; z.FNindex = fixnum_inc(z.FNindex,1); z.count--;
  1947.                           # Typ, hat max. 3 Buchstaben:
  1948.                           type = parse_name_or_type(&z,3,O(leer_string));
  1949.                         }
  1950.                         else
  1951.                         { type = NIL; }
  1952.                       pushSTACK(type);
  1953.                     }
  1954.                     # Stackaufbau: ...,
  1955.                     #   Datenvektor, Pathname, (last (pathname-directory Pathname)),
  1956.                     #   name, type.
  1957.                     # Kommt sofort ein '\', so war es ein Unterdirectory,
  1958.                     # sonst ist der Pathname beendet:
  1959.                     if ((z.count==0) || !slashp(TheSstring(STACK_4)->data[z.index])) break;
  1960.                     # Es kommt ein '\'. Character übergehen:
  1961.                     z.index++; z.FNindex = fixnum_inc(z.FNindex,1); z.count--;
  1962.                     # name=NIL -> durch "" ersetzen:
  1963.                     if (eq(STACK_1,NIL)) { STACK_1 = O(leer_string); }
  1964.                     # type=NIL -> durch "" ersetzen:
  1965.                     if (eq(STACK_0,NIL)) { STACK_0 = O(leer_string); }
  1966.                     { var reg1 object new_cons = allocate_cons(); # neues Cons
  1967.                       Cdr(new_cons) = popSTACK(); # type
  1968.                       Car(new_cons) = popSTACK(); # name
  1969.                       # new_cons = (cons name type)
  1970.                       pushSTACK(new_cons);
  1971.                     }
  1972.                     subdir_ok:
  1973.                   #endif
  1974.                   #ifdef PATHNAME_NOEXT
  1975.                     { var reg3 uintL z_start_index = z.index; # Index beim Start
  1976.                       loop
  1977.                         { var reg2 uintB ch;
  1978.                           if (z.count == 0) break;
  1979.                           ch = TheSstring(STACK_2)->data[z.index]; # nächstes Character
  1980.                           if (!legal_namechar(ch)) break; # gültiges Character ?
  1981.                           # ja -> Teil des Namens
  1982.                           # Character übergehen:
  1983.                           z.index++; z.FNindex = fixnum_inc(z.FNindex,1); z.count--;
  1984.                         }
  1985.                       # Ende des Namens erreicht.
  1986.                       # Name := Teilstring von STACK_2 von z_start_index (einschließlich)
  1987.                       #                                bis z.index (ausschließlich).
  1988.                      {var reg3 uintL len = z.index - z_start_index;
  1989.                       var reg4 object string = allocate_string(len); # String der Länge len
  1990.                       # füllen:
  1991.                       var reg1 uintB* ptr1 = &TheSstring(STACK_2)->data[z_start_index];
  1992.                       var reg2 uintB* ptr2 = &TheSstring(string)->data[0];
  1993.                       dotimesL(len,len, { *ptr2++ = *ptr1++; });
  1994.                       # Name fertig.
  1995.                       pushSTACK(string);
  1996.                     }}
  1997.                     # Kommt sofort ein '/' bzw. '\', so war es ein Unterdirectory,
  1998.                     # sonst ist der Pathname beendet:
  1999.                     if ((z.count==0) || !slashp(TheSstring(STACK_3)->data[z.index]))
  2000.                       # Nein -> war der Name und kein Subdir.
  2001.                       break;
  2002.                     # Es kommt ein '/' bzw. '\'. Character übergehen:
  2003.                     z.index++; z.FNindex = fixnum_inc(z.FNindex,1); z.count--;
  2004.                     # Stackaufbau: ...,
  2005.                     #   Datenvektor, Pathname, (last (pathname-directory Pathname)),
  2006.                     #   subdir.
  2007.                     #ifdef PATHNAME_AMIGAOS
  2008.                     # War es '' ?
  2009.                     if (equal(STACK_0,O(leer_string)))
  2010.                       { STACK_0 = S(Kparent); } # ja -> durch :PARENT ersetzen
  2011.                     else
  2012.                     #endif
  2013.                     # War es '**' oder '...' ?
  2014.                     if (equal(STACK_0,O(wildwild_string)) || equal(STACK_0,O(punktpunktpunkt_string)))
  2015.                       { STACK_0 = S(Kwild_inferiors); } # ja -> durch :WILD-INFERIORS ersetzen
  2016.                   #endif
  2017.                   # (pathname-directory pathname) um Subdir STACK_0 verlängern:
  2018.                   { var reg1 object new_cons = allocate_cons(); # neues Cons
  2019.                     Car(new_cons) = popSTACK(); # = (cons subdir NIL)
  2020.                     Cdr(STACK_0) = new_cons; # verlängert (pathname-directory Pathname)
  2021.                     STACK_0 = new_cons; # neues (last (pathname-directory Pathname))
  2022.                   }
  2023.                 }
  2024.               #else # defined(PATHNAME_RISCOS)
  2025.               pushSTACK(unbound); # maybe-name
  2026.               # Stackaufbau: ..., Datenvektor, Pathname, (last (pathname-directory Pathname)),
  2027.               #              maybe-name.
  2028.               loop
  2029.                 { # Versuche, ein weiteres Unterdirectory zu parsen.
  2030.                   # Maybe-Name = die letzte gelesene Komponente in
  2031.                   # { { legal-wild char }+ | empty } '.'  Syntax.
  2032.                   # Ob ein weiteres subdir oder der Name, wird sich erst noch
  2033.                   # entscheiden.
  2034.                   # Kommt '^.' ?
  2035.                   if (!nullp(STACK_0)
  2036.                       && (z.count >= 2)
  2037.                       && (TheSstring(STACK_3)->data[z.index] == '^')
  2038.                       && slashp(TheSstring(STACK_3)->data[z.index+1])
  2039.                      )
  2040.                     { # beide Characters übergehen:
  2041.                       z.index+=2; z.FNindex = fixnum_inc(z.FNindex,2); z.count-=2;
  2042.                       pushSTACK(S(Kparent)); # :PARENT
  2043.                     }
  2044.                     else
  2045.                     # Versuche, normale  { legal-wild char }+  Syntax zu parsen:
  2046.                     { var reg3 uintL z_start_index = z.index; # Index beim Start des Namens
  2047.                       loop
  2048.                         { var reg2 uintB ch;
  2049.                           if (z.count == 0) break;
  2050.                           ch = TheSstring(STACK_3)->data[z.index]; # nächstes Character
  2051.                           if (!legal_namechar(ch)) break; # gültiges Character ?
  2052.                           # ja -> Teil des Namens
  2053.                           # Character übergehen:
  2054.                           z.index++; z.FNindex = fixnum_inc(z.FNindex,1); z.count--;
  2055.                         }
  2056.                       # Ende des Namens erreicht.
  2057.                       # Name := Teilstring von STACK_3 von z_start_index (einschließlich)
  2058.                       #                                bis z.index (ausschließlich).
  2059.                      {var reg3 uintL len = z.index - z_start_index;
  2060.                       var reg4 object string;
  2061.                       if (len==0)
  2062.                         { string = NIL; } # "" wird zu NIL
  2063.                         else
  2064.                         { string = allocate_string(len); # String der Länge len
  2065.                           # füllen:
  2066.                          {var reg1 uintB* ptr1 = &TheSstring(STACK_3)->data[z_start_index];
  2067.                           var reg2 uintB* ptr2 = &TheSstring(string)->data[0];
  2068.                           dotimespL(len,len, { *ptr2++ = *ptr1++; });
  2069.                         }}
  2070.                       # Name fertig.
  2071.                       if (nullp(STACK_0)
  2072.                           || (z.count==0)
  2073.                           || !slashp(TheSstring(STACK_3)->data[z.index])
  2074.                          )
  2075.                         { pushSTACK(string); break; }
  2076.                       # Character '.' übergehen:
  2077.                       z.index++; z.FNindex = fixnum_inc(z.FNindex,1); z.count--;
  2078.                       pushSTACK(string);
  2079.                     }}
  2080.                   if (!eq(STACK_1,unbound))
  2081.                     # (pathname-directory pathname) um Subdir STACK_1 verlängern:
  2082.                     { var reg1 object new_cons = allocate_cons(); # neues Cons
  2083.                       Car(new_cons) = STACK_1; # = (cons subdir NIL)
  2084.                       Cdr(STACK_2) = new_cons; # verlängert (pathname-directory Pathname)
  2085.                       STACK_2 = new_cons; # neues (last (pathname-directory Pathname))
  2086.                     }
  2087.                   STACK_1 = STACK_0; skipSTACK(1); # maybe-name := subdir
  2088.                 }
  2089.               if (eq(STACK_1,unbound)) { STACK_1 = STACK_0; STACK_0 = NIL; }
  2090.               # Stackaufbau: ..., Datenvektor, Pathname, (last (pathname-directory Pathname)),
  2091.               #              name, type.
  2092.               # In gewissen Fällen hört die Directory-Angabe nicht nach dem
  2093.               # vorletzten Punkt, sondern nach dem letzten Punkt auf:
  2094.               elif (eq(STACK_1,S(Kparent)) # z.B. "bar.^.foo"
  2095.                     || (nullp(STACK_0) && !nullp(STACK_1)) # z.B. "foo.bar."
  2096.                    )
  2097.                 # (pathname-directory pathname) um Subdir STACK_1 verlängern:
  2098.                 { var reg1 object new_cons = allocate_cons(); # neues Cons
  2099.                   Car(new_cons) = STACK_1; # = (cons subdir NIL)
  2100.                   Cdr(STACK_2) = new_cons; # verlängert (pathname-directory Pathname)
  2101.                   STACK_2 = new_cons; # neues (last (pathname-directory Pathname))
  2102.                   STACK_1 = STACK_0; # name := type
  2103.                   STACK_0 = NIL;     # type := NIL
  2104.                 }
  2105.               #endif
  2106.               #if defined(PATHNAME_EXT83) || defined(PATHNAME_RISCOS)
  2107.               # Stackaufbau: ...,
  2108.               #   Datenvektor, Pathname, (last (pathname-directory Pathname)),
  2109.               #   name, type.
  2110.               # Name und Typ in Pathname eintragen:
  2111.               { var reg3 object type = popSTACK();
  2112.                 var reg2 object name = popSTACK();
  2113.                 skipSTACK(1); # Directory ist schon eingetragen
  2114.                {var reg1 object pathname = STACK_0;
  2115.                 ThePathname(pathname)->pathname_name = name;
  2116.                 ThePathname(pathname)->pathname_type = type;
  2117.               }}
  2118.               #endif
  2119.               #ifdef PATHNAME_NOEXT
  2120.               # Stackaufbau: ..., Datenvektor, Pathname, (last (pathname-directory Pathname)),
  2121.               #              string.
  2122.               split_name_type(sym_nullp(S(read_pathname_p))); # String STACK_0 in Name und Typ aufspalten
  2123.               after_name_type:
  2124.               # Stackaufbau: ..., Datenvektor, Pathname, (last (pathname-directory Pathname)),
  2125.               #              name, type.
  2126.               # Name und Typ in Pathname eintragen:
  2127.               { var reg3 object type = popSTACK();
  2128.                 var reg2 object name = popSTACK();
  2129.                 skipSTACK(1); # Directory ist schon eingetragen
  2130.                 # name="" durch Name=NIL ersetzen:
  2131.                 if (equal(name,O(leer_string))) { name = NIL; }
  2132.                {var reg1 object pathname = STACK_0;
  2133.                 ThePathname(pathname)->pathname_name = name;
  2134.                 ThePathname(pathname)->pathname_type = type;
  2135.               }}
  2136.               #endif
  2137.             }
  2138.             #undef slashp
  2139.           }
  2140.         # Pathname fertig.
  2141.         # Stackaufbau: ..., Datenvektor, Pathname.
  2142.         if (!junk_allowed)
  2143.           # Überprüfen, ob keine Zeichen mehr übrig sind:
  2144.           if (!(z.count == 0))
  2145.             { pushSTACK(z.FNindex); # letzter Index
  2146.               pushSTACK(STACK_(4+2+1)); # thing
  2147.               pushSTACK(S(parse_namestring));
  2148.               //: DEUTSCH "~: Syntax Error im Dateinamen ~ an Position ~."
  2149.               //: ENGLISH "~: syntax error in filename ~ at position ~"
  2150.               //: FRANCAIS "~ : Erreur de syntaxe dans le nom de fichier ~, à la position ~."
  2151.               fehler(error, GETTEXT("~: syntax error in filename ~ at position ~"));
  2152.             }
  2153.         #ifdef LOGICAL_PATHNAMES
  2154.         if (parse_logical)
  2155.           { if (!nullp(STACK_(3+2)))
  2156.               # Hosts müssen übereinstimmen, vgl. CLtL2 S. 629
  2157.               if (!equal(STACK_(3+2),TheLogpathname(STACK_0)->pathname_host))
  2158.                 { pushSTACK(STACK_0);
  2159.                   pushSTACK(TheLogpathname(STACK_(0+1))->pathname_host);
  2160.                   pushSTACK(STACK_(3+2+2));
  2161.                   pushSTACK(S(parse_namestring));
  2162.                   //: DEUTSCH "~: Hosts ~ und ~ von ~ stimmen nicht überein."
  2163.                   //: ENGLISH "~: hosts ~ and ~ of ~ should coincide"
  2164.                   //: FRANCAIS "~ : Les «hosts» ~ et ~ de ~ ne sont pas les mêmes."
  2165.                   fehler(error, GETTEXT("~: hosts ~ and ~ of ~ should coincide"));
  2166.           }     }
  2167.         #endif
  2168.         value1 = STACK_0; # Pathname als 1. Wert
  2169.         #ifdef PATHNAME_RISCOS
  2170.         if (as_oint(z.FNindex) >= as_oint(FNindex_limit))
  2171.           # FNindex von new_thing nach thing umrechnen:
  2172.           { value2 = fixnum_inc(z.FNindex,FNindex_offset); }
  2173.           else
  2174.           # FNindex zeigt in den ersetzten (!) String envval. Was bleibt
  2175.           # uns als Index anderes übrig als der Start-Index?
  2176.           # (Nicht ganz korrekt freilich: Hätte das Parsen wirklich dort
  2177.           # aufgehört, würde value1 anders aussehen!)
  2178.           # Zum Beispiel ein Index in das Innere des <...>-Konstruktes.
  2179.           # (Auch das ist nicht ganz korrekt, kommt der Sache aber näher.)
  2180.           { value2 = FNindex_fallback; }
  2181.         #else
  2182.         value2 = z.FNindex; # Index als 2. Wert
  2183.         #endif
  2184.         mv_count=2; # 2 Werte
  2185.         skipSTACK(5+2); return;
  2186.   } } }
  2187.  
  2188. # UP: Wandelt ein Objekt in einen Pathname um.
  2189. # coerce_xpathname(object)
  2190. # > object: Objekt
  2191. # < ergebnis: (PATHNAME Objekt)
  2192. # kann GC auslösen
  2193.   local object coerce_xpathname (object obj);
  2194.   local object coerce_xpathname(obj)
  2195.     var reg1 object obj;
  2196.     { if (xpathnamep(obj))
  2197.         # Bei Pathnames ist nichts zu tun.
  2198.         { return obj; }
  2199.         else
  2200.         # sonst: PARSE-NAMESTRING aufrufen:
  2201.         { pushSTACK(subr_self); # subr_self retten (für spätere Fehlermeldungen)
  2202.           pushSTACK(obj); funcall(L(parse_namestring),1);
  2203.           subr_self = popSTACK();
  2204.           return value1;
  2205.         }
  2206.     }
  2207.  
  2208. LISPFUNN(pathname,1)
  2209. # (PATHNAME pathname), CLTL S. 413
  2210.   { value1 = coerce_xpathname(popSTACK()); mv_count=1; }
  2211.  
  2212. LISPFUN(pathnamehost,1,0,norest,key,1, (kw(case)))
  2213. # (PATHNAME-HOST pathname), CLTL S. 417, CLtL2 S. 644
  2214.   { var reg1 object pathname = coerce_xpathname(STACK_1);
  2215.     #ifdef LOGICAL_PATHNAMES
  2216.     if (logpathnamep(pathname))
  2217.       { value1 = TheLogpathname(pathname)->pathname_host; mv_count=1; }
  2218.       else
  2219.     #endif
  2220.       {
  2221.         #if HAS_HOST
  2222.         var reg2 object erg = ThePathname(pathname)->pathname_host;
  2223.         value1 = (eq(STACK_0,S(Kcommon)) ? common_case(erg) : erg);
  2224.         mv_count=1; # host als Wert
  2225.         #else
  2226.         value1 = NIL; mv_count=1; # NIL als Wert
  2227.         #endif
  2228.       }
  2229.     skipSTACK(2);
  2230.   }
  2231.  
  2232. LISPFUN(pathnamedevice,1,0,norest,key,1, (kw(case)))
  2233. # (PATHNAME-DEVICE pathname), CLTL S. 417, CLtL2 S. 644
  2234.   { var reg1 object pathname = coerce_xpathname(STACK_1);
  2235.     #ifdef LOGICAL_PATHNAMES
  2236.     if (logpathnamep(pathname))
  2237.       { value1 = NIL; mv_count=1; }
  2238.       else
  2239.     #endif
  2240.       {
  2241.         #if HAS_DEVICE
  2242.         var reg2 object erg = ThePathname(pathname)->pathname_device;
  2243.         value1 = (eq(STACK_0,S(Kcommon)) ? common_case(erg) : erg);
  2244.         mv_count=1; # device als Wert
  2245.         #else
  2246.         value1 = NIL; mv_count=1; # NIL als Wert
  2247.         #endif
  2248.       }
  2249.     skipSTACK(2);
  2250.   }
  2251.  
  2252. LISPFUN(pathnamedirectory,1,0,norest,key,1, (kw(case)))
  2253. # (PATHNAME-DIRECTORY pathname), CLTL S. 417, CLtL2 S. 644
  2254.   { var reg1 object pathname = coerce_xpathname(STACK_1);
  2255.     #ifdef LOGICAL_PATHNAMES
  2256.     if (logpathnamep(pathname))
  2257.       { value1 = TheLogpathname(pathname)->pathname_directory; }
  2258.       else
  2259.     #endif
  2260.       { var reg2 object erg = ThePathname(pathname)->pathname_directory;
  2261.         value1 = (eq(STACK_0,S(Kcommon)) ? subst_common_case(erg) : erg);
  2262.       }
  2263.     mv_count=1; # directory als Wert
  2264.     skipSTACK(2);
  2265.   }
  2266.  
  2267. LISPFUN(pathnamename,1,0,norest,key,1, (kw(case)))
  2268. # (PATHNAME-NAME pathname), CLTL S. 417, CLtL2 S. 644
  2269.   { var reg1 object pathname = coerce_xpathname(STACK_1);
  2270.     #ifdef LOGICAL_PATHNAMES
  2271.     if (logpathnamep(pathname))
  2272.       { value1 = TheLogpathname(pathname)->pathname_name; }
  2273.       else
  2274.     #endif
  2275.       { var reg2 object erg = ThePathname(pathname)->pathname_name;
  2276.         value1 = (eq(STACK_0,S(Kcommon)) ? common_case(erg) : erg);
  2277.       }
  2278.     mv_count=1; # name als Wert
  2279.     skipSTACK(2);
  2280.   }
  2281.  
  2282. LISPFUN(pathnametype,1,0,norest,key,1, (kw(case)))
  2283. # (PATHNAME-TYPE pathname), CLTL S. 417, CLtL2 S. 644
  2284.   { var reg1 object pathname = coerce_xpathname(STACK_1);
  2285.     #ifdef LOGICAL_PATHNAMES
  2286.     if (logpathnamep(pathname))
  2287.       { value1 = TheLogpathname(pathname)->pathname_type; }
  2288.       else
  2289.     #endif
  2290.       { var reg2 object erg = ThePathname(pathname)->pathname_type;
  2291.         value1 = (eq(STACK_0,S(Kcommon)) ? common_case(erg) : erg);
  2292.       }
  2293.     mv_count=1; # type als Wert
  2294.     skipSTACK(2);
  2295.   }
  2296.  
  2297. LISPFUNN(pathnameversion,1)
  2298. # (PATHNAME-VERSION pathname), CLTL S. 417, CLtL2 S. 644
  2299.   { var reg1 object pathname = coerce_xpathname(popSTACK());
  2300.     #ifdef LOGICAL_PATHNAMES
  2301.     if (logpathnamep(pathname))
  2302.       { value1 = TheLogpathname(pathname)->pathname_version; }
  2303.       else
  2304.     #endif
  2305.       {
  2306.         #if HAS_VERSION
  2307.         value1 = ThePathname(pathname)->pathname_version; # version als Wert
  2308.         #else
  2309.         value1 = NIL; # NIL als Wert
  2310.         #endif
  2311.       }
  2312.     mv_count=1;
  2313.   }
  2314.  
  2315. # Zugriffsfunktionen ohne Groß-/Klein-Umwandlung:
  2316. # xpathname_host(logical,pathname)
  2317. # xpathname_device(logical,pathname)
  2318. # xpathname_directory(logical,pathname)
  2319. # xpathname_name(logical,pathname)
  2320. # xpathname_type(logical,pathname)
  2321. # xpathname_version(logical,pathname)
  2322. # > pathname: Pathname oder Logical Pathname
  2323. # > logical: Flag, ob es sich um einen Logical Pathname handelt
  2324. # < ergebnis: Wert der entsprechenden Komponente von pathname
  2325.   #ifdef LOGICAL_PATHNAMES
  2326.     #if HAS_HOST
  2327.       #define xpathname_host(logical,pathname)  \
  2328.         (logical ? TheLogpathname(pathname)->pathname_host : ThePathname(pathname)->pathname_host)
  2329.     #else
  2330.       #define xpathname_host(logical,pathname)  \
  2331.         (logical ? TheLogpathname(pathname)->pathname_host : NIL)
  2332.     #endif
  2333.     #if HAS_DEVICE
  2334.       #define xpathname_device(logical,pathname)  \
  2335.         (logical ? NIL : ThePathname(pathname)->pathname_device)
  2336.     #else
  2337.       #define xpathname_device(logical,pathname)  NIL
  2338.     #endif
  2339.     #define xpathname_directory(logical,pathname)  \
  2340.       (logical ? TheLogpathname(pathname)->pathname_directory : ThePathname(pathname)->pathname_directory)
  2341.     #define xpathname_name(logical,pathname)  \
  2342.       (logical ? TheLogpathname(pathname)->pathname_name : ThePathname(pathname)->pathname_name)
  2343.     #define xpathname_type(logical,pathname)  \
  2344.       (logical ? TheLogpathname(pathname)->pathname_type : ThePathname(pathname)->pathname_type)
  2345.     #if HAS_VERSION
  2346.       #define xpathname_version(logical,pathname)  \
  2347.         (logical ? TheLogpathname(pathname)->pathname_version : ThePathname(pathname)->pathname_version)
  2348.     #else
  2349.       #define xpathname_version(logical,pathname)  \
  2350.         (logical ? TheLogpathname(pathname)->pathname_version : NIL)
  2351.     #endif
  2352.   #else
  2353.     # logical immer =FALSE
  2354.     #if HAS_HOST
  2355.       #define xpathname_host(logical,pathname)  ThePathname(pathname)->pathname_host
  2356.     #else
  2357.       #define xpathname_host(logical,pathname)  NIL
  2358.     #endif
  2359.     #if HAS_DEVICE
  2360.       #define xpathname_device(logical,pathname)  ThePathname(pathname)->pathname_device
  2361.     #else
  2362.       #define xpathname_device(logical,pathname)  NIL
  2363.     #endif
  2364.     #define xpathname_directory(logical,pathname)  ThePathname(pathname)->pathname_directory
  2365.     #define xpathname_name(logical,pathname)  ThePathname(pathname)->pathname_name
  2366.     #define xpathname_type(logical,pathname)  ThePathname(pathname)->pathname_type
  2367.     #if HAS_VERSION
  2368.       #define xpathname_version(logical,pathname)  ThePathname(pathname)->pathname_version
  2369.     #else
  2370.       #define xpathname_version(logical,pathname)  NIL
  2371.     #endif
  2372.   #endif
  2373.  
  2374. #ifdef LOGICAL_PATHNAMES
  2375.  
  2376. LISPFUNN(logical_pathname,1)
  2377. # (LOGICAL-PATHNAME thing), CLtL2 S. 631
  2378.   { var reg1 object thing = popSTACK();
  2379.     if (logpathnamep(thing))
  2380.       # Bei Logical Pathnames ist nichts zu tun.
  2381.       { value1 = thing; mv_count=1; }
  2382.     elif (pathnamep(thing))
  2383.       # Normale Pathnames können nicht in Logical Pathnames umgewandelt werden.
  2384.       { pushSTACK(thing); # Wert für Slot DATUM von TYPE-ERROR
  2385.         pushSTACK(O(type_logical_pathname)); # Wert für Slot EXPECTED-TYPE von TYPE-ERROR
  2386.         pushSTACK(thing);
  2387.         pushSTACK(S(logical_pathname));
  2388.         //: DEUTSCH "~: Argument ~ ist kein Logical Pathname, String, Stream oder Symbol."
  2389.         //: ENGLISH "~: argument ~ is not a logical pathname, string, stream or symbol"
  2390.         //: FRANCAIS "~ : L'argument ~ n'est pas un «pathname logique», une chaîne, un «stream» ou un symbole."
  2391.         fehler(type_error, GETTEXT("~: argument ~ is not a logical pathname, string, stream or symbol"));
  2392.       }
  2393.     else
  2394.       # sonst: PARSE-NAMESTRING aufrufen:
  2395.       { pushSTACK(subr_self); # subr_self retten (für spätere Fehlermeldungen)
  2396.         # Das Ergebnis von (PARSE-NAMESTRING thing nil empty-logical-pathname)
  2397.         # ist garantiert ein logischer Pathname.
  2398.         pushSTACK(thing); pushSTACK(NIL); pushSTACK(O(empty_logical_pathname));
  2399.         funcall(L(parse_namestring),3);
  2400.         subr_self = popSTACK();
  2401.         mv_count=1;
  2402.       }
  2403.   }
  2404.  
  2405. LISPFUN(translate_logical_pathname,1,0,norest,key,0,_EMA_)
  2406. # (TRANSLATE-LOGICAL-PATHNAME pathname &key), CLtL2 S. 631
  2407.   { var reg3 object pathname;
  2408.     funcall(L(logical_pathname),1);
  2409.     pathname = value1;
  2410.     if (logpathnamep(pathname))
  2411.       { # Umwandeln eines logischen in einen normalen Pathname:
  2412.         # (let ((ht (make-hash-table :test #'equal)))
  2413.         #   (loop
  2414.         #     (when (gethash pathname ht) (error "Translation loop"))
  2415.         #     (setf (gethash pathname ht) t)
  2416.         #     (let ((host (or (pathname-host pathname) "SYS")))
  2417.         #       (unless (logical-host-p host) (error "No translation for host"))
  2418.         #       (let* ((translations (gethash host sys::*logical-pathname-translations*))
  2419.         #              (translation (assoc pathname translations :test #'pathname-match-p)))
  2420.         #         (unless (and translation (consp translation) (consp (cdr translation)))
  2421.         #           (error "No translation for pathname")
  2422.         #         )
  2423.         #         (setq pathname (translate-pathname pathname (first translation) (second translation)))
  2424.         #     ) )
  2425.         #     (unless (sys::logical-pathname-p pathname) (return))
  2426.         #   )
  2427.         #   pathname
  2428.         # )
  2429.         pushSTACK(pathname);
  2430.         pushSTACK(S(Ktest)); pushSTACK(L(equal)); funcall(L(make_hash_table),2);
  2431.         pushSTACK(value1);
  2432.         # Stackaufbau: pathname, ht.
  2433.         loop
  2434.           { if (!nullp(shifthash(STACK_0,STACK_1,T)))
  2435.               { # STACK_1 = pathname; # Wert für Slot PATHNAME von FILE-ERROR
  2436.                 STACK_0 = STACK_1;
  2437.                 pushSTACK(S(translate_logical_pathname));
  2438.                 //: DEUTSCH "~: Endlosschleife beim Auflösen von ~"
  2439.                 //: ENGLISH "~: endless loop while resolving ~"
  2440.                 //: FRANCAIS "~ : boucle infinie pour ~"
  2441.                 fehler(file_error, GETTEXT("~: endless loop while resolving ~"));
  2442.               }
  2443.             if (nullp(TheLogpathname(STACK_1)->pathname_host))
  2444.               # Host NIL durch Default-Host ersetzen:
  2445.               { var reg1 object new = allocate_logpathname();
  2446.                 var reg2 object old = STACK_1;
  2447.                 TheLogpathname(new)->pathname_host      = O(default_logical_pathname_host); # Default "SYS"
  2448.                 TheLogpathname(new)->pathname_directory = TheLogpathname(old)->pathname_directory;
  2449.                 TheLogpathname(new)->pathname_name      = TheLogpathname(old)->pathname_name;
  2450.                 TheLogpathname(new)->pathname_type      = TheLogpathname(old)->pathname_type;
  2451.                 TheLogpathname(new)->pathname_version   = TheLogpathname(old)->pathname_version;
  2452.                 STACK_1 = new;
  2453.               }
  2454.            {var reg2 object host = TheLogpathname(STACK_1)->pathname_host;
  2455.             var reg1 object translations = gethash(host,Symbol_value(S(logpathname_translations)));
  2456.             if (eq(translations,nullobj))
  2457.               { # STACK_1 = pathname; # Wert für Slot PATHNAME von FILE-ERROR
  2458.                 STACK_0 = STACK_1;
  2459.                 pushSTACK(host);
  2460.                 pushSTACK(S(translate_logical_pathname));
  2461.                 //: DEUTSCH "~: Logical Host ~ ist unbekannt: ~"
  2462.                 //: ENGLISH "~: unknown logical host ~ in ~"
  2463.                 //: FRANCAIS "~ : host ~ inconnu dans ~"
  2464.                 fehler(file_error, GETTEXT("~: unknown logical host ~ in ~"));
  2465.               }
  2466.             # (ASSOC pathname translations :test #'pathname-match-p):
  2467.             pushSTACK(STACK_1); pushSTACK(translations);
  2468.             pushSTACK(S(Ktest)); pushSTACK(L(pathname_match_p));
  2469.             funcall(L(assoc),4);
  2470.             if (atomp(value1) || matomp(Cdr(value1)))
  2471.               { # STACK_1 = pathname; # Wert für Slot PATHNAME von FILE-ERROR
  2472.                 STACK_0 = STACK_1;
  2473.                 pushSTACK(S(translate_logical_pathname));
  2474.                 //: DEUTSCH "~: Keine Ersetzungsregel für ~ ist bekannt."
  2475.                 //: ENGLISH "~: No replacement rule for ~ is known."
  2476.                 //: FRANCAIS "~ : Aucune règle de traduction est connue pour ~."
  2477.                 fehler(file_error, GETTEXT("~: No replacement rule for ~ is known."));
  2478.               }
  2479.             # (TRANSLATE-PATHNAME pathname (first rule) (second rule) :MERGE NIL):
  2480.             pushSTACK(STACK_1); pushSTACK(Car(value1)); pushSTACK(Car(Cdr(value1)));
  2481.             pushSTACK(S(Kmerge)); pushSTACK(NIL);
  2482.             funcall(L(translate_pathname),5);
  2483.             STACK_1 = pathname = value1;
  2484.             if (!logpathnamep(pathname)) break;
  2485.           }}
  2486.         skipSTACK(2);
  2487.       }
  2488.     value1 = pathname; mv_count=1;
  2489.   }
  2490.  
  2491. # UP: Wandelt ein Objekt in einen nicht-Logical Pathname um.
  2492. # coerce_pathname(object)
  2493. # > object: Objekt
  2494. # < ergebnis: (TRANSLATE-LOGICAL-PATHNAME (PATHNAME Objekt))
  2495. # kann GC auslösen
  2496.   local object coerce_pathname (object obj);
  2497.   local object coerce_pathname(obj)
  2498.     var reg1 object obj;
  2499.     { obj = coerce_xpathname(obj);
  2500.       if (pathnamep(obj))
  2501.         # Bei Pathnames ist nichts zu tun.
  2502.         { return obj; }
  2503.       elif (logpathnamep(obj))
  2504.         # TRANSLATE-LOGICAL-PATHNAME aufrufen:
  2505.         { pushSTACK(subr_self); # subr_self retten (für spätere Fehlermeldungen)
  2506.           pushSTACK(obj); funcall(L(translate_logical_pathname),1);
  2507.           subr_self = popSTACK();
  2508.           return value1;
  2509.         }
  2510.       else
  2511.         { NOTREACHED }
  2512.     }
  2513.  
  2514. #endif
  2515.  
  2516. # UP: Legt Teilstrings für STRING_CONCAT auf den STACK, die zusammen den
  2517. # String für ein Subdirectory (car path) ergeben.
  2518. # subdir_namestring_parts(path)
  2519. # > path: ein Cons
  2520. # < ergebnis: Anzahl der auf den Stack gelegten Strings
  2521. # verändert STACK
  2522.   local uintC subdir_namestring_parts (object path);
  2523.   local uintC subdir_namestring_parts(path)
  2524.     var reg4 object path;
  2525.     { var reg1 object subdir = Car(path);
  2526.       #if defined(PATHNAME_MSDOS)
  2527.       if (eq(subdir,S(Kcurrent))) # :CURRENT ?
  2528.         { pushSTACK(O(punkt_string)); return 1; }
  2529.       elif (eq(subdir,S(Kparent))) # :PARENT ?
  2530.         { pushSTACK(O(punktpunkt_string)); return 1; }
  2531.       elif (eq(subdir,S(Kwild_inferiors))) # :WILD-INFERIORS ?
  2532.         { pushSTACK(O(punktpunktpunkt_string)); return 1; }
  2533.       else
  2534.         # normales subdir (name . type)
  2535.         { var reg3 object name = Car(subdir);
  2536.           var reg2 object type = Cdr(subdir);
  2537.           # name = :WILD -> String "*"
  2538.           if (eq(name,S(Kwild))) { name = O(wild_string); }
  2539.           pushSTACK(name);
  2540.           # type = :WILD -> String "*"
  2541.           if (eq(type,S(Kwild))) { type = O(wild_string); }
  2542.           if (TheSstring(type)->length == 0)
  2543.             # type = "" -> nicht auszugeben
  2544.             { return 1+0; }
  2545.             else
  2546.             { pushSTACK(O(punkt_string)); # "."
  2547.               pushSTACK(type);
  2548.               return 1+2;
  2549.             }
  2550.         }
  2551.       #endif
  2552.       #ifdef PATHNAME_AMIGAOS
  2553.       if (eq(subdir,S(Kparent))) # :PARENT ?
  2554.         { return 0; } # Leerstring
  2555.       elif (eq(subdir,S(Kwild_inferiors))) # :WILD-INFERIORS ?
  2556.         { pushSTACK(O(wildwild_string)); return 1; }
  2557.       else
  2558.         # normales subdir
  2559.         { pushSTACK(subdir); return 1; }
  2560.       #endif
  2561.       #if defined(PATHNAME_UNIX) || defined(PATHNAME_OS2)
  2562.       if (eq(subdir,S(Kwild_inferiors))) # :WILD-INFERIORS ?
  2563.         { pushSTACK(O(wildwild_string)); return 1; }
  2564.         else
  2565.         # normales subdir
  2566.         { pushSTACK(subdir); return 1; }
  2567.       #endif
  2568.       #ifdef PATHNAME_RISCOS
  2569.       if (eq(subdir,S(Kparent))) # :PARENT ?
  2570.         { pushSTACK(O(parent_string)); return 1; }
  2571.         else
  2572.         # normales subdir
  2573.         { pushSTACK(subdir); return 1; }
  2574.       #endif
  2575.     }
  2576.  
  2577. # UP: Legt Teilstrings für STRING_CONCAT auf den STACK, die zusammen den
  2578. # String für den Host des Pathname pathname ergeben.
  2579. # host_namestring_parts(pathname)
  2580. # > pathname: nicht-Logical Pathname
  2581. # < ergebnis: Anzahl der auf den Stack gelegten Strings
  2582. # verändert STACK
  2583. #if HAS_HOST
  2584.   local uintC host_namestring_parts (object pathname);
  2585.   local uintC host_namestring_parts(pathname)
  2586.     var reg1 object pathname;
  2587.     { var reg2 object host = ThePathname(pathname)->pathname_host;
  2588.       if (nullp(host))
  2589.         { return 0; } # kein String
  2590.         else
  2591.         { pushSTACK(host);
  2592.           pushSTACK(O(doppelpunkt_string)); # ":"
  2593.           return 2;
  2594.     }   }
  2595. #else
  2596.   #define host_namestring_parts(pathname)  (unused (pathname), 0)  # keine Strings
  2597. #endif
  2598.  
  2599. # UP: Legt Teilstrings für STRING_CONCAT auf den STACK, die zusammen den
  2600. # String fürs Device und Directory des Pathname pathname ergeben.
  2601. # directory_namestring_parts(pathname)
  2602. # > pathname: nicht-Logical Pathname
  2603. # < ergebnis: Anzahl der auf den Stack gelegten Strings
  2604. # verändert STACK
  2605.   local uintC directory_namestring_parts (object pathname);
  2606.   local uintC directory_namestring_parts(pathname)
  2607.     var reg4 object pathname;
  2608.     { var reg3 uintC stringcount = 0; # bisherige Stringzahl = 0
  2609.       #if defined(PATHNAME_MSDOS) || defined(PATHNAME_OS2)
  2610.       # Device:
  2611.       { var reg1 object device = ThePathname(pathname)->pathname_device;
  2612.         if (!(nullp(device))) # NIL -> kein String
  2613.           { if (eq(device,S(Kwild))) { device = O(wild_string); } # :WILD -> String "*"
  2614.             pushSTACK(device); # Device auf den Stack
  2615.             stringcount++; # und mitzählen
  2616.       }   }
  2617.       #endif
  2618.       #ifdef PATHNAME_AMIGAOS
  2619.       # Device:
  2620.       { var reg1 object device = ThePathname(pathname)->pathname_device;
  2621.         if (!(nullp(device))) # NIL -> kein String
  2622.           { pushSTACK(device); # Device auf den Stack
  2623.             stringcount += 1; # und mitzählen
  2624.             # Wegen :ABSOLUTE kommt gleich danach ein ":" auf den Stack.
  2625.       }   }
  2626.       #endif
  2627.       #ifdef PATHNAME_RISCOS
  2628.       # Device:
  2629.       { var reg1 object device = ThePathname(pathname)->pathname_device;
  2630.         if (!(nullp(device))) # NIL -> kein String
  2631.           { pushSTACK(O(doppelpunkt_string)); # ":"
  2632.             pushSTACK(device); # Device auf den Stack
  2633.             pushSTACK(O(punkt_string)); # "."
  2634.             stringcount += 3; # und mitzählen
  2635.       }   }
  2636.       #endif
  2637.       # Directory:
  2638.       { var reg2 object directory = ThePathname(pathname)->pathname_directory;
  2639.         #if defined(PATHNAME_MSDOS) || defined(PATHNAME_OS2)
  2640.         # evtl. Doppelpunkt:
  2641.         if (!(stringcount == 0)) # nur falls schon was auf dem Stack
  2642.           { pushSTACK(O(doppelpunkt_string)); stringcount++; } # ":" auf den Stack
  2643.         #endif
  2644.         # Ist das erste subdir = :ABSOLUTE oder = :RELATIVE ?
  2645.         if (eq(Car(directory),S(Kabsolute)))
  2646.           #if defined(PATHNAME_MSDOS) || defined(PATHNAME_OS2)
  2647.           { pushSTACK(O(backslash_string)); stringcount++; } # "\\" auf den Stack
  2648.           #endif
  2649.           #ifdef PATHNAME_AMIGAOS
  2650.           { pushSTACK(O(doppelpunkt_string)); stringcount++; } # ":" auf den Stack
  2651.           #endif
  2652.           #ifdef PATHNAME_UNIX
  2653.           { pushSTACK(O(slash_string)); stringcount++; } # "/" auf den Stack
  2654.           #endif
  2655.           #ifdef PATHNAME_RISCOS
  2656.           { directory = Cdr(directory); # übergehen
  2657.            {var reg1 object firstdir = Car(directory);
  2658.             if (eq(firstdir,S(Kroot)))
  2659.               { pushSTACK(O(root_string)); stringcount++; } # "$." auf den Stack
  2660.             elif (eq(firstdir,S(Khome)))
  2661.               { pushSTACK(O(home_string)); stringcount++; } # "&." auf den Stack
  2662.             elif (eq(firstdir,S(Kcurrent)))
  2663.               { pushSTACK(O(current_string)); stringcount++; } # "@." auf den Stack
  2664.             elif (eq(firstdir,S(Klibrary)))
  2665.               { pushSTACK(O(library_string)); stringcount++; } # "%." auf den Stack
  2666.             elif (eq(firstdir,S(Kprevious)))
  2667.               { pushSTACK(O(previous_string)); stringcount++; } # "\\." auf den Stack
  2668.             else
  2669.               { NOTREACHED }
  2670.           }}
  2671.           #endif
  2672.         directory = Cdr(directory); # übergehen
  2673.         # weitere subdirs auf den Stack:
  2674.         while (consp(directory))
  2675.           { stringcount += subdir_namestring_parts(directory);
  2676.             #if defined(PATHNAME_MSDOS) || defined(PATHNAME_OS2)
  2677.             pushSTACK(O(backslash_string)); stringcount++; # "\\" auf den Stack
  2678.             #endif
  2679.             #if defined(PATHNAME_UNIX) || defined(PATHNAME_AMIGAOS)
  2680.             pushSTACK(O(slash_string)); stringcount++; # "/" auf den Stack
  2681.             #endif
  2682.             #ifdef PATHNAME_RISCOS
  2683.             pushSTACK(O(punkt_string)); stringcount++; # "." auf den Stack
  2684.             #endif
  2685.             directory = Cdr(directory);
  2686.           }
  2687.       }
  2688.       return stringcount;
  2689.     }
  2690.  
  2691. # UP: Legt Teilstrings für STRING_CONCAT auf den STACK, die zusammen den
  2692. # String für Name und Typ des Pathname ergeben.
  2693. # nametype_namestring_parts(name,type,version)
  2694. # > name, type, evtl. version: Komponenten des Pathname
  2695. # < ergebnis: Anzahl der auf den Stack gelegten Strings
  2696. # kann GC auslösen
  2697. # verändert STACK
  2698.   #if HAS_VERSION
  2699.   local uintC nametype_namestring_parts (object name, object type, object version);
  2700.   local uintC nametype_namestring_parts PARM3(name,type,version,
  2701.     var reg2 object name,
  2702.     var reg1 object type,
  2703.     var reg4 object version)
  2704.   #else
  2705.   local uintC nametype_namestring_parts_ (object name, object type);
  2706.   local uintC nametype_namestring_parts_ PARM2(name,type,
  2707.     var reg2 object name,
  2708.     var reg1 object type)
  2709.   #define nametype_namestring_parts(n,t,v)  nametype_namestring_parts_(n,t)
  2710.   #endif
  2711.     { var reg3 uintC stringcount = 0;
  2712.       # Name:
  2713.       if (!nullp(name)) # name=NIL -> nicht ausgeben
  2714.         {
  2715.           #if defined(PATHNAME_MSDOS)
  2716.           if (eq(name,S(Kwild))) { name = O(wild_string); } # :WILD -> String "*"
  2717.           #endif
  2718.           pushSTACK(name); # Name auf den Stack
  2719.           stringcount++; # und mitzählen
  2720.         }
  2721.       # Typ:
  2722.       if (!nullp(type)) # type=NIL -> nicht ausgeben
  2723.         { pushSTACK(O(punkt_string)); # "." auf den Stack
  2724.           stringcount++; # und mitzählen
  2725.           #if defined(PATHNAME_MSDOS)
  2726.           if (eq(type,S(Kwild))) { type = O(wild_string); } # :WILD -> String "*"
  2727.           #endif
  2728.           pushSTACK(type); # Typ auf den Stack
  2729.           stringcount++; # und mitzählen
  2730.         }
  2731.       #if HAS_VERSION
  2732.       if (!nullp(version)) # version=NIL -> nicht ausgeben
  2733.         { pushSTACK(O(strichpunkt_string)); # ";" auf den Stack
  2734.           stringcount++; # und mitzählen
  2735.           if (eq(version,S(Knewest)))
  2736.             { pushSTACK(O(zero_string)); } # :NEWEST -> String "0"
  2737.             else
  2738.           # Version (Integer >0) in String umwandeln: (sys::decimal-string version)
  2739.           { pushSTACK(version);
  2740.             C_decimal_string(); # == funcall(L(decimal_string),1);
  2741.             pushSTACK(value1);
  2742.           }
  2743.           stringcount++; # und mitzählen
  2744.         }
  2745.       #endif
  2746.       return stringcount;
  2747.     }
  2748.  
  2749. # UP: Legt Teilstrings für STRING_CONCAT auf den STACK, die zusammen den
  2750. # String für Name und Typ des Pathname ergeben.
  2751. # file_namestring_parts(pathname)
  2752. # > pathname: nicht-Logical Pathname
  2753. # < ergebnis: Anzahl der auf den Stack gelegten Strings
  2754. # kann GC auslösen
  2755. # verändert STACK
  2756.   local uintC file_namestring_parts (object pathname);
  2757.   local uintC file_namestring_parts(pathname)
  2758.     var reg1 object pathname;
  2759.     { return nametype_namestring_parts(ThePathname(pathname)->pathname_name,
  2760.                                        ThePathname(pathname)->pathname_type,
  2761.                                        ThePathname(pathname)->pathname_version);
  2762.     }
  2763.  
  2764. # UP: Wandelt Pathname in String um.
  2765. # whole_namestring(pathname)
  2766. # > pathname: nicht-Logical Pathname
  2767. # < ergebnis: Simple-String
  2768. # kann GC auslösen
  2769.   local object whole_namestring (object pathname);
  2770.   local object whole_namestring(pathname)
  2771.     var reg1 object pathname;
  2772.     { var reg2 uintC stringcount;
  2773.       stringcount = host_namestring_parts(pathname); # Strings für den Host
  2774.       stringcount += directory_namestring_parts(pathname); # Strings fürs Directory
  2775.       stringcount += file_namestring_parts(pathname); # Strings für den Filename
  2776.       subr_self = L(namestring); # ("aktuelles" SUBR für Fehlermeldung)
  2777.       return string_concat(stringcount); # zusammenhängen
  2778.     }
  2779.  
  2780. LISPFUNN(file_namestring,1)
  2781. # (FILE-NAMESTRING pathname), CLTL S. 417
  2782.   { var reg1 object pathname = coerce_pathname(popSTACK());
  2783.     var reg2 uintC stringcount = file_namestring_parts(pathname); # Strings für den Filename
  2784.     value1 = string_concat(stringcount); mv_count=1; # zusammenhängen
  2785.   }
  2786.  
  2787. # UP: Liefert den String zum Directory eines Pathname.
  2788. # directory_namestring(pathname)
  2789. # > pathname: nicht-Logical Pathname
  2790. # > subr_self: Aufrufer (ein SUBR)
  2791. # < ergebnis: Simple-String
  2792. # kann GC auslösen
  2793.   local object directory_namestring (object pathname);
  2794.   local object directory_namestring(pathname)
  2795.     var reg1 object pathname;
  2796.     { var reg2 uintC stringcount =
  2797.         directory_namestring_parts(pathname); # Strings fürs Directory
  2798.       return string_concat(stringcount); # zusammenhängen
  2799.     }
  2800.  
  2801. LISPFUNN(directory_namestring,1)
  2802. # (DIRECTORY-NAMESTRING pathname), CLTL S. 417
  2803.   { var reg1 object pathname = coerce_pathname(popSTACK());
  2804.     value1 = directory_namestring(pathname); mv_count=1;
  2805.   }
  2806.  
  2807. LISPFUNN(host_namestring,1)
  2808. # (HOST-NAMESTRING pathname), CLTL S. 417
  2809.   { var reg1 object pathname = coerce_pathname(popSTACK());
  2810.     #if HAS_HOST
  2811.     var reg2 uintC stringcount = host_namestring_parts(pathname); # Strings für den Host
  2812.     value1 = string_concat(stringcount); # zusammenhängen
  2813.     #else
  2814.     value1 = O(leer_string); # "" als Wert
  2815.     #endif
  2816.     mv_count=1;
  2817.   }
  2818.  
  2819. #if HAS_VERSION || defined(LOGICAL_PATHNAMES)
  2820. # UP: Überprüft ein optionales VERSION-Argument.
  2821. # test_optional_version(def);
  2822. # > STACK_0: VERSION-Argument
  2823. # > def: Defaultwert dafür
  2824. # > subr_self: Aufrufer (ein SUBR)
  2825. # < ergebnis: gültige Version-Komponente
  2826.   local object test_optional_version (object def);
  2827.   local object test_optional_version(def)
  2828.     var reg2 object def;
  2829.     { var reg1 object version = STACK_0;
  2830.       if (eq(version,unbound)) { return def; } # nicht angegeben -> Default
  2831.       elif (nullp(version)) {} # NIL ist OK
  2832.       elif (eq(version,S(Kwild))) {} # :WILD ist OK
  2833.       elif (eq(version,S(Knewest))) {} # :NEWEST ist OK
  2834.       elif (posfixnump(version) && !eq(version,Fixnum_0)) {} # Fixnum >0 ist OK
  2835.       elif (pathnamep(version)) # Pathname -> dessen Version
  2836.         { STACK_0 = xpathname_version(FALSE,version); }
  2837.       #ifdef LOGICAL_PATHNAMES
  2838.       elif (logpathnamep(version)) # Logical Pathname -> dessen Version
  2839.         { STACK_0 = TheLogpathname(version)->pathname_version; }
  2840.       #endif
  2841.       else # Keiner der gewünschten Fälle -> Fehler:
  2842.         { pushSTACK(version); # Wert für Slot DATUM von TYPE-ERROR
  2843.           pushSTACK(O(type_version)); # Wert für Slot EXPECTED-TYPE von TYPE-ERROR
  2844.           pushSTACK(version);
  2845.           pushSTACK(TheSubr(subr_self)->name);
  2846.           //: DEUTSCH "~: :VERSION-Argument muß NIL oder ein Fixnum >0 oder :WILD oder :NEWEST sein, nicht ~"
  2847.           //: ENGLISH "~: :VERSION-argument should be NIL or a positive fixnum or :WILD or :NEWEST, not ~"
  2848.           //: FRANCAIS "~ : L'argument pour :VERSION doit être NIL, un petit nombre entier positif, :WILD ou :NEWEST mais non ~"
  2849.           fehler(type_error, GETTEXT("~: :VERSION-argument should be NIL or a positive fixnum or :WILD or :NEWEST, not ~"));
  2850.         }
  2851.       return version;
  2852.     }
  2853. #else
  2854. # UP: Überprüft ein optionales VERSION-Argument.
  2855. # test_optional_version();
  2856. # > STACK_0: VERSION-Argument
  2857. # > subr_self: Aufrufer (ein SUBR)
  2858.   #define test_optional_version(def)  test_optional_version_()
  2859.   local void test_optional_version_ (void);
  2860.   local void test_optional_version_()
  2861.     { var reg1 object version = STACK_0;
  2862.       if (eq(version,unbound) # nicht angegeben?
  2863.           || nullp(version)         # oder NIL ?
  2864.           || eq(version,S(Kwild))   # oder :WILD ?
  2865.           || eq(version,S(Knewest)) # oder :NEWEST ?
  2866.          )
  2867.         { return; } # ja -> OK
  2868.         else
  2869.         { pushSTACK(version); # Wert für Slot DATUM von TYPE-ERROR
  2870.           pushSTACK(O(type_version)); # Wert für Slot EXPECTED-TYPE von TYPE-ERROR
  2871.           pushSTACK(version);
  2872.           pushSTACK(TheSubr(subr_self)->name);
  2873.           //: DEUTSCH "~: :VERSION-Argument muß NIL oder :WILD oder :NEWEST sein, nicht ~"
  2874.           //: ENGLISH "~: :VERSION-argument should be NIL or :WILD or :NEWEST, not ~"
  2875.           //: FRANCAIS "~ : L'argument pour :VERSION doit être NIL, :WILD ou :NEWEST mais non ~"
  2876.           fehler(type_error, GETTEXT("~: :VERSION-argument should be NIL or :WILD or :NEWEST, not ~"));
  2877.     }   }
  2878. #endif
  2879.  
  2880. #if defined(PATHNAME_MSDOS) || defined(PATHNAME_OS2)
  2881.  
  2882. # Das Betriebssystem verwaltet ein Default-Drive.
  2883. # Das Betriebssystem verwaltet auf jedem Drive ein Default-Directory. Dieses
  2884. # kann sich allerdings ändern, wenn eine andere Diskette eingelegt wird.
  2885.  
  2886. # Es wird ein Default-Drive geführt: DEFAULT_DRIVE = O(default_drive).
  2887.  
  2888. # Die Variable *DEFAULT-PATHNAME-DEFAULTS* enthält (als Pathname) den
  2889. # Defaultwert für jede MERGE-Operation. Dies ist derjenige, den das System
  2890. # in vom Benutzer eingegebene Pathnames "hineininterpretiert".
  2891. # Er wird auf dem neuesten Stand des DEFAULT_DRIVE gehalten: bei der
  2892. # Initialisierung das aktuelle Device (im Sinne von DOS), bei der
  2893. # Änderung von DEFAULT_DRIVE mittels CD.
  2894.  
  2895. #endif # PATHNAME_MSDOS || PATHNAME_OS2
  2896.  
  2897. #if defined(PATHNAME_UNIX) || defined(PATHNAME_AMIGAOS)
  2898.  
  2899. # Die Variable *DEFAULT-PATHNAME-DEFAULTS* enthält (als Pathname) den
  2900. # Defaultwert für jede MERGE-Operation. Dies ist derjenige, den das System
  2901. # in vom Benutzer eingegebene Pathnames "hineininterpretiert".
  2902.  
  2903. #endif
  2904.  
  2905. #if defined(UNIX) || defined(WIN32_UNIX)
  2906.  
  2907. # Das Betriebssystem verwaltet ein Default-Directory ("working directory")
  2908. # für diesen Prozeß. Es kann mit chdir verändert und mit getwd abgefragt
  2909. # werden. Siehe CHDIR(2) und GETWD(3).
  2910.  
  2911. #endif
  2912.  
  2913. #ifdef AMIGAOS
  2914.  
  2915. # Das Betriebssystem verwaltet ein Default-Directory ("current directory")
  2916. # für diesen Prozeß. Es kann mit CurrentDir verändert und mit einer
  2917. # Kombination aus Examine und ParentDir abgefragt werden.
  2918.  
  2919. #endif
  2920.  
  2921. # UP: Neuberechnung von *DEFAULT-PATHNAME-DEFAULTS*
  2922. #if defined(PATHNAME_MSDOS) || defined(PATHNAME_OS2)
  2923. # aus DEFAULT_DRIVE
  2924. #endif
  2925. # recalc_defaults_pathname();
  2926. # < ergebnis: Wert von *DEFAULT-PATHNAME-DEFAULTS*, ein Pathname
  2927. # kann GC auslösen
  2928.   local object recalc_defaults_pathname (void);
  2929.   local object recalc_defaults_pathname()
  2930.     {
  2931.       #if defined(PATHNAME_MSDOS) || defined(PATHNAME_OS2)
  2932.       # (MAKE-PATHNAME :DEVICE default-drive) ausführen:
  2933.       pushSTACK(S(Kdevice)); pushSTACK(O(default_drive));
  2934.       funcall(L(make_pathname),2);
  2935.       #endif
  2936.       #if defined(PATHNAME_UNIX) || defined(PATHNAME_AMIGAOS) || defined(PATHNAME_RISCOS)
  2937.       # (MAKE-PATHNAME) ausführen:
  2938.       funcall(L(make_pathname),0);
  2939.       #endif
  2940.       # und *DEFAULT-PATHNAME-DEFAULTS* zuweisen:
  2941.       set_Symbol_value(S(default_pathname_defaults),value1);
  2942.       return value1;
  2943.     }
  2944.  
  2945. # UP: Liefert den Default-Pathname.
  2946. # defaults_pathname()
  2947. # < ergebnis: Wert von *DEFAULT-PATHNAME-DEFAULTS*, ein Pathname
  2948. # kann GC auslösen
  2949.   local object defaults_pathname (void);
  2950.   local object defaults_pathname()
  2951.     { var reg1 object pathname = Symbol_value(S(default_pathname_defaults)); # Wert von *DEFAULT-PATHNAME-DEFAULTS*
  2952.       if (pathnamep(pathname))
  2953.         # ist Pathname -> OK
  2954.         { return pathname; }
  2955.         else
  2956.         # sonst Warnung:
  2957.         { pushSTACK(subr_self); # subr_self retten (für spätere Fehlermeldungen)
  2958.           # (WARN "Der Wert von ~S war kein Pathname. ~:*~S wird zurückgesetzt." ...)
  2959.           pushSTACK(OL(defaults_warn_string));
  2960.           pushSTACK(S(default_pathname_defaults));
  2961.           funcall(S(warn),2);
  2962.           # und neuberechnen:
  2963.           pathname = recalc_defaults_pathname();
  2964.           subr_self = popSTACK();
  2965.           return pathname;
  2966.     }   }
  2967.  
  2968. LISPFUN(merge_pathnames,1,2,norest,key,1, (kw(wild)))
  2969. # (MERGE-PATHNAMES pathname [defaults [default-version]] [:wild]), CLTL S. 415
  2970. # (defun merge-pathnames (pathname &optional (defaults *default-pathname-defaults*) default-version)
  2971. #   (setq pathname (pathname pathname))
  2972. #   (setq defaults (pathname defaults))
  2973. #   (multiple-value-call #'make-pathname
  2974. #if HAS_HOST
  2975. #     (if (or (equal (pathname-host pathname) (pathname-host defaults))
  2976. #             (null (pathname-host pathname))
  2977. #         )
  2978. #       (values
  2979. #         :host (or (pathname-host pathname) (pathname-host defaults))
  2980. #endif
  2981. #if HAS_DEVICE
  2982. #     (if (or (equal (pathname-device pathname) (pathname-device defaults))
  2983. #             (null (pathname-device pathname))
  2984. #         )
  2985. #       (values
  2986. #         :device (or (pathname-device pathname) (pathname-device defaults))
  2987. #endif
  2988. #         :directory
  2989. #           (let ((pathname-dir (pathname-directory pathname))
  2990. #                 (defaults-dir (pathname-directory defaults)))
  2991. #             (if (eq (car pathname-dir) ':RELATIVE)
  2992. #               (cond ((null (cdr pathname-dir)) defaults-dir)
  2993. #                     ((not (eq (car defaults-dir) ':RELATIVE))
  2994. #                      (append defaults-dir (cdr pathname-dir))
  2995. #                     )
  2996. #                     (t pathname-dir)
  2997. #               )
  2998. #               pathname-dir
  2999. #           ) )
  3000. #       )
  3001. #       (values
  3002. #if HAS_HOST
  3003. #         :host (pathname-host pathname)
  3004. #endif
  3005. #if HAS_DEVICE
  3006. #         :device (pathname-device pathname)
  3007. #endif
  3008. #         :directory (pathname-directory pathname)
  3009. #     ) )
  3010. #     :name (or (pathname-name pathname) (pathname-name defaults))
  3011. #     :type (or (pathname-type pathname) (pathname-type defaults))
  3012. # ) )
  3013. # Ist das :WILD-Argument angegeben, werden statt fehlenden Komponenten
  3014. # :WILD-Komponenten ersetzt.
  3015.   { var reg10 boolean wildp = !(eq(STACK_0,unbound) || nullp(STACK_0));
  3016.     skipSTACK(1);
  3017.     # default-version überprüfen:
  3018.     #if HAS_VERSION || defined(LOGICAL_PATHNAMES)
  3019.     {var reg9 object v = test_optional_version(S(Knewest)); # Default ist :NEWEST
  3020.      STACK_0 = STACK_1; STACK_1 = STACK_2; STACK_2 = v;
  3021.     }# Stackaufbau: default-version, pathname, defaults.
  3022.     #else
  3023.      test_optional_version(S(Knewest)); skipSTACK(1);
  3024.      # Stackaufbau: pathname, defaults.
  3025.     #endif
  3026.     # pathname und defaults überprüfen:
  3027.     # defaults zu einem Pathname machen:
  3028.     STACK_0 = test_default_pathname(STACK_0);
  3029.     # pathname zu einem Pathname machen:
  3030.     #ifdef LOGICAL_PATHNAMES
  3031.     if (logpathnamep(STACK_0))
  3032.       { if (!xpathnamep(STACK_1))
  3033.           { pushSTACK(subr_self); # subr_self retten (für spätere Fehlermeldungen)
  3034.             # Das Ergebnis von (PARSE-NAMESTRING obj nil empty-logical-pathname)
  3035.             # ist garantiert ein logischer Pathname.
  3036.             pushSTACK(STACK_(1+1)); pushSTACK(NIL); pushSTACK(O(empty_logical_pathname));
  3037.             funcall(L(parse_namestring),3);
  3038.             subr_self = popSTACK();
  3039.             STACK_1 = value1;
  3040.       }   }
  3041.       else
  3042.     #endif
  3043.       { STACK_1 = coerce_xpathname(STACK_1); }
  3044.     #ifdef LOGICAL_PATHNAMES
  3045.     if (logpathnamep(STACK_1) && logpathnamep(STACK_0))
  3046.       # MERGE-PATHNAMES für Logical Pathnames
  3047.       { var reg6 object new = allocate_logpathname(); # neuen Pathname holen
  3048.         var reg8 object d = popSTACK(); # defaults
  3049.         var reg7 object p = popSTACK(); # pathname
  3050.         # Hosts matchen:
  3051.         { var reg1 object p_host = TheLogpathname(p)->pathname_host;
  3052.           var reg2 object d_host = TheLogpathname(d)->pathname_host;
  3053.           TheLogpathname(new)->pathname_host = p_host; # erstmal new-host := pathname-host
  3054.           if (equal(p_host,d_host)) goto lmatch_directories;
  3055.           if (wildp ? FALSE : nullp(p_host))
  3056.             { # pathname-host nicht angegeben, aber defaults-host angegeben:
  3057.               TheLogpathname(new)->pathname_host = d_host; # new-host := defaults-host
  3058.               goto lmatch_directories;
  3059.         }   }
  3060.         # Directories nicht matchen:
  3061.         { # new-directory := pathname-directory :
  3062.           TheLogpathname(new)->pathname_directory = TheLogpathname(p)->pathname_directory;
  3063.         }
  3064.         goto ldirectories_OK;
  3065.         lmatch_directories:
  3066.         # Directories matchen:
  3067.         { var reg2 object p_directory = TheLogpathname(p)->pathname_directory; # pathname-directory
  3068.           var reg3 object d_directory = TheLogpathname(d)->pathname_directory; # defaults-directory
  3069.          {var reg4 object new_subdirs = p_directory;
  3070.       # Fängt pathname-subdirs mit :RELATIVE an?
  3071.           if (!wildp && eq(Car(p_directory),S(Krelative)))
  3072.             # ja.
  3073.             { # Endet pathname-subdirs danach?
  3074.               if (matomp(Cdr(p_directory)))
  3075.                 # ja -> verwende defaults-subdirs:
  3076.                 { new_subdirs = d_directory; }
  3077.                 else
  3078.                 # nein.
  3079.                 { # Fängt defaults-subdirs mit :RELATIVE an?
  3080.                   if (eq(Car(d_directory),S(Krelative)))
  3081.                     # ja -> Ersetzen von :RELATIVE in pathname-subdirs
  3082.                     # durch das gesamte defaults-subdirs ist nicht sinnvoll
  3083.                     # (da nicht klar ist, auf was das dabei entstehende
  3084.                     # Default-Directory sich beziehen soll). Daher nichts tun:
  3085.                     {}
  3086.                     else
  3087.                     # nein -> Um :RELATIVE aufzulösen: ersetze :RELATIVE
  3088.                     # in pathname-subdirs durch defaults-subdirs, d.h.
  3089.                     # bilde (append defaults-subdirs (cdr pathname-subdirs)) =
  3090.                     # (nreconc (reverse defaults-subdirs) (cdr pathname-subdirs)) :
  3091.                     { pushSTACK(p); pushSTACK(d); pushSTACK(new);
  3092.                       pushSTACK(Cdr(p_directory));
  3093.                       {var reg1 object temp = reverse(d_directory);
  3094.                        new_subdirs = nreconc(temp,popSTACK());
  3095.                       }
  3096.                       new = popSTACK(); d = popSTACK(); p = popSTACK();
  3097.                     }
  3098.             }   }
  3099.           TheLogpathname(new)->pathname_directory = new_subdirs; # new-directory := new-subdirs
  3100.         }}
  3101.         ldirectories_OK:
  3102.         # Nun sind die Directories OK.
  3103.         # Name matchen:
  3104.         # Verwende pathname-name, falls angegeben, und defaults-name sonst.
  3105.         { var reg1 object p_name = TheLogpathname(p)->pathname_name;
  3106.           TheLogpathname(new)->pathname_name =
  3107.             (!(wildp ? eq(p_name,S(Kwild)) : nullp(p_name))
  3108.              ? p_name
  3109.              : TheLogpathname(d)->pathname_name
  3110.             );
  3111.         }
  3112.         # Typ matchen:
  3113.         # Verwende pathname-type, falls angegeben, und defaults-type sonst.
  3114.         { var reg1 object p_type = TheLogpathname(p)->pathname_type;
  3115.           TheLogpathname(new)->pathname_type =
  3116.             (!(wildp ? eq(p_type,S(Kwild)) : nullp(p_type))
  3117.              ? p_type
  3118.              : TheLogpathname(d)->pathname_type
  3119.             );
  3120.         }
  3121.         # Version matchen:
  3122.         # Verwende pathname-version, falls angegeben, und default-version sonst.
  3123.         { var reg1 object p_version = TheLogpathname(p)->pathname_version;
  3124.           TheLogpathname(new)->pathname_version =
  3125.             (!(wildp ? eq(p_version,S(Kwild)) : nullp(p_version))
  3126.              ? p_version
  3127.              : STACK_0
  3128.             );
  3129.           skipSTACK(1);
  3130.         }
  3131.         # new als Wert:
  3132.         value1 = new; mv_count=1;
  3133.         return;
  3134.       }
  3135.     # nicht beides logische Pathnames -> erst in normale Pathnames umwandeln:
  3136.     STACK_1 = coerce_pathname(STACK_1);
  3137.     STACK_0 = coerce_pathname(STACK_0);
  3138.     #endif
  3139.    {var reg6 object new = allocate_pathname(); # neuen Pathname holen
  3140.     var reg8 object d = popSTACK(); # defaults
  3141.     var reg7 object p = popSTACK(); # pathname
  3142.     #if HAS_HOST
  3143.     # Hosts matchen:
  3144.     { var reg1 object p_host = ThePathname(p)->pathname_host;
  3145.       var reg2 object d_host = ThePathname(d)->pathname_host;
  3146.       ThePathname(new)->pathname_host = p_host; # erstmal new-host := pathname-host
  3147.       # beide Hosts gleich -> Devices matchen:
  3148.       if (equal(p_host,d_host)) goto match_devices;
  3149.       if (wildp ? FALSE : nullp(p_host))
  3150.         { # pathname-host nicht angegeben, aber defaults-host angegeben:
  3151.           ThePathname(new)->pathname_host = d_host; # new-host := defaults-host
  3152.           goto match_devices;
  3153.         }
  3154.       goto notmatch_devices;
  3155.     }
  3156.     #endif
  3157.     match_devices:
  3158.     #if HAS_DEVICE
  3159.     # Devices matchen:
  3160.     { var reg1 object p_device = ThePathname(p)->pathname_device;
  3161.       var reg2 object d_device = ThePathname(d)->pathname_device;
  3162.       ThePathname(new)->pathname_device = p_device; # erstmal new-device := pathname-device
  3163.       # beide Devices gleich -> Directories matchen:
  3164.       if (equal(p_device,d_device)) goto match_directories;
  3165.       if (wildp ? eq(p_device,S(Kwild)) : nullp(p_device))
  3166.         { # pathname-device nicht angegeben, aber defaults-device angegeben:
  3167.           ThePathname(new)->pathname_device = d_device; # new-device := defaults-device
  3168.           goto match_directories;
  3169.         }
  3170.       goto notmatch_directories;
  3171.     }
  3172.     #endif
  3173.     # Directories matchen:
  3174.     match_directories:
  3175.     { var reg2 object p_directory = ThePathname(p)->pathname_directory; # pathname-directory
  3176.       var reg3 object d_directory = ThePathname(d)->pathname_directory; # defaults-directory
  3177.       var reg4 object new_subdirs = p_directory;
  3178.       # Fängt pathname-subdirs mit :RELATIVE an?
  3179.       if (!wildp && eq(Car(p_directory),S(Krelative)))
  3180.         # ja.
  3181.         { # Endet pathname-subdirs danach?
  3182.           if (matomp(Cdr(p_directory)))
  3183.             # ja -> verwende defaults-subdirs:
  3184.             { new_subdirs = d_directory; }
  3185.             else
  3186.             # nein.
  3187.             { # Fängt defaults-subdirs mit :RELATIVE an?
  3188.               if (eq(Car(d_directory),S(Krelative)))
  3189.                 # ja -> Ersetzen von :RELATIVE in pathname-subdirs
  3190.                 # durch das gesamte defaults-subdirs ist nicht sinnvoll
  3191.                 # (da nicht klar ist, auf was das dabei entstehende
  3192.                 # Default-Directory sich beziehen soll). Daher nichts tun:
  3193.                 {}
  3194.                 else
  3195.                 # nein -> Um :RELATIVE aufzulösen: ersetze :RELATIVE
  3196.                 # in pathname-subdirs durch defaults-subdirs, d.h.
  3197.                 # bilde (append defaults-subdirs (cdr pathname-subdirs)) =
  3198.                 # (nreconc (reverse defaults-subdirs) (cdr pathname-subdirs)) :
  3199.                 { pushSTACK(p); pushSTACK(d); pushSTACK(new);
  3200.                   pushSTACK(Cdr(p_directory));
  3201.                   {var reg1 object temp = reverse(d_directory);
  3202.                    new_subdirs = nreconc(temp,popSTACK());
  3203.                   }
  3204.                   new = popSTACK(); d = popSTACK(); p = popSTACK();
  3205.                 }
  3206.         }   }
  3207.       ThePathname(new)->pathname_directory = new_subdirs; # new-directory := new-subdirs
  3208.     }
  3209.     goto directories_OK;
  3210.     # Devices nicht matchen:
  3211.     notmatch_devices:
  3212.     #if HAS_DEVICE
  3213.     { # new-device := pathname-device :
  3214.       ThePathname(new)->pathname_device = ThePathname(p)->pathname_device;
  3215.     }
  3216.     #endif
  3217.     # Directories nicht matchen:
  3218.     notmatch_directories:
  3219.     { # new-directory := pathname-directory :
  3220.       ThePathname(new)->pathname_directory = ThePathname(p)->pathname_directory;
  3221.     }
  3222.     directories_OK:
  3223.     # Nun sind die Directories OK.
  3224.     # Name matchen:
  3225.     # Verwende pathname-name, falls angegeben, und defaults-name sonst.
  3226.     { var reg1 object p_name = ThePathname(p)->pathname_name;
  3227.       ThePathname(new)->pathname_name =
  3228.         (!(wildp ?
  3229.            #ifdef PATHNAME_EXT83
  3230.            eq(p_name,S(Kwild))
  3231.            #else # PATHNAME_NOEXT || PATHNAME_RISCOS
  3232.            equal(p_name,O(wild_string))
  3233.            #endif
  3234.            : nullp(p_name)
  3235.           )
  3236.          ? p_name
  3237.          : ThePathname(d)->pathname_name
  3238.         );
  3239.     }
  3240.     # Typ matchen:
  3241.     # Verwende pathname-type, falls angegeben, und defaults-type sonst.
  3242.     { var reg1 object p_type = ThePathname(p)->pathname_type;
  3243.       ThePathname(new)->pathname_type =
  3244.         (!(wildp ?
  3245.            #ifdef PATHNAME_EXT83
  3246.            eq(p_type,S(Kwild))
  3247.            #else # PATHNAME_NOEXT || PATHNAME_RISCOS
  3248.            equal(p_type,O(wild_string))
  3249.            #endif
  3250.            : nullp(p_type)
  3251.           )
  3252.          ? p_type
  3253.          : ThePathname(d)->pathname_type
  3254.         );
  3255.     }
  3256.     #if HAS_VERSION
  3257.     # Version matchen:
  3258.     # Verwende pathname-version, falls angegeben, und default-version sonst.
  3259.     { var reg1 object p_version = ThePathname(p)->pathname_version;
  3260.       ThePathname(new)->pathname_version =
  3261.         (!(wildp ? eq(p_version,S(Kwild)) : nullp(p_version))
  3262.          ? p_version
  3263.          : STACK_0
  3264.         );
  3265.     }
  3266.     #endif
  3267.     #if HAS_VERSION || defined(LOGICAL_PATHNAMES)
  3268.     skipSTACK(1);
  3269.     #endif
  3270.     # new als Wert:
  3271.     value1 = new; mv_count=1;
  3272.   }}
  3273.  
  3274. LISPFUN(enough_namestring,1,1,norest,nokey,0,NIL)
  3275. # (ENOUGH-NAMESTRING pathname [defaults]), CLTL S. 417
  3276. # (defun enough-namestring (pathname &optional (defaults *default-pathname-defaults*))
  3277. #   (setq pathname (pathname pathname))
  3278. #   (setq defaults (pathname defaults))
  3279. #   (namestring
  3280. #     (multiple-value-call #'make-pathname
  3281. #if HAS_HOST
  3282. #       (if (equal (pathname-host pathname) (pathname-host defaults))
  3283. #         (values
  3284. #           :host nil
  3285. #endif
  3286. #if HAS_DEVICE
  3287. #       (if (equal (pathname-device pathname) (pathname-device defaults))
  3288. #         (values
  3289. #           :device nil
  3290. #endif
  3291. #           :directory
  3292. #             (let ((pathname-dir (pathname-directory pathname))
  3293. #                   (defaults-dir (pathname-directory defaults)))
  3294. #               (if (equal pathname-dir defaults-dir)
  3295. #                 (list ':RELATIVE)
  3296. #                 (if (and (not (eq (car pathname-dir) ':RELATIVE))
  3297. #                          (not (eq (car defaults-dir) ':RELATIVE))
  3298. #                          (equal (subseq pathname-dir 0 (min (length pathname-dir) (length defaults-dir)))
  3299. #                                 defaults-dir
  3300. #                     )    )
  3301. #                   (cons ':RELATIVE (nthcdr (length defaults-dir) pathname-dir))
  3302. #                   pathname-dir
  3303. #             ) ) )
  3304. #         )
  3305. #         (values
  3306. #if HAS_HOST
  3307. #           :host (pathname-host pathname)
  3308. #endif
  3309. #if HAS_DEVICE
  3310. #           :device (pathname-device pathname)
  3311. #endif
  3312. #           :directory (pathname-directory pathname)
  3313. #       ) )
  3314. #       :name (if (equal (pathname-name pathname) (pathname-name defaults))
  3315. #               nil
  3316. #               (pathname-name pathname)
  3317. #             )
  3318. #       :type (if (equal (pathname-type pathname) (pathname-type defaults))
  3319. #               nil
  3320. #               (pathname-type pathname)
  3321. #             )
  3322. # ) ) )
  3323.   { # pathname und defaults überprüfen:
  3324.     # pathname zu einem Pathname machen:
  3325.     STACK_1 = coerce_pathname(STACK_1);
  3326.     # defaults zu einem Pathname machen:
  3327.     STACK_0 = coerce_pathname(test_default_pathname(STACK_0));
  3328.     # neuen Pathname holen:
  3329.    {var reg6 object new = allocate_pathname();
  3330.     pushSTACK(new);
  3331.     # Stackaufbau: pathname, defaults, new.
  3332.     #if HAS_HOST
  3333.     # Hosts vergleichen:
  3334.     { var reg7 object p_host = ThePathname(STACK_2)->pathname_host; # pathname-host
  3335.       var reg8 object d_host = ThePathname(STACK_1)->pathname_host; # defaults-host
  3336.       if (equal(p_host,d_host)) # beide Hosts gleich ?
  3337.         # ja.
  3338.         { ThePathname(new)->pathname_host = NIL; # new-host := NIL
  3339.     #endif
  3340.     #if HAS_DEVICE
  3341.     # Devices vergleichen:
  3342.     { var reg7 object p_device = ThePathname(STACK_2)->pathname_device; # pathname-device
  3343.       var reg8 object d_device = ThePathname(STACK_1)->pathname_device; # defaults-device
  3344.       if (equal(p_device,d_device)) # beide Devices gleich ?
  3345.         # ja.
  3346.         { ThePathname(new)->pathname_device = NIL; # new-device := NIL
  3347.     #endif
  3348.          {var reg3 object p_directory = ThePathname(STACK_2)->pathname_directory; # pathname-directory
  3349.           var reg4 object d_directory = ThePathname(STACK_1)->pathname_directory; # defaults-directory
  3350.           var reg5 object new_subdirs;
  3351.           # vergleiche pathname-subdirs und defaults-subdirs:
  3352.           if (equal(p_directory,d_directory))
  3353.             # gleich -> verwende (cons :RELATIVE nil) :
  3354.             { new_subdirs = NIL; goto insert_RELATIVE; }
  3355.             else
  3356.             { # Fängt weder pathname-subdirs noch defaults-subdirs
  3357.               # mit :RELATIVE an?
  3358.               if (   (!eq(Car(p_directory),S(Krelative)))
  3359.                   && (!eq(Car(d_directory),S(Krelative)))
  3360.                  )
  3361.                 # ja -> testen, ob defaults-subdirs ein Anfangsstück
  3362.                 # der Liste pathname-subdirs ist:
  3363.                 { var reg1 object Lp = p_directory;
  3364.                   var reg2 object Ld = d_directory;
  3365.                   # Ist Ld ein Anfangsstück von Lp ?
  3366.                   loop
  3367.                     { if (atomp(Ld)) # Ld zu Ende -> ja
  3368.                         { new_subdirs = Lp; goto insert_RELATIVE; }
  3369.                       if (atomp(Lp)) break; # Lp zu Ende -> nein
  3370.                       if (!equal(Car(Ld),Car(Lp))) # verschiedene Listenelemente?
  3371.                         break; # -> nein
  3372.                       Ld = Cdr(Ld); Lp = Cdr(Lp); # Listen weiterrücken
  3373.                     }
  3374.                 }
  3375.               new_subdirs = p_directory; # new-subdirs := pathname-subdirs
  3376.               goto subdirs_ok;
  3377.             }
  3378.           insert_RELATIVE:
  3379.           # new-subdirs := (cons :RELATIVE new-subdirs) :
  3380.           { pushSTACK(new_subdirs);
  3381.             new_subdirs = allocate_cons();
  3382.             Cdr(new_subdirs) = popSTACK(); Car(new_subdirs) = S(Krelative);
  3383.           }
  3384.           subdirs_ok: # new-subdirs ist die neue Subdir-Liste.
  3385.           # new-directory := new-subdirs :
  3386.           ThePathname(new=STACK_0)->pathname_directory = new_subdirs;
  3387.          }
  3388.     #if HAS_DEVICE
  3389.         }
  3390.         else
  3391.         # verschiedene Devices
  3392.         { # new-device := pathname-device :
  3393.           ThePathname(new)->pathname_device = p_device;
  3394.           # new-directory := pathname-directory :
  3395.           ThePathname(new)->pathname_directory = ThePathname(STACK_2)->pathname_directory;
  3396.         }
  3397.     }
  3398.     #endif
  3399.     #if HAS_HOST
  3400.         }
  3401.         else
  3402.         # verschiedene Hosts
  3403.         { # new-host := pathname-host :
  3404.           ThePathname(new)->pathname_host = p_host;
  3405.           #if HAS_DEVICE
  3406.           # new-device := pathname-device :
  3407.           ThePathname(new)->pathname_device = ThePathname(STACK_2)->pathname_device;
  3408.           #endif
  3409.           # new-directory := pathname-directory :
  3410.           ThePathname(new)->pathname_directory = ThePathname(STACK_2)->pathname_directory;
  3411.         }
  3412.     }
  3413.     #endif
  3414.     # name einfüllen:
  3415.     { var reg1 object p_name = ThePathname(STACK_2)->pathname_name; # pathname-name
  3416.       var reg2 object d_name = ThePathname(STACK_1)->pathname_name; # defaults-name
  3417.       ThePathname(new)->pathname_name = (equal(p_name,d_name) ? NIL : p_name);
  3418.     }
  3419.     # type einfüllen:
  3420.     { var reg1 object p_type = ThePathname(STACK_2)->pathname_type; # pathname-type
  3421.       var reg2 object d_type = ThePathname(STACK_1)->pathname_type; # defaults-type
  3422.       ThePathname(new)->pathname_type = (equal(p_type,d_type) ? NIL : p_type);
  3423.     }
  3424.     skipSTACK(3);
  3425.     # (namestring new) bilden:
  3426.     value1 = whole_namestring(new); mv_count=1;
  3427.   }}
  3428.  
  3429. #ifdef LOGICAL_PATHNAMES
  3430.  
  3431. # UP: Überprüft, ob object ein zulässiger Name ist:
  3432. # :WILD oder ein Simple-String aus gültigen Zeichen, keine adjazenten '*'.
  3433. # legal_logical_word(object)
  3434.   local boolean legal_logical_word (object obj);
  3435.   local boolean legal_logical_word(obj)
  3436.     var reg5 object obj;
  3437.     { if (eq(obj,S(Kwild))) { return TRUE; }
  3438.       if (!simple_string_p(obj)) { return FALSE; }
  3439.      {var reg3 uintL len = TheSstring(obj)->length;
  3440.       if (len==0) { return FALSE; } # leeres Word ist verboten
  3441.       {var reg2 uintB* charptr = &TheSstring(obj)->data[0];
  3442.        var reg4 boolean last_was_star = FALSE;
  3443.        dotimespL(len,len,
  3444.          { var reg1 uintB ch = *charptr++;
  3445.            if (!(legal_logical_word_char(ch) || (ch=='*'))) { return FALSE; }
  3446.            if (ch=='*')
  3447.              { if (last_was_star) return FALSE; # adjazente '*' sind verboten
  3448.                last_was_star = TRUE;
  3449.              }
  3450.              else
  3451.              { last_was_star = FALSE; }
  3452.          });
  3453.        return TRUE;
  3454.     }}}
  3455.  
  3456. #endif
  3457.  
  3458. #ifdef PATHNAME_EXT83
  3459.  
  3460. # UP: Überprüft, ob object ein zulässiger Name oder Typ ist: :WILD oder
  3461. # ein Simple-String mit max. stdlen Zeichen, alle alphabetisch und Up-case.
  3462. # legal_name_or_type(object,stdlen)
  3463.   local boolean legal_name_or_type (object obj, uintL stdlen);
  3464.   local boolean legal_name_or_type(obj,stdlen)
  3465.     var reg3 object obj;
  3466.     var reg4 uintL stdlen;
  3467.     { if (eq(obj,S(Kwild))) { return TRUE; } # :WILD ist OK
  3468.       if (!simple_string_p(obj)) { return FALSE; } # sonst: Simple-String ?
  3469.      {var reg2 uintL len = TheSstring(obj)->length;
  3470.       #ifndef EMUNIX_PORTABEL
  3471.       if (!(len <= stdlen)) { return FALSE; } # und Länge <=stdlen ?
  3472.       #endif
  3473.       # Jedes einzelne Zeichen überprüfen:
  3474.       {var reg1 uintB* ptr = &TheSstring(obj)->data[0];
  3475.        dotimesL(len,len,
  3476.          { var reg1 uintB ch = *ptr++;
  3477.            if (!(legal_namechar(ch) # zulässiges Zeichen ?
  3478.                  && (up_case(ch)==ch) # und Großbuchstabe ?
  3479.               ) )
  3480.              { return FALSE; }
  3481.          });
  3482.       }
  3483.       return TRUE;
  3484.     }}
  3485.  
  3486. # UP: Überprüft, ob object ein zulässiger Name ist: :WILD oder
  3487. # ein Simple-String mit max. 8 Zeichen, alle alphabetisch und Up-case.
  3488. # legal_name(object)
  3489.   #define legal_name(obj)  legal_name_or_type(obj,8)
  3490.  
  3491. # UP: Überprüft, ob object ein zulässiger Typ ist: :WILD oder
  3492. # ein Simple-String mit max. 3 Zeichen, alle alphabetisch und Up-case.
  3493. # legal_type(object)
  3494.   #define legal_type(obj)  legal_name_or_type(obj,3)
  3495.  
  3496. #endif # PATHNAME_EXT83
  3497.  
  3498. #if defined(PATHNAME_NOEXT) || defined(PATHNAME_RISCOS)
  3499.  
  3500. # UP: Überprüft, ob object ein zulässiger Name ist:
  3501. # ein Simple-String aus gültigen Zeichen
  3502. # legal_name(object)
  3503.   local boolean legal_name (object obj);
  3504.   local boolean legal_name(obj)
  3505.     var reg3 object obj;
  3506.     { if (!simple_string_p(obj)) { return FALSE; }
  3507.      {var reg2 uintL len = TheSstring(obj)->length;
  3508.       var reg1 uintB* charptr = &TheSstring(obj)->data[0];
  3509.       dotimesL(len,len, { if (!legal_namechar(*charptr++)) { return FALSE; } } );
  3510.       return TRUE;
  3511.     }}
  3512.  
  3513. # UP: Überprüft, ob object ein zulässiger Name ist:
  3514. # ein Simple-String aus gültigen Zeichen, ohne '.'
  3515. # legal_type(object)
  3516.   local boolean legal_type (object obj);
  3517. #ifdef PATHNAME_NOEXT
  3518.   local boolean legal_type(obj)
  3519.     var reg4 object obj;
  3520.     { if (!simple_string_p(obj)) { return FALSE; }
  3521.      {var reg3 uintL len = TheSstring(obj)->length;
  3522.       var reg2 uintB* charptr = &TheSstring(obj)->data[0];
  3523.       dotimesL(len,len,
  3524.         { var reg1 uintB ch = *charptr++;
  3525.           if ((ch=='.') || (!legal_namechar(ch))) { return FALSE; }
  3526.         });
  3527.       return TRUE;
  3528.     }}
  3529. #endif
  3530. #ifdef PATHNAME_RISCOS
  3531.   #define legal_type(obj)  legal_name(obj)
  3532. #endif
  3533.  
  3534. #endif # PATHNAME_NOEXT || PATHNAME_RISCOS
  3535.  
  3536. LISPFUN(make_pathname,0,0,norest,key,8,\
  3537.         (kw(defaults),kw(case),kw(host),kw(device),kw(directory),kw(name),kw(type),kw(version)) )
  3538. # (MAKE-PATHNAME [:host] [:device] [:directory] [:name] [:type] [:version]
  3539. #                [:defaults] [:case]),
  3540. # CLTL S. 416, CLtL2 S. 643
  3541.   # Stackaufbau: defaults, case, host, device, directory, name, type, version.
  3542.   { var reg5 boolean logical = FALSE;
  3543.     var reg4 boolean convert = eq(STACK_6,S(Kcommon));
  3544.     # 1. host überprüfen:
  3545.     #ifdef LOGICAL_PATHNAMES
  3546.     # Damit TRANSLATE-PATHNAMES logische Pathnames erzeugen kann:
  3547.     if (logpathnamep(STACK_5))
  3548.       { STACK_5 = TheLogpathname(STACK_5)->pathname_host;
  3549.         logical = TRUE; convert = FALSE;
  3550.       }
  3551.     #endif
  3552.     #if HAS_HOST
  3553.     STACK_5 = test_optional_host(STACK_5,convert);
  3554.     #else
  3555.     STACK_5 = test_optional_host(STACK_5);
  3556.     #endif
  3557.     #ifdef LOGICAL_PATHNAMES
  3558.     if (!nullp(STACK_5) && logical_host_p(STACK_5))
  3559.       { logical = TRUE; convert = FALSE; STACK_5 = string_upcase(STACK_5); }
  3560.     #endif
  3561.     # 2. device überprüfen:
  3562.     #if HAS_DEVICE
  3563.     { var reg1 object device = STACK_4;
  3564.       if (eq(device,unbound)) # angegeben ?
  3565.         { STACK_4 = NIL; } # nein -> verwende NIL
  3566.         else
  3567.         { if (convert) { STACK_4 = device = common_case(device); }
  3568.           if (nullp(device)) goto device_ok; # = NIL ?
  3569.           #ifdef LOGICAL_PATHNAMES
  3570.           elif (logical)
  3571.             { if (logpathnamep(device)) # Pathname -> dessen Device
  3572.                 { STACK_4 = NIL; goto device_ok; }
  3573.             }
  3574.           #endif
  3575.           #if defined(PATHNAME_MSDOS) || defined(PATHNAME_OS2)
  3576.           elif (eq(device,S(Kwild))) goto device_ok; # = :WILD ?
  3577.           elif (simple_string_p(device)) # Simple-String ?
  3578.             { if (TheSstring(device)->length == 1) # der Länge 1 ?
  3579.                 { var reg2 uintB ch = TheSstring(device)->data[0];
  3580.                   if ((ch >= 'A') && (ch <= 'Z')) # mit Buchstaben >='A' und <='Z' ?
  3581.                     goto device_ok;
  3582.             }   }
  3583.           #endif
  3584.           #ifdef PATHNAME_AMIGAOS
  3585.           elif (simple_string_p(device)) # Simple-String ?
  3586.             { var reg1 uintB* ptr = &TheSstring(device)->data[0];
  3587.               var reg2 uintL count;
  3588.               dotimesL(count,TheSstring(device)->length,
  3589.                 { if (!legal_namechar(*ptr++)) goto device_not_ok; }
  3590.                 );
  3591.               goto device_ok;
  3592.               device_not_ok: ;
  3593.             }
  3594.           #endif
  3595.           #ifdef PATHNAME_RISCOS
  3596.           elif (simple_string_p(device)) # Simple-String ?
  3597.             { var reg2 uintB* ptr = &TheSstring(device)->data[0];
  3598.               var reg3 uintL count;
  3599.               dotimesL(count,TheSstring(device)->length,
  3600.                 { var reg2 uintB ch = *ptr++;
  3601.                   if (!(legal_namechar(ch) && !(ch=='*') && !singlewild_char_p(ch)))
  3602.                     goto device_not_ok;
  3603.                 });
  3604.               goto device_ok;
  3605.               device_not_ok: ;
  3606.             }
  3607.           #endif
  3608.           elif (xpathnamep(device)) # Pathname -> dessen Device
  3609.             {
  3610.               #ifdef LOGICAL_PATHNAMES
  3611.               device = coerce_pathname(device);
  3612.               #endif
  3613.               STACK_4 = ThePathname(device)->pathname_device; goto device_ok;
  3614.             }
  3615.           # Keiner der gewünschten Fälle -> Fehler:
  3616.           pushSTACK(STACK_4); pushSTACK(S(Kdevice)); goto fehler_arg;
  3617.           device_ok: ;
  3618.     }   }
  3619.     #else
  3620.     { var reg1 object device = STACK_4;
  3621.       if (!eq(device,unbound)) # angegeben ?
  3622.         if (!(nullp(device) || xpathnamep(device))) # NIL oder Pathname -> OK
  3623.           # Keiner der gewünschten Fälle -> Fehler:
  3624.           { pushSTACK(STACK_4); pushSTACK(S(Kdevice)); goto fehler_arg; }
  3625.     }
  3626.     #endif
  3627.     # 3. directory überprüfen:
  3628.     { var reg1 object directory = STACK_3;
  3629.       if (eq(directory,unbound) || nullp(directory)) # nicht angegeben oder =NIL ?
  3630.         {
  3631.           #ifdef PATHNAME_AMIGAOS
  3632.           if (!nullp(STACK_4)) # Device angegeben (bei nicht-logical Pathname)?
  3633.             STACK_3 = O(directory_absolute); # Default ist (:ABSOLUTE)
  3634.           else
  3635.           #endif
  3636.             STACK_3 = O(directory_default); # Default ist ([NIL] :RELATIVE)
  3637.           goto directory_ok;
  3638.         }
  3639.       elif (consp(directory)) # ein Cons?
  3640.         { if (convert) { STACK_3 = directory = subst_common_case(directory); }
  3641.           # Der CAR entweder :RELATIVE oder :ABSOLUTE ?
  3642.           if (!consp(directory)) goto directory_bad;
  3643.           { var reg1 object startpoint = Car(directory);
  3644.             if (!(eq(startpoint,S(Krelative)) || eq(startpoint,S(Kabsolute))))
  3645.               goto directory_bad;
  3646.             #ifdef PATHNAME_RISCOS
  3647.             if (!logical && eq(startpoint,S(Kabsolute)))
  3648.               { directory = Cdr(directory);
  3649.                 startpoint = Car(directory);
  3650.                 if (!(eq(startpoint,S(Kroot))
  3651.                       || eq(startpoint,S(Khome))
  3652.                       || eq(startpoint,S(Kcurrent))
  3653.                       || eq(startpoint,S(Klibrary))
  3654.                       || eq(startpoint,S(Kprevious))
  3655.                    ) )
  3656.                   goto directory_bad;
  3657.               }
  3658.             #endif
  3659.           }
  3660.           directory = Cdr(directory);
  3661.           # Subdir-Liste überprüfen:
  3662.           while (consp(directory))
  3663.             { # nächstes subdir überprüfen:
  3664.               var reg1 object subdir = Car(directory);
  3665.               #ifdef LOGICAL_PATHNAMES
  3666.               if (logical)
  3667.                 { if (!(eq(subdir,S(Kwild_inferiors)) || legal_logical_word(subdir)))
  3668.                     goto directory_bad;
  3669.                 }
  3670.                 else
  3671.               #endif
  3672.                 {
  3673.                   #ifdef PATHNAME_EXT83
  3674.                   if (consp(subdir))
  3675.                     { # subdir ist ein Cons
  3676.                       if (!(legal_name(Car(subdir)) && legal_type(Cdr(subdir))))
  3677.                         goto directory_bad;
  3678.                     }
  3679.                     else
  3680.                     { # subdir ist ein Atom
  3681.                       if (!(eq(subdir,S(Kcurrent)) # = :CURRENT ?
  3682.                             || eq(subdir,S(Kparent)) # = :PARENT ?
  3683.                             || eq(subdir,S(Kwild_inferiors)) # = :WILD-INFERIORS ?
  3684.                          ) )
  3685.                         goto directory_bad;
  3686.                     }
  3687.                   #endif
  3688.                   #ifdef PATHNAME_NOEXT
  3689.                   #ifdef PATHNAME_AMIGAOS
  3690.                   if (!(eq(subdir,S(Kwild_inferiors)) || eq(subdir,S(Kparent))
  3691.                         || legal_name(subdir)
  3692.                      ) )
  3693.                     goto directory_bad;
  3694.                   #endif
  3695.                   #if defined(PATHNAME_UNIX) || defined(PATHNAME_OS2)
  3696.                   if (!(eq(subdir,S(Kwild_inferiors)) || legal_name(subdir)))
  3697.                     goto directory_bad;
  3698.                   #endif
  3699.                   #endif
  3700.                   #ifdef PATHNAME_RISCOS
  3701.                   if (!(eq(subdir,S(Kparent)) || legal_name(subdir)))
  3702.                     goto directory_bad;
  3703.                   #endif
  3704.                 }
  3705.               directory = Cdr(directory);
  3706.             }
  3707.           goto directory_ok;
  3708.         }
  3709.       #ifdef LOGICAL_PATHNAMES
  3710.       elif (logical)
  3711.         { if (logpathnamep(directory)) # Pathname -> dessen Directory
  3712.             { STACK_3 = TheLogpathname(directory)->pathname_directory; goto directory_ok; }
  3713.         }
  3714.       #endif
  3715.       elif (xpathnamep(directory)) # Pathname -> dessen Directory
  3716.         {
  3717.           #ifdef LOGICAL_PATHNAMES
  3718.           directory = coerce_pathname(directory);
  3719.           #endif
  3720.           STACK_3 = ThePathname(directory)->pathname_directory; goto directory_ok;
  3721.         }
  3722.       elif (stringp(directory) && legal_name(directory))
  3723.         { var reg1 object new_cons = allocate_cons();
  3724.           Car(new_cons) = S(Kabsolute); Cdr(new_cons) = allocate_cons();
  3725.           Car(Cdr(new_cons)) = directory;
  3726.           STACK_3 = new_cons;
  3727.           goto directory_ok;
  3728.         }
  3729.       # Keiner der gewünschten Fälle -> Fehler:
  3730.       directory_bad:
  3731.       pushSTACK(STACK_3); pushSTACK(S(Kdirectory)); goto fehler_arg;
  3732.       directory_ok: ;
  3733.       #ifdef PATHNAME_AMIGAOS
  3734.       # Bei device /= NIL muß directory mit :ABSOLUTE anfangen:
  3735.       if (!nullp(STACK_4) && !eq(Car(STACK_3),S(Kabsolute))) goto directory_bad;
  3736.       #endif
  3737.     }
  3738.     # 4. name überprüfen:
  3739.     { var reg1 object name = STACK_2;
  3740.       if (convert) { STACK_2 = name = common_case(name); }
  3741.       if (eq(name,unbound))
  3742.         { STACK_2 = NIL; } # nicht angegeben -> verwende NIL
  3743.       elif (nullp(name)) {} # NIL ist OK
  3744.       #ifdef LOGICAL_PATHNAMES
  3745.       elif (logical)
  3746.         { if (legal_logical_word(name)) {} # OK
  3747.           elif (logpathnamep(name)) # Pathname -> dessen Name
  3748.             { STACK_2 = TheLogpathname(name)->pathname_name; }
  3749.           else # Keiner der gewünschten Fälle -> Fehler:
  3750.             { pushSTACK(STACK_2); pushSTACK(S(Kname)); goto fehler_arg; }
  3751.         }
  3752.       #endif
  3753.       #if defined(PATHNAME_NOEXT) || defined(PATHNAME_RISCOS)
  3754.       elif (eq(name,S(Kwild))) { STACK_2 = O(wild_string); } # aus :WILD mache "*"
  3755.       #endif
  3756.       elif (equal(name,O(leer_string))) # name = "" ?
  3757.         { STACK_2 = NIL; } # ja -> verwende NIL
  3758.       elif (legal_name(name)) {} # zulässiger Name ist OK
  3759.       elif (xpathnamep(name)) # Pathname -> dessen Name
  3760.         {
  3761.           #ifdef LOGICAL_PATHNAMES
  3762.           name = coerce_pathname(name);
  3763.           #endif
  3764.           STACK_2 = ThePathname(name)->pathname_name;
  3765.         }
  3766.       else # Keiner der gewünschten Fälle -> Fehler:
  3767.         { pushSTACK(STACK_2); pushSTACK(S(Kname)); goto fehler_arg; }
  3768.     }
  3769.     # 5. type überprüfen:
  3770.     { var reg1 object type = STACK_1;
  3771.       if (convert) { STACK_1 = type = common_case(type); }
  3772.       if (eq(type,unbound))
  3773.         { STACK_1 = NIL; } # nicht angegeben -> verwende NIL
  3774.       elif (nullp(type)) {} # NIL ist OK
  3775.       #ifdef LOGICAL_PATHNAMES
  3776.       elif (logical)
  3777.         { if (legal_logical_word(type)) {} # OK
  3778.           elif (logpathnamep(type)) # Pathname -> dessen Typ
  3779.             { STACK_1 = TheLogpathname(type)->pathname_type; }
  3780.           else # Keiner der gewünschten Fälle -> Fehler:
  3781.             { pushSTACK(STACK_1); pushSTACK(S(Ktype)); goto fehler_arg; }
  3782.         }
  3783.       #endif
  3784.       #if defined(PATHNAME_NOEXT) || defined(PATHNAME_RISCOS)
  3785.       elif (eq(type,S(Kwild))) { STACK_1 = O(wild_string); } # aus :WILD mache "*"
  3786.       #endif
  3787.       elif (legal_type(type)) {} # zulässiger Typ ist OK
  3788.       elif (xpathnamep(type)) # Pathname -> dessen Typ
  3789.         {
  3790.           #ifdef LOGICAL_PATHNAMES
  3791.           type = coerce_pathname(type);
  3792.           #endif
  3793.           STACK_1 = ThePathname(type)->pathname_type;
  3794.         }
  3795.       else # Keiner der gewünschten Fälle -> Fehler:
  3796.         { pushSTACK(STACK_1); pushSTACK(S(Ktype)); goto fehler_arg; }
  3797.     }
  3798.     # 6. version überprüfen:
  3799.     #if HAS_VERSION || defined(LOGICAL_PATHNAMES)
  3800.     STACK_0 = test_optional_version(NIL); # Default ist NIL
  3801.     #else
  3802.     test_optional_version(NIL);
  3803.     #endif
  3804.     # 7. Pathname bauen:
  3805.     {var reg1 object pathname;
  3806.      #ifdef LOGICAL_PATHNAMES
  3807.      if (logical)
  3808.        { pathname = allocate_logpathname(); # neuer Logical Pathname
  3809.          TheLogpathname(pathname)->pathname_version   = popSTACK();
  3810.          TheLogpathname(pathname)->pathname_type      = popSTACK();
  3811.          TheLogpathname(pathname)->pathname_name      = popSTACK();
  3812.          TheLogpathname(pathname)->pathname_directory = popSTACK();
  3813.          skipSTACK(1);
  3814.          TheLogpathname(pathname)->pathname_host      = popSTACK();
  3815.        }
  3816.        else
  3817.      #endif
  3818.        { pathname = allocate_pathname(); # neuer Pathname
  3819.          #if HAS_VERSION
  3820.          ThePathname(pathname)->pathname_version   = popSTACK();
  3821.          #else
  3822.          skipSTACK(1);
  3823.          #endif
  3824.          ThePathname(pathname)->pathname_type      = popSTACK();
  3825.          ThePathname(pathname)->pathname_name      = popSTACK();
  3826.          ThePathname(pathname)->pathname_directory = popSTACK();
  3827.          #if HAS_DEVICE
  3828.          ThePathname(pathname)->pathname_device    = popSTACK();
  3829.          #else
  3830.          skipSTACK(1);
  3831.          #endif
  3832.          #if HAS_HOST
  3833.          ThePathname(pathname)->pathname_host      = popSTACK();
  3834.          #else
  3835.          skipSTACK(1);
  3836.          #endif
  3837.        }
  3838.     skipSTACK(1); # case vergessen
  3839.     # 8. evtl. Defaults hineinmergen:
  3840.      {var reg2 object defaults = popSTACK();
  3841.       if (eq(defaults,unbound))
  3842.         # keine Defaults angegeben -> pathname als Wert
  3843.         { value1 = pathname; }
  3844.         else
  3845.         # (MERGE-PATHNAMES pathname defaults [nil]) aufrufen:
  3846.         { pushSTACK(pathname); pushSTACK(defaults); pushSTACK(NIL);
  3847.           funcall(L(merge_pathnames),3);
  3848.         }
  3849.       mv_count=1;
  3850.       return;
  3851.     }}
  3852.     # Fehlermeldung:
  3853.     fehler_arg:
  3854.     pushSTACK(TheSubr(subr_self)->name);
  3855.     //: DEUTSCH "~: Unzulässiges ~-Argument ~"
  3856.     //: ENGLISH "~: illegal ~ argument ~"
  3857.     //: FRANCAIS "~ : Argument incorrect pour ~ : ~"
  3858.     fehler(error, GETTEXT("~: illegal ~ argument ~"));
  3859.   }
  3860.  
  3861. #ifdef LOGICAL_PATHNAMES
  3862.  
  3863. LISPFUN(make_logical_pathname,0,0,norest,key,8,\
  3864.         (kw(defaults),kw(case),kw(host),kw(device),kw(directory),kw(name),kw(type),kw(version)) )
  3865. # (MAKE-LOGICAL-PATHNAME [:host] [:device] [:directory] [:name] [:type] [:version]
  3866. #                        [:defaults] [:case]),
  3867. # wie MAKE-PATHNAME, nur daß ein Logical Pathname gebildet wird.
  3868.   { # Ein logischer Pathname als :HOST-Argument zu MAKE-PATHNAME
  3869.     # erzwingt einen logischen Pathname als Ergebnis.
  3870.     var reg1 object obj = allocate_logpathname();
  3871.     TheLogpathname(obj)->pathname_host = (!eq(STACK_5,unbound) ? STACK_5 : NIL);
  3872.     STACK_5 = obj;
  3873.     # weiter bei MAKE-PATHNAME.
  3874.     C_make_pathname();
  3875.   }
  3876.  
  3877. #endif
  3878.  
  3879. #ifdef USER_HOMEDIR
  3880. LISPFUN(user_homedir_pathname,0,1,norest,nokey,0,NIL)
  3881. # (USER-HOMEDIR-PATHNAME [host]), CLTL S. 418
  3882.   {
  3883.     #if HAS_HOST
  3884.     STACK_0 = test_optional_host(STACK_0,FALSE); # Host überprüfen
  3885.     #ifdef PATHNAME_RISCOS
  3886.     {var reg1 object pathname = allocate_pathname(); # neuer Pathname
  3887.      ThePathname(pathname)->pathname_host      = popSTACK();
  3888.      #if HAS_DEVICE
  3889.      ThePathname(pathname)->pathname_device    = NIL;
  3890.      #endif
  3891.      ThePathname(pathname)->pathname_directory = O(directory_homedir);
  3892.      ThePathname(pathname)->pathname_name      = NIL;
  3893.      ThePathname(pathname)->pathname_type      = NIL;
  3894.      #if HAS_VERSION
  3895.      ThePathname(pathname)->pathname_version   = NIL;
  3896.      #endif
  3897.      value1 = pathname;
  3898.     }
  3899.     #else
  3900.     ??
  3901.     #endif
  3902.     #else
  3903.     test_optional_host(popSTACK()); # Host überprüfen und ignorieren
  3904.     value1 = O(user_homedir); # User-Homedir-Pathname
  3905.     #endif
  3906.     mv_count=1; # als Wert
  3907.   }
  3908. #endif
  3909.  
  3910. # UP: Kopiert einen Pathname.
  3911. # copy_pathname(pathname)
  3912. # > pathname: nicht-Logical Pathname
  3913. # < ergebnis: Kopie des Pathname, mit denselben Komponenten
  3914. # kann GC auslösen
  3915.   local object copy_pathname (object pathname);
  3916.   local object copy_pathname(pathname)
  3917.     var reg2 object pathname;
  3918.     { pushSTACK(pathname);
  3919.      {var reg1 object new = allocate_pathname();
  3920.       pathname = popSTACK();
  3921.       #if HAS_HOST
  3922.       ThePathname(new)->pathname_host      = ThePathname(pathname)->pathname_host     ;
  3923.       #endif
  3924.       #if HAS_DEVICE
  3925.       ThePathname(new)->pathname_device    = ThePathname(pathname)->pathname_device   ;
  3926.       #endif
  3927.       ThePathname(new)->pathname_directory = ThePathname(pathname)->pathname_directory;
  3928.       ThePathname(new)->pathname_name      = ThePathname(pathname)->pathname_name     ;
  3929.       ThePathname(new)->pathname_type      = ThePathname(pathname)->pathname_type     ;
  3930.       #if HAS_VERSION
  3931.       ThePathname(new)->pathname_version   = ThePathname(pathname)->pathname_version  ;
  3932.       #endif
  3933.       return new;
  3934.     }}
  3935.  
  3936. # Wildcards
  3937. # =========
  3938.  
  3939. #if defined(PATHNAME_NOEXT) || defined(PATHNAME_RISCOS)
  3940. # UP: Testet, ob ein Simple-String Wildcards enthält.
  3941. # has_wildcards(string)
  3942. # > string: Simple-String
  3943. # < ergebnis: TRUE wenn string Wildcard-Zeichen enthält
  3944.   local boolean has_wildcards (object string);
  3945.   local boolean has_wildcards(string)
  3946.     var reg4 object string;
  3947.     { var reg3 uintL len = TheSstring(string)->length;
  3948.       var reg2 uintB* charptr = &TheSstring(string)->data[0];
  3949.       dotimesL(len,len,
  3950.         { var reg1 uintB ch = *charptr++;
  3951.           if ((ch=='*') # Wildcard für beliebig viele Zeichen
  3952.               || singlewild_char_p(ch) # Wildcard für genau ein Zeichen
  3953.              )
  3954.             { return TRUE; }
  3955.         });
  3956.       return FALSE;
  3957.     }
  3958. #endif
  3959.  
  3960. #ifdef LOGICAL_PATHNAMES
  3961. # UP: Testet, ob ein Simple-String Wildcards enthält.
  3962. # has_word_wildcards(string)
  3963. # > string: Simple-String
  3964. # < ergebnis: TRUE wenn string Wildcard-Zeichen enthält
  3965.   local boolean has_word_wildcards (object string);
  3966.   local boolean has_word_wildcards(string)
  3967.     var reg3 object string;
  3968.     { var reg2 uintL len = TheSstring(string)->length;
  3969.       var reg1 uintB* charptr = &TheSstring(string)->data[0];
  3970.       dotimesL(len,len, { if (*charptr++ == '*') { return TRUE; } } );
  3971.       return FALSE;
  3972.     }
  3973. #endif
  3974.  
  3975. # UP: Testet, ob die Host-Komponente eines Pathname Wildcards enthält.
  3976. # has_host_wildcards(pathname)
  3977. # > pathname: Pathname
  3978. # < ergebnis: TRUE wenn (PATHNAME-HOST pathname) Wildcards enthält.
  3979.   local boolean has_host_wildcards (object pathname);
  3980.   # Host kann keine Wildcards enthalten.
  3981.   #define has_host_wildcards(pathname)  (unused (pathname), FALSE)
  3982.  
  3983. # UP: Testet, ob die Device-Komponente eines Pathname Wildcards enthält.
  3984. # has_device_wildcards(pathname)
  3985. # > pathname: Pathname
  3986. # < ergebnis: TRUE wenn (PATHNAME-DEVICE pathname) Wildcards enthält.
  3987.   local boolean has_device_wildcards (object pathname);
  3988.   local boolean has_device_wildcards(pathname)
  3989.     var reg1 object pathname;
  3990.     {
  3991.       #if defined(PATHNAME_MSDOS) || defined(PATHNAME_OS2)
  3992.       #ifdef LOGICAL_PATHNAMES
  3993.       if (logpathnamep(pathname))
  3994.         return FALSE;
  3995.       #endif
  3996.       # Device überprüfen: = :WILD ?
  3997.       return eq(ThePathname(pathname)->pathname_device,S(Kwild));
  3998.       #else
  3999.       return FALSE;
  4000.       #endif
  4001.     }
  4002.  
  4003. # UP: Testet, ob die Directory-Komponente eines Pathname Wildcards enthält.
  4004. # has_directory_wildcards(pathname)
  4005. # > pathname: Pathname
  4006. # < ergebnis: TRUE wenn (PATHNAME-DIRECTORY pathname) Wildcards enthält.
  4007.   local boolean has_directory_wildcards (object pathname);
  4008.   local boolean has_directory_wildcards(pathname)
  4009.     var reg3 object pathname;
  4010.     { # Directory überprüfen:
  4011.       #ifdef LOGICAL_PATHNAMES
  4012.       if (logpathnamep(pathname))
  4013.         { var reg1 object directory = TheLogpathname(pathname)->pathname_directory;
  4014.           while (consp(directory = Cdr(directory)))
  4015.             { var reg2 object subdir = Car(directory);
  4016.               if (simple_string_p(subdir))
  4017.                 { if (has_word_wildcards(subdir)) return TRUE; }
  4018.                 else
  4019.                 { if (eq(subdir,S(Kwild)) || eq(subdir,S(Kwild_inferiors)))
  4020.                     return TRUE;
  4021.             }   }
  4022.           return FALSE;
  4023.         }
  4024.       #endif
  4025.      {var reg1 object directory = ThePathname(pathname)->pathname_directory;
  4026.       while (consp(directory = Cdr(directory)))
  4027.         { var reg2 object subdir = Car(directory);
  4028.           #ifdef PATHNAME_EXT83
  4029.           if (consp(subdir))
  4030.             { # subdir ist ein Cons. name oder type = :WILD ?
  4031.               if (eq(Car(subdir),S(Kwild)) || eq(Cdr(subdir),S(Kwild)))
  4032.                 return TRUE;
  4033.             }
  4034.             else
  4035.             { # subdir ist ein Atom. = :WILD-INFERIORS ?
  4036.               if (eq(subdir,S(Kwild_inferiors)))
  4037.                 return TRUE;
  4038.             }
  4039.           #endif
  4040.           #ifdef PATHNAME_NOEXT
  4041.           if (simple_string_p(subdir))
  4042.             { if (has_wildcards(subdir)) return TRUE; }
  4043.             else
  4044.             { if (eq(subdir,S(Kwild_inferiors))) return TRUE; }
  4045.           #endif
  4046.           #ifdef PATHNAME_RISCOS
  4047.           if (simple_string_p(subdir))
  4048.             { if (has_wildcards(subdir)) return TRUE; }
  4049.           #endif
  4050.         }
  4051.       return FALSE;
  4052.     }}
  4053.  
  4054. # UP: Testet, ob die Name-Komponente eines Pathname Wildcards enthält.
  4055. # has_name_wildcards(pathname)
  4056. # > pathname: Pathname
  4057. # < ergebnis: TRUE wenn (PATHNAME-NAME pathname) Wildcards enthält.
  4058.   local boolean has_name_wildcards (object pathname);
  4059.   local boolean has_name_wildcards(pathname)
  4060.     var reg2 object pathname;
  4061.     { # Name überprüfen:
  4062.       #ifdef LOGICAL_PATHNAMES
  4063.       if (logpathnamep(pathname))
  4064.         { var reg1 object name = TheLogpathname(pathname)->pathname_name;
  4065.           if (simple_string_p(name))
  4066.             { if (has_word_wildcards(name)) return TRUE; }
  4067.             else
  4068.             { if (eq(name,S(Kwild))) return TRUE; }
  4069.           return FALSE;
  4070.         }
  4071.       #endif
  4072.       #ifdef PATHNAME_EXT83
  4073.       if (eq(ThePathname(pathname)->pathname_name,S(Kwild))) # Name = :WILD ?
  4074.         return TRUE;
  4075.       #endif
  4076.       #if defined(PATHNAME_NOEXT) || defined(PATHNAME_RISCOS)
  4077.       { var reg1 object name = ThePathname(pathname)->pathname_name;
  4078.         if (simple_string_p(name))
  4079.           { if (has_wildcards(name)) return TRUE; }
  4080.       }
  4081.       #endif
  4082.       return FALSE;
  4083.     }
  4084.  
  4085. # UP: Testet, ob die Type-Komponente eines Pathname Wildcards enthält.
  4086. # has_type_wildcards(pathname)
  4087. # > pathname: Pathname
  4088. # < ergebnis: TRUE wenn (PATHNAME-TYPE pathname) Wildcards enthält.
  4089.   local boolean has_type_wildcards (object pathname);
  4090.   local boolean has_type_wildcards(pathname)
  4091.     var reg2 object pathname;
  4092.     { # Typ überprüfen:
  4093.       #ifdef LOGICAL_PATHNAMES
  4094.       if (logpathnamep(pathname))
  4095.         { var reg1 object type = TheLogpathname(pathname)->pathname_type;
  4096.           if (simple_string_p(type))
  4097.             { if (has_word_wildcards(type)) return TRUE; }
  4098.             else
  4099.             { if (eq(type,S(Kwild))) return TRUE; }
  4100.           return FALSE;
  4101.         }
  4102.       #endif
  4103.       #ifdef PATHNAME_EXT83
  4104.       if (eq(ThePathname(pathname)->pathname_type,S(Kwild))) # Typ = :WILD ?
  4105.         return TRUE;
  4106.       #endif
  4107.       #if defined(PATHNAME_NOEXT) || defined(PATHNAME_RISCOS)
  4108.       { var reg1 object type = ThePathname(pathname)->pathname_type;
  4109.         if (simple_string_p(type))
  4110.           { if (has_wildcards(type)) return TRUE; }
  4111.       }
  4112.       #endif
  4113.       return FALSE;
  4114.     }
  4115.  
  4116. # UP: Testet, ob die Version-Komponente eines Pathname Wildcards enthält.
  4117. # has_version_wildcards(pathname)
  4118. # > pathname: Pathname
  4119. # < ergebnis: TRUE wenn (PATHNAME-VERSION pathname) Wildcards enthält.
  4120.   local boolean has_version_wildcards (object pathname);
  4121.   local boolean has_version_wildcards(pathname)
  4122.     var reg2 object pathname;
  4123.     { # Version überprüfen:
  4124.       #ifdef LOGICAL_PATHNAMES
  4125.       if (logpathnamep(pathname))
  4126.         { if (eq(TheLogpathname(pathname)->pathname_version,S(Kwild)))
  4127.             return TRUE;
  4128.           return FALSE;
  4129.         }
  4130.       #endif
  4131.       return FALSE;
  4132.     }
  4133.  
  4134. # UP: Testet, ob irgendeine Komponente eines Pathname Wildcards enthält.
  4135. # has_some_wildcards(pathname)
  4136. # > pathname: Pathname
  4137. # < ergebnis: TRUE wenn pathname Wildcards enthält.
  4138.   local boolean has_some_wildcards (object pathname);
  4139.   local boolean has_some_wildcards(pathname)
  4140.     var reg1 object pathname;
  4141.     { if (has_host_wildcards(pathname)) return TRUE;
  4142.       if (has_device_wildcards(pathname)) return TRUE;
  4143.       if (has_directory_wildcards(pathname)) return TRUE;
  4144.       if (has_name_wildcards(pathname)) return TRUE;
  4145.       if (has_type_wildcards(pathname)) return TRUE;
  4146.       if (has_version_wildcards(pathname)) return TRUE;
  4147.       return FALSE;
  4148.     }
  4149.  
  4150. # UP: Überprüft, ob ein Pathname keine Wildcards enthält.
  4151. # check_no_wildcards(pathname);
  4152. # > pathname: Pathname
  4153.   local void check_no_wildcards (object pathname);
  4154.   local void check_no_wildcards(pathname)
  4155.     var reg2 object pathname;
  4156.     { if (!has_some_wildcards(pathname))
  4157.         # Keine Wildcards gefunden.
  4158.         return;
  4159.       # Fehlermeldung, wenn der Pathname Wildcards enthält:
  4160.       pushSTACK(pathname); # Wert für Slot PATHNAME von FILE-ERROR
  4161.       pushSTACK(pathname);
  4162.       //: DEUTSCH "Hier sind keine Wildcards (Dateiquantoren) erlaubt: ~"
  4163.       //: ENGLISH "wildcards are not allowed here: ~"
  4164.       //: FRANCAIS "Les caractères joker ne sont pas permis ici : ~"
  4165.       fehler(file_error, GETTEXT("wildcards are not allowed here: ~"));
  4166.     }
  4167.  
  4168. LISPFUN(wild_pathname_p,1,1,norest,nokey,0,NIL)
  4169. # (WILD-PATHNAME-P pathname [field-key]), CLtL2 S. 623
  4170.   { var reg3 object pathname = coerce_xpathname(STACK_1);
  4171.     var reg1 object key = STACK_0;
  4172.     var reg2 boolean erg;
  4173.     if (eq(key,unbound) || nullp(key)) { erg = has_some_wildcards(pathname); }
  4174.     elif (eq(key,S(Khost))) { erg = has_host_wildcards(pathname); }
  4175.     elif (eq(key,S(Kdevice))) { erg = has_device_wildcards(pathname); }
  4176.     elif (eq(key,S(Kdirectory))) { erg = has_directory_wildcards(pathname); }
  4177.     elif (eq(key,S(Kname))) { erg = has_name_wildcards(pathname); }
  4178.     elif (eq(key,S(Ktype))) { erg = has_type_wildcards(pathname); }
  4179.     elif (eq(key,S(Kversion))) { erg = has_version_wildcards(pathname); }
  4180.     else
  4181.       { pushSTACK(key); # Wert für Slot DATUM von TYPE-ERROR
  4182.         pushSTACK(O(type_pathname_field_key)); # Wert für Slot EXPECTED-TYPE von TYPE-ERROR
  4183.         pushSTACK(NIL);
  4184.         pushSTACK(S(Kversion));
  4185.         pushSTACK(S(Ktype));
  4186.         pushSTACK(S(Kname));
  4187.         pushSTACK(S(Kdirectory));
  4188.         pushSTACK(S(Kdevice));
  4189.         pushSTACK(S(Khost));
  4190.         pushSTACK(key);
  4191.         pushSTACK(TheSubr(subr_self)->name);
  4192.         //: DEUTSCH "~: Argument ~ sollte ~, ~, ~, ~, ~, ~ oder ~ sein."
  4193.         //: ENGLISH "~: argument ~ should be ~, ~, ~, ~, ~, ~ or ~"
  4194.         //: FRANCAIS "~ : L'argument ~ devrait être ~, ~, ~, ~, ~, ~ ou ~ ."
  4195.         fehler(type_error, GETTEXT("~: argument ~ should be ~, ~, ~, ~, ~, ~ or ~"));
  4196.       }
  4197.     value1 = (erg ? T : NIL); mv_count=1; # boolescher Wert
  4198.     skipSTACK(2);
  4199.   }
  4200.  
  4201. #if defined(PATHNAME_NOEXT) || defined(LOGICAL_PATHNAMES)
  4202.  
  4203.   # UP: Matcht einen Wildcard-String ("Muster") mit einem "Beispiel".
  4204.   # > muster: Simple-String, mit Platzhaltern
  4205.   #           '?' für genau 1 Zeichen
  4206.   #           '*' für beliebig viele Zeichen
  4207.   # > beispiel: Simple-String, der damit zu matchen ist
  4208.   local boolean wildcard_match (object muster, object beispiel);
  4209.   # rekursive Implementation wegen Backtracking:
  4210.   local boolean wildcard_match_ab (uintL m_count, uintB* m_ptr, uintL b_count, uintB* b_ptr);
  4211.   local boolean wildcard_match(muster,beispiel)
  4212.     var reg2 object muster;
  4213.     var reg1 object beispiel;
  4214.     { return wildcard_match_ab(
  4215.                                /* m_count = */ TheSstring(muster)->length,
  4216.                                /* m_ptr   = */ &TheSstring(muster)->data[0],
  4217.                                /* b_count = */ TheSstring(beispiel)->length,
  4218.                                /* b_ptr   = */ &TheSstring(beispiel)->data[0]
  4219.                               );
  4220.     }
  4221.   local boolean wildcard_match_ab(m_count,m_ptr,b_count,b_ptr)
  4222.     var reg5 uintL m_count;
  4223.     var reg2 uintB* m_ptr;
  4224.     var reg4 uintL b_count;
  4225.     var reg1 uintB* b_ptr;
  4226.     { var reg3 uintB c;
  4227.       loop
  4228.         { if (m_count==0)
  4229.             { return (b_count==0 ? TRUE : FALSE); } # "" matcht nur ""
  4230.           m_count--;
  4231.           c = *m_ptr++; # nächstes Match-Zeichen
  4232.           if (c=='?') # Wildcard '?'
  4233.             { if (b_count==0) return FALSE; # mindestens ein Zeichen muß noch kommen
  4234.               b_count--; b_ptr++; # es wird ignoriert
  4235.             }
  4236.           elif (c=='*') break; # Wildcard '*' später
  4237.           else # alles andere muß genau matchen:
  4238.             { if (b_count==0) return FALSE;
  4239.               b_count--; if (!equal_pathchar(*b_ptr++,c)) return FALSE;
  4240.             }
  4241.         }
  4242.       # Wildcard '*': Suche nächstes non-Wildcard-Zeichen und zähle die '?'
  4243.       # mit (denn eine Folge '*??*???***?' matcht alles, was mindestens so
  4244.       # lang ist, wie die Folge Fragezeichen enthält). Man kann die '?' auch
  4245.       # gleich verwerten, denn '*??*???***?' ist zu '??????*' äquivalent.
  4246.       loop
  4247.         { if (m_count==0) return TRUE; # Wildcard am Ende matcht den Rest.
  4248.           m_count--;
  4249.           c = *m_ptr++; # nächstes Match-Zeichen
  4250.           if (c=='?') # Fragezeichen: nach vorne ziehen, sofort abarbeiten
  4251.             { if (b_count==0) return FALSE;
  4252.               b_count--; b_ptr++;
  4253.             }
  4254.           elif (!(c=='*')) break;
  4255.         }
  4256.       # c = nächstes non-Wildcard-Zeichen. Suche es.
  4257.       loop
  4258.         { if (b_count==0) return FALSE; # c nicht gefunden
  4259.           b_count--;
  4260.           if (equal_pathchar(*b_ptr++,c))
  4261.             { if (wildcard_match_ab(m_count,m_ptr,b_count,b_ptr))
  4262.                 return TRUE;
  4263.         }   }
  4264.     }
  4265.  
  4266. #endif
  4267.  
  4268. # UPs: Matcht jeweils eine Pathname-Komponente ("Beispiel") und
  4269. # eine Pathname-Komponente ("Muster").
  4270.   local boolean host_match (object muster, object beispiel, boolean logical);
  4271.   local boolean device_match (object muster, object beispiel, boolean logical);
  4272.   local boolean directory_match (object muster, object beispiel, boolean logical);
  4273.   local boolean nametype_match (object muster, object beispiel, boolean logical);
  4274.   local boolean version_match (object muster, object beispiel, boolean logical);
  4275.   local boolean host_match(muster,beispiel,logical)
  4276.     var reg2 object muster;
  4277.     var reg1 object beispiel;
  4278.     var reg3 boolean logical;
  4279.     {
  4280.       #ifdef LOGICAL_PATHNAMES
  4281.       if (logical)
  4282.         { return equal(muster,beispiel); }
  4283.       #endif
  4284.       #if HAS_HOST
  4285.       return equal(muster,beispiel);
  4286.       #else
  4287.       return TRUE;
  4288.       #endif
  4289.     }
  4290.   local boolean device_match(muster,beispiel,logical)
  4291.     var reg2 object muster;
  4292.     var reg1 object beispiel;
  4293.     var reg3 boolean logical;
  4294.     {
  4295.       #if HAS_DEVICE
  4296.       #ifdef LOGICAL_PATHNAMES
  4297.       if (logical)
  4298.         { return TRUE; }
  4299.       #endif
  4300.       #if defined(PATHNAME_MSDOS) || defined(PATHNAME_OS2)
  4301.       if (eq(muster,S(Kwild))) return TRUE;
  4302.       if (eq(beispiel,S(Kwild))) return FALSE;
  4303.       #endif
  4304.       #if defined(PATHNAME_AMIGAOS) || defined(PATHNAME_OS2)
  4305.       return equalp(muster,beispiel);
  4306.       #else
  4307.       return equal(muster,beispiel);
  4308.       #endif
  4309.       #else
  4310.       return TRUE;
  4311.       #endif
  4312.     }
  4313.   local boolean nametype_match(muster,beispiel,logical)
  4314.     var reg2 object muster;
  4315.     var reg1 object beispiel;
  4316.     var reg3 boolean logical;
  4317.     {
  4318.       #ifdef LOGICAL_PATHNAMES
  4319.       if (logical)
  4320.         { if (eq(muster,S(Kwild))) return TRUE;
  4321.           if (eq(beispiel,S(Kwild))) return FALSE;
  4322.           if (nullp(muster))
  4323.             { if (nullp(beispiel)) return TRUE; else return FALSE; }
  4324.           if (nullp(beispiel))
  4325.             { return FALSE; }
  4326.           return wildcard_match(muster,beispiel);
  4327.         }
  4328.       #endif
  4329.       #ifdef PATHNAME_EXT83
  4330.       if (eq(muster,S(Kwild))) return TRUE;
  4331.       if (eq(beispiel,S(Kwild))) return FALSE;
  4332.       return equal(muster,beispiel);
  4333.       #endif
  4334.       #ifdef PATHNAME_NOEXT
  4335.       if (nullp(muster))
  4336.         { if (nullp(beispiel)) return TRUE; else return FALSE; }
  4337.       if (nullp(beispiel))
  4338.         { return FALSE; }
  4339.       return wildcard_match(muster,beispiel);
  4340.       #endif
  4341.     }
  4342.   local boolean subdir_match(muster,beispiel,logical)
  4343.     var reg2 object muster;
  4344.     var reg1 object beispiel;
  4345.     var reg3 boolean logical;
  4346.     { if (eq(muster,beispiel)) return TRUE;
  4347.       #ifdef LOGICAL_PATHNAMES
  4348.       if (logical)
  4349.         { if (eq(muster,S(Kwild))) return TRUE;
  4350.           if (!simple_string_p(muster) || !simple_string_p(beispiel)) return FALSE;
  4351.           return wildcard_match(muster,beispiel);
  4352.         }
  4353.       #endif
  4354.       #ifdef PATHNAME_EXT83
  4355.       if (atomp(muster) || atomp(beispiel)) return FALSE;
  4356.       return (nametype_match(Car(muster),Car(beispiel),FALSE)
  4357.               && nametype_match(Cdr(muster),Cdr(beispiel),FALSE)
  4358.              );
  4359.       #endif
  4360.       #ifdef PATHNAME_NOEXT
  4361.       if (!simple_string_p(muster) || !simple_string_p(beispiel)) return FALSE;
  4362.       return wildcard_match(muster,beispiel);
  4363.       #endif
  4364.     }
  4365.   # rekursive Implementation wegen Backtracking:
  4366.   local boolean directory_match_ab (object m_list, object b_list, boolean logical);
  4367.   local boolean directory_match_ab(m_list,b_list,logical)
  4368.     var reg2 object m_list;
  4369.     var reg1 object b_list;
  4370.     var reg4 boolean logical;
  4371.     { # Algorithmus analog zu wildcard_match_ab.
  4372.       var reg3 object item;
  4373.       loop
  4374.         { if (atomp(m_list)) { return atomp(b_list); }
  4375.           item = Car(m_list); m_list = Cdr(m_list);
  4376.           if (eq(item,S(Kwild_inferiors))) break;
  4377.           if (atomp(b_list)) return FALSE;
  4378.           if (!subdir_match(item,Car(b_list),logical)) return FALSE;
  4379.           b_list = Cdr(b_list);
  4380.         }
  4381.       loop
  4382.         { if (atomp(m_list)) return TRUE;
  4383.           item = Car(m_list); m_list = Cdr(m_list);
  4384.           if (!eq(item,S(Kwild_inferiors))) break;
  4385.         }
  4386.       loop
  4387.         { if (atomp(b_list)) return FALSE;
  4388.           if (subdir_match(item,Car(b_list),logical))
  4389.             { b_list = Cdr(b_list);
  4390.               if (directory_match_ab(m_list,b_list,logical)) return TRUE;
  4391.             }
  4392.             else
  4393.             { b_list = Cdr(b_list); }
  4394.         }
  4395.     }
  4396.   local boolean directory_match(muster,beispiel,logical)
  4397.     var reg2 object muster;
  4398.     var reg1 object beispiel;
  4399.     var reg3 boolean logical;
  4400.     {
  4401.       # Startpoint matchen:
  4402.       if (!eq(Car(muster),Car(beispiel)))
  4403.         return FALSE;
  4404.       muster = Cdr(muster); beispiel = Cdr(beispiel);
  4405.       # subdirs matchen:
  4406.       return directory_match_ab(muster,beispiel,logical);
  4407.     }
  4408.   local boolean version_match(muster,beispiel,logical)
  4409.     var reg2 object muster;
  4410.     var reg1 object beispiel;
  4411.     var reg3 boolean logical;
  4412.     {
  4413.       #ifdef LOGICAL_PATHNAMES
  4414.       if (logical)
  4415.         { if (eq(muster,S(Kwild))) return TRUE;
  4416.           return eql(muster,beispiel);
  4417.         }
  4418.       #endif
  4419.       #if HAS_VERSION
  4420.       if (eq(muster,S(Kwild))) return TRUE;
  4421.       if (eq(beispiel,S(Kwild))) return FALSE;
  4422.       if (eql(muster,beispiel)) return TRUE;
  4423.       return FALSE;
  4424.       #else
  4425.       return TRUE;
  4426.       #endif
  4427.     }
  4428.  
  4429. LISPFUNN(pathname_match_p,2)
  4430. # (PATHNAME-MATCH-P pathname wildname), CLtL2 S. 623
  4431.   { # Stackaufbau: pathname, wildname.
  4432.     var reg3 boolean logical = FALSE;
  4433.     STACK_1 = coerce_xpathname(STACK_1);
  4434.     STACK_0 = coerce_xpathname(STACK_0);
  4435.     #ifdef LOGICAL_PATHNAMES
  4436.     if (logpathnamep(STACK_1) && logpathnamep(STACK_0))
  4437.       { logical = TRUE; }
  4438.       else
  4439.       # nicht beides logische Pathnames -> erst in normale Pathnames umwandeln:
  4440.       { STACK_1 = coerce_pathname(STACK_1);
  4441.         STACK_0 = coerce_pathname(STACK_0);
  4442.       }
  4443.     #endif
  4444.    {var reg2 object wildname = popSTACK();
  4445.     var reg1 object pathname = popSTACK();
  4446.     if (!host_match(xpathname_host(logical,wildname),
  4447.                     xpathname_host(logical,pathname),
  4448.                     logical
  4449.        )           )
  4450.       goto no;
  4451.     if (!device_match(xpathname_device(logical,wildname),
  4452.                       xpathname_device(logical,pathname),
  4453.                       logical
  4454.        )             )
  4455.       goto no;
  4456.     if (!directory_match(xpathname_directory(logical,wildname),
  4457.                          xpathname_directory(logical,pathname),
  4458.                          logical
  4459.        )                )
  4460.       goto no;
  4461.     if (!nametype_match(xpathname_name(logical,wildname),
  4462.                         xpathname_name(logical,pathname),
  4463.                         logical
  4464.        )               )
  4465.       goto no;
  4466.     if (!nametype_match(xpathname_type(logical,wildname),
  4467.                         xpathname_type(logical,pathname),
  4468.                         logical
  4469.        )               )
  4470.       goto no;
  4471.     if (!version_match(xpathname_version(logical,wildname),
  4472.                        xpathname_version(logical,pathname),
  4473.                        logical
  4474.        )              )
  4475.       goto no;
  4476.     yes: value1 = T; mv_count=1; return;
  4477.     no: value1 = NIL; mv_count=1; return;
  4478.   }}
  4479.  
  4480. # (TRANSLATE-PATHNAME beispiel muster1 muster2) machen wir folgendermaßen:
  4481. # 1. (PATHNAME-MATCH-P beispiel muster1) nachrechnen, dabei aber die
  4482. #    Substitution aufheben, in Form von Textstücken (:WILD -> "*").
  4483. # 2. In muster2 die Textstücke einsetzen, bis muster2 voll ist oder die
  4484. #    Textstücke aufgebraucht sind.
  4485. # 3. Zum Schluß (MERGE-PATHNAMES modifiziertes_muster2 beispiel).
  4486.  
  4487.   # UP: Vergleicht einen Wildcard-String ("Muster") mit einem "Beispiel".
  4488.   # wildcard_diff(muster,beispiel,previous,solutions);
  4489.   # > muster: Simple-String, mit Platzhaltern
  4490.   #           '?' für genau 1 Zeichen
  4491.   #           '*' für beliebig viele Zeichen
  4492.   # > beispiel: Simple-String, der damit zu vergleichen ist
  4493.   # > previous: bisher bekanntes Vergleichsergebnis
  4494.   #             (umgedrehte Liste von Simple-Strings und Listen)
  4495.   # > solutions: Pointer auf eine Liste im STACK, auf die die
  4496.   #              Vergleichsergebnisse (umgedrehte Liste von Simple-Strings und
  4497.   #              Listen) zu consen sind
  4498.   # kann GC auslösen
  4499.  
  4500.   # Hier wünscht man sich nicht Lisp oder C, sondern PROLOG als Sprache!
  4501.  
  4502.   #define push_solution()  \
  4503.     { var reg1 object new_cons = allocate_cons(); \
  4504.       Car(new_cons) = *previous;                  \
  4505.       Cdr(new_cons) = *solutions;                 \
  4506.       *solutions = new_cons;                      \
  4507.     }
  4508.   #define push_solution_with(new_piece)  \
  4509.     { pushSTACK(new_piece);                                   \
  4510.      {var reg1 object new_cons = allocate_cons();             \
  4511.       Car(new_cons) = STACK_0; Cdr(new_cons) = *previous;     \
  4512.       STACK_0 = new_cons;                                     \
  4513.       new_cons = allocate_cons();                             \
  4514.       Car(new_cons) = popSTACK(); Cdr(new_cons) = *solutions; \
  4515.       *solutions = new_cons;                                  \
  4516.     }}
  4517.  
  4518. #if defined(PATHNAME_NOEXT) || defined(LOGICAL_PATHNAMES)
  4519.  
  4520.   local void wildcard_diff (object muster, object beispiel, object* previous, object* solutions);
  4521.  
  4522.   # rekursive Implementation wegen Backtracking:
  4523.   local void wildcard_diff_ab (object muster, object beispiel, uintL m_index, uintL b_index, object* previous, object* solutions);
  4524.  
  4525.   local void wildcard_diff(muster,beispiel,previous,solutions)
  4526.     var reg2 object muster;
  4527.     var reg1 object beispiel;
  4528.     var reg4 object* previous;
  4529.     var reg3 object* solutions;
  4530.     { wildcard_diff_ab(muster,beispiel,0,0,previous,solutions); }
  4531.  
  4532.   local void wildcard_diff_ab(muster,beispiel,m_index,b_index,previous,solutions)
  4533.     var reg3 object muster;
  4534.     var reg2 object beispiel;
  4535.     var reg6 uintL m_index;
  4536.     var reg5 uintL b_index;
  4537.     var reg8 object* previous;
  4538.     var reg7 object* solutions;
  4539.     { var reg4 uintB c;
  4540.       loop
  4541.         { if (m_index == TheSstring(muster)->length)
  4542.             { if (b_index == TheSstring(beispiel)->length)
  4543.                 { push_solution(); }
  4544.               return;
  4545.             }
  4546.           c = TheSstring(muster)->data[m_index++];
  4547.           if (c=='*') break;
  4548.           if (b_index == TheSstring(beispiel)->length) return;
  4549.           if (c=='?')
  4550.             { # wildcard_diff_ab() rekursiv aufrufen, mit erweitertem previous:
  4551.               c = TheSstring(beispiel)->data[b_index++];
  4552.               pushSTACK(muster); pushSTACK(beispiel);
  4553.               { var reg1 object new_string = allocate_string(1);
  4554.                 TheSstring(new_string)->data[0] = c;
  4555.                 pushSTACK(new_string);
  4556.               }
  4557.               { var reg1 object new_cons = allocate_cons();
  4558.                 Car(new_cons) = STACK_0; Cdr(new_cons) = *previous;
  4559.                 STACK_0 = new_cons; # (CONS ... previous)
  4560.               }
  4561.               wildcard_diff_ab(STACK_2,STACK_1,m_index,b_index,&STACK_0,solutions);
  4562.               skipSTACK(3);
  4563.               return;
  4564.             }
  4565.             else
  4566.             { if (!equal_pathchar(TheSstring(beispiel)->data[b_index++],c))
  4567.                 return;
  4568.             }
  4569.         }
  4570.      {var reg9 uintL b_start_index = b_index;
  4571.       loop
  4572.         { # Um weniger zu consen, die Fälle abfangen, wo wildcard_diff_ab()
  4573.           # gar nichts tut:
  4574.           if (m_index == TheSstring(muster)->length
  4575.               ? b_index == TheSstring(beispiel)->length
  4576.               : (c = TheSstring(muster)->data[m_index],
  4577.                  (c=='*') || (c=='?')
  4578.                  || (b_index < TheSstring(beispiel)->length
  4579.                      && equal_pathchar(TheSstring(beispiel)->data[b_index],c)
  4580.              )  )   )
  4581.             # wildcard_diff_ab() rekursiv aufrufen, mit erweitertem previous:
  4582.             { pushSTACK(muster); pushSTACK(beispiel);
  4583.               pushSTACK(beispiel); pushSTACK(fixnum(b_start_index)); pushSTACK(fixnum(b_index));
  4584.               funcall(L(substring),3); # (SUBSTRING beispiel b_start_index b_index)
  4585.               pushSTACK(value1);
  4586.              {var reg1 object new_cons = allocate_cons();
  4587.               Car(new_cons) = STACK_0; Cdr(new_cons) = *previous;
  4588.               STACK_0 = new_cons; # (CONS ... previous)
  4589.               wildcard_diff_ab(STACK_2,STACK_1,m_index,b_index,&STACK_0,solutions);
  4590.               skipSTACK(1);
  4591.               beispiel = popSTACK(); muster = popSTACK();
  4592.             }}
  4593.           if (b_index == TheSstring(beispiel)->length)
  4594.             break;
  4595.           b_index++;
  4596.         }
  4597.     }}
  4598.  
  4599. #endif
  4600.  
  4601. # UPs: Vergleicht jeweils eine Pathname-Komponente ("Beispiel") und
  4602. # eine Pathname-Komponente ("Muster").
  4603. # kann GC auslösen
  4604.   local void host_diff (object muster, object beispiel, boolean logical, object* previous, object* solutions);
  4605.   local void device_diff (object muster, object beispiel, boolean logical, object* previous, object* solutions);
  4606.   local void directory_diff (object muster, object beispiel, boolean logical, object* previous, object* solutions);
  4607.   local void nametype_diff (object muster, object beispiel, boolean logical, object* previous, object* solutions);
  4608.   local void version_diff (object muster, object beispiel, boolean logical, object* previous, object* solutions);
  4609.   local void host_diff(muster,beispiel,logical,previous,solutions)
  4610.     var reg2 object muster;
  4611.     var reg1 object beispiel;
  4612.     var reg5 boolean logical;
  4613.     var reg4 object* previous;
  4614.     var reg3 object* solutions;
  4615.     {
  4616.       #ifdef LOGICAL_PATHNAMES
  4617.       if (logical)
  4618.         { if (!equal(muster,beispiel)) return; }
  4619.         else
  4620.       #endif
  4621.         {
  4622.           #if HAS_HOST
  4623.           if (!equal(muster,beispiel)) return;
  4624.           #endif
  4625.         }
  4626.       push_solution();
  4627.     }
  4628.   local void device_diff(muster,beispiel,logical,previous,solutions)
  4629.     var reg2 object muster;
  4630.     var reg1 object beispiel;
  4631.     var reg5 boolean logical;
  4632.     var reg4 object* previous;
  4633.     var reg3 object* solutions;
  4634.     {
  4635.       #if HAS_DEVICE
  4636.       #ifdef LOGICAL_PATHNAMES
  4637.       if (logical)
  4638.         { push_solution(); return; }
  4639.       #endif
  4640.       #if defined(PATHNAME_MSDOS) || defined(PATHNAME_OS2)
  4641.       if (eq(muster,S(Kwild)))
  4642.         { var reg1 object string =
  4643.             (eq(beispiel,S(Kwild)) ? O(wild_string) :
  4644.              simple_string_p(beispiel) ? beispiel : O(leer_string)
  4645.             );
  4646.           push_solution_with(string);
  4647.           return;
  4648.         }
  4649.       if (eq(beispiel,S(Kwild))) return;
  4650.       #endif
  4651.       #if defined(PATHNAME_AMIGAOS) || defined(PATHNAME_OS2)
  4652.       if (!equalp(muster,beispiel)) return;
  4653.       #else
  4654.       if (!equal(muster,beispiel)) return;
  4655.       #endif
  4656.       #endif
  4657.       push_solution();
  4658.     }
  4659.   local void nametype_diff(muster,beispiel,logical,previous,solutions)
  4660.     var reg2 object muster;
  4661.     var reg1 object beispiel;
  4662.     var reg5 boolean logical;
  4663.     var reg4 object* previous;
  4664.     var reg3 object* solutions;
  4665.     {
  4666.       #ifdef LOGICAL_PATHNAMES
  4667.       if (logical)
  4668.         { if (eq(muster,S(Kwild)))
  4669.             { var reg1 object string =
  4670.                 (eq(beispiel,S(Kwild)) ? O(wild_string) :
  4671.                  simple_string_p(beispiel) ? beispiel : O(leer_string)
  4672.                 );
  4673.               push_solution_with(string);
  4674.               return;
  4675.             }
  4676.           if (eq(beispiel,S(Kwild))) return;
  4677.           if (nullp(muster))
  4678.             { if (nullp(beispiel))
  4679.                 { push_solution(); }
  4680.               return;
  4681.             }
  4682.           if (nullp(beispiel))
  4683.             return;
  4684.           wildcard_diff(muster,beispiel,previous,solutions);
  4685.           return;
  4686.         }
  4687.       #endif
  4688.       #ifdef PATHNAME_EXT83
  4689.       if (eq(muster,S(Kwild)))
  4690.         { var reg1 object string =
  4691.             (eq(beispiel,S(Kwild)) ? O(wild_string) :
  4692.              simple_string_p(beispiel) ? beispiel : O(leer_string)
  4693.             );
  4694.           push_solution_with(string);
  4695.           return;
  4696.         }
  4697.       if (eq(beispiel,S(Kwild))) return;
  4698.       if (!equal(muster,beispiel)) return;
  4699.       push_solution();
  4700.       #endif
  4701.       #ifdef PATHNAME_NOEXT
  4702.       if (nullp(muster))
  4703.         { if (nullp(beispiel))
  4704.             { push_solution(); }
  4705.           return;
  4706.         }
  4707.       if (nullp(beispiel))
  4708.         return;
  4709.       wildcard_diff(muster,beispiel,previous,solutions);
  4710.       #endif
  4711.     }
  4712.   local void subdir_diff(muster,beispiel,logical,previous,solutions)
  4713.     var reg2 object muster;
  4714.     var reg1 object beispiel;
  4715.     var reg5 boolean logical;
  4716.     var reg4 object* previous;
  4717.     var reg3 object* solutions;
  4718.     { if (eq(muster,beispiel))
  4719.         { if (eq(beispiel,S(Kwild)))
  4720.             { push_solution_with(O(wild_string)); }
  4721.             else
  4722.             { push_solution(); }
  4723.           return;
  4724.         }
  4725.       #ifdef LOGICAL_PATHNAMES
  4726.       if (logical)
  4727.         { if (eq(muster,S(Kwild)))
  4728.             { var reg1 object string =
  4729.                 (eq(beispiel,S(Kwild)) ? O(wild_string) :
  4730.                  simple_string_p(beispiel) ? beispiel : O(leer_string)
  4731.                 );
  4732.               push_solution_with(string);
  4733.               return;
  4734.             }
  4735.           if (eq(beispiel,S(Kwild))) return;
  4736.           if (!simple_string_p(muster) || !simple_string_p(beispiel)) return;
  4737.           wildcard_diff(muster,beispiel,previous,solutions);
  4738.           return;
  4739.         }
  4740.       #endif
  4741.       #ifdef PATHNAME_EXT83
  4742.       if (atomp(muster) || atomp(beispiel)) return;
  4743.       pushSTACK(NIL); pushSTACK(Cdr(muster)); pushSTACK(Cdr(beispiel));
  4744.       nametype_diff(Car(muster),Car(beispiel),FALSE,previous,&STACK_2);
  4745.       while (mconsp(STACK_2))
  4746.         { pushSTACK(Car(STACK_2));
  4747.           nametype_diff(STACK_(1+1),STACK_(0+1),FALSE,&STACK_0,solutions);
  4748.           skipSTACK(1);
  4749.           STACK_2 = Cdr(STACK_2);
  4750.         }
  4751.       skipSTACK(3);
  4752.       #endif
  4753.       #ifdef PATHNAME_NOEXT
  4754.       if (!simple_string_p(muster) || !simple_string_p(beispiel)) return;
  4755.       wildcard_diff(muster,beispiel,previous,solutions);
  4756.       #endif
  4757.     }
  4758.   # rekursive Implementation wegen Backtracking:
  4759.   local void directory_diff_ab (object m_list, object b_list, boolean logical, object* previous, object* solutions);
  4760.   local void directory_diff_ab(m_list,b_list,logical,previous,solutions)
  4761.     var reg3 object m_list;
  4762.     var reg2 object b_list;
  4763.     var reg8 boolean logical;
  4764.     var reg6 object* previous;
  4765.     var reg7 object* solutions;
  4766.     { # Algorithmus analog zu wildcard_diff_ab.
  4767.       var reg4 object item;
  4768.       if (atomp(m_list))
  4769.         { if (atomp(b_list))
  4770.             { push_solution(); }
  4771.           return;
  4772.         }
  4773.       item = Car(m_list); m_list = Cdr(m_list);
  4774.       if (!eq(item,S(Kwild_inferiors)))
  4775.         { if (atomp(b_list)) return;
  4776.           pushSTACK(NIL); pushSTACK(m_list); pushSTACK(Cdr(b_list));
  4777.           subdir_diff(item,Car(b_list),logical,previous,&STACK_2);
  4778.           # directory_diff_ab() rekursiv aufrufen, mit erweitertem previous:
  4779.           while (mconsp(STACK_2))
  4780.             { pushSTACK(Car(STACK_2));
  4781.               directory_diff_ab(STACK_(1+1),STACK_(0+1),logical,&STACK_0,solutions);
  4782.               skipSTACK(1);
  4783.               STACK_2 = Cdr(STACK_2);
  4784.             }
  4785.           skipSTACK(3);
  4786.         }
  4787.         else
  4788.         { pushSTACK(b_list); # b_start_list := b_list
  4789.           loop
  4790.             { # Um weniger zu consen, die Fälle abfangen, wo directory_diff_ab()
  4791.               # gar nichts tut:
  4792.               if (atomp(m_list)
  4793.                   ? atomp(b_list)
  4794.                   : (eq(Car(m_list),S(Kwild_inferiors)) || !atomp(b_list))
  4795.                  )
  4796.                 # directory_diff_ab() rekursiv aufrufen, mit erweitertem previous:
  4797.                 { pushSTACK(m_list); pushSTACK(b_list);
  4798.                   pushSTACK(STACK_2); pushSTACK(b_list);
  4799.                   funcall(L(ldiff),2); # (LDIFF b_start_list b_list)
  4800.                   pushSTACK(value1);
  4801.                  {var reg1 object new_cons = allocate_cons();
  4802.                   Car(new_cons) = STACK_0; Cdr(new_cons) = *previous;
  4803.                   STACK_0 = new_cons; # (CONS ... previous)
  4804.                   directory_diff_ab(STACK_2,STACK_1,logical,&STACK_0,solutions);
  4805.                   skipSTACK(1);
  4806.                   b_list = popSTACK(); m_list = popSTACK();
  4807.                 }}
  4808.               if (atomp(b_list)) break;
  4809.               b_list = Cdr(b_list);
  4810.             }
  4811.           skipSTACK(1);
  4812.         }
  4813.     }
  4814.   local void directory_diff(muster,beispiel,logical,previous,solutions)
  4815.     var reg2 object muster;
  4816.     var reg1 object beispiel;
  4817.     var reg5 boolean logical;
  4818.     var reg4 object* previous;
  4819.     var reg3 object* solutions;
  4820.     {
  4821.       # Startpoint vergleichen:
  4822.       if (!eq(Car(muster),Car(beispiel)))
  4823.         return;
  4824.       muster = Cdr(muster); beispiel = Cdr(beispiel);
  4825.       # subdirs vergleichen:
  4826.       directory_diff_ab(muster,beispiel,logical,previous,solutions);
  4827.     }
  4828.   local void version_diff(muster,beispiel,logical,previous,solutions)
  4829.     var reg2 object muster;
  4830.     var reg1 object beispiel;
  4831.     var reg5 boolean logical;
  4832.     var reg4 object* previous;
  4833.     var reg3 object* solutions;
  4834.     {
  4835.       #ifdef LOGICAL_PATHNAMES
  4836.       if (logical)
  4837.         { if (eq(muster,S(Kwild)))
  4838.             { var reg1 object string =
  4839.                 (eq(beispiel,S(Kwild)) ? O(wild_string) :
  4840.                  integerp(beispiel) ? (pushSTACK(beispiel), C_decimal_string(), value1) : # (SYS::DECIMAL-STRING beispiel)
  4841.                  O(leer_string)
  4842.                 );
  4843.               push_solution_with(string);
  4844.               return;
  4845.             }
  4846.           if (eq(beispiel,S(Kwild))) return;
  4847.           if (!eql(muster,beispiel)) return;
  4848.           push_solution();
  4849.           return;
  4850.         }
  4851.       #endif
  4852.       #if HAS_VERSION
  4853.       if (eq(muster,S(Kwild)))
  4854.         { var reg1 object string =
  4855.             (eq(beispiel,S(Kwild)) ? O(wild_string) :
  4856.              integerp(beispiel) ? (pushSTACK(beispiel), C_decimal_string(), value1) : # (SYS::DECIMAL-STRING beispiel)
  4857.              O(leer_string)
  4858.             );
  4859.           push_solution_with(string);
  4860.           return;
  4861.         }
  4862.       if (eq(beispiel,S(Kwild))) return;
  4863.       if (!eql(muster,beispiel)) return;
  4864.       #endif
  4865.       push_solution();
  4866.     }
  4867.  
  4868.   #undef push_solution_with
  4869.   #undef push_solution
  4870.  
  4871. # Jede Substitution ist eine Liste von Simple-Strings oder Listen.
  4872. # (Die Listen entstehen bei :WILD-INFERIORS in directory_diff().)
  4873. # Ein Simple-String paßt nur auf '?' oder '*' oder :WILD,
  4874. # eine Liste paßt nur auf :WILD-INFERIORS.
  4875.  
  4876. #ifdef LOGICAL_PATHNAMES
  4877.  
  4878. # Beim Einsetzen von Stücken normaler Pathnames in logische Pathnames:
  4879. # Umwandlung in Großbuchstaben.
  4880. # logical_case(string)
  4881. # > string: Simple-String oder Symbol/Zahl
  4882. # < ergebnis: umgewandelter Simple-String oder dasselbe Symbol/Zahl
  4883. # kann GC auslösen
  4884.   local object logical_case (object string);
  4885. # Dasselbe, rekursiv wie mit SUBST:
  4886.   local object subst_logical_case (object obj);
  4887. #if defined(PATHNAME_MSDOS)
  4888.   # sowieso schon alles Großbuchstaben
  4889.   #define logical_case(string)  string
  4890.   #define subst_logical_case(obj)  obj
  4891. #else
  4892.   local object logical_case(string)
  4893.     var reg1 object string;
  4894.     { if (!simple_string_p(string))
  4895.         return string;
  4896.       return string_upcase(string);
  4897.     }
  4898.   local object subst_logical_case(obj)
  4899.     var reg1 object obj;
  4900.     { if (atomp(obj))
  4901.         { return logical_case(obj); }
  4902.       check_STACK(); check_SP();
  4903.       pushSTACK(obj);
  4904.       # rekursiv für den CAR aufrufen:
  4905.       { var reg2 object new_car = subst_logical_case(Car(obj));
  4906.         pushSTACK(new_car);
  4907.       }
  4908.       # rekursiv für den CDR aufrufen:
  4909.       { var reg2 object new_cdr = subst_logical_case(Cdr(STACK_1));
  4910.         if (eq(new_cdr,Cdr(STACK_1)) && eq(STACK_0,Car(STACK_1)))
  4911.           { obj = STACK_1; skipSTACK(2); return obj; }
  4912.           else
  4913.           # (CONS new_car new_cdr)
  4914.           { STACK_1 = new_cdr;
  4915.            {var reg1 object new_cons = allocate_cons();
  4916.             Car(new_cons) = popSTACK(); Cdr(new_cons) = popSTACK();
  4917.             return new_cons;
  4918.     } }   }}
  4919. #endif
  4920.  
  4921. # Beim Einsetzen von Stücken logischer Pathnames in normale Pathnames:
  4922. # Umwandlung in Großbuchstaben.
  4923. # logical_case(string)
  4924. # > string: Simple-String oder Symbol/Zahl
  4925. # < ergebnis: umgewandelter Simple-String oder dasselbe Symbol/Zahl
  4926. # kann GC auslösen
  4927.   local object customary_case (object string);
  4928. # Dasselbe, rekursiv wie mit SUBST:
  4929.   local object subst_customary_case (object obj);
  4930. #if defined(PATHNAME_MSDOS)
  4931.   # Betriebssystem mit Vorzug für Großbuchstaben
  4932.   #define customary_case(string)  string
  4933.   #define subst_customary_case(obj)  obj
  4934. #else
  4935.   local object customary_case(string)
  4936.     var reg1 object string;
  4937.     { if (!simple_string_p(string))
  4938.         return string;
  4939.       #if defined(PATHNAME_UNIX) || defined(PATHNAME_OS2) || defined(PATHNAME_RISCOS)
  4940.       # Betriebssystem mit Vorzug für Kleinbuchstaben
  4941.       return string_downcase(string);
  4942.       #endif
  4943.       #ifdef PATHNAME_AMIGAOS
  4944.       # Betriebssystem mit Vorzug für Capitalize
  4945.       string = copy_string(string);
  4946.       nstring_capitalize(&TheSstring(string)->data[0],TheSstring(string)->length);
  4947.       return string;
  4948.       #endif
  4949.     }
  4950.   local object subst_customary_case(obj)
  4951.     var reg1 object obj;
  4952.     { if (atomp(obj))
  4953.         { return customary_case(obj); }
  4954.       check_STACK(); check_SP();
  4955.       pushSTACK(obj);
  4956.       # rekursiv für den CAR aufrufen:
  4957.       { var reg2 object new_car = subst_customary_case(Car(obj));
  4958.         pushSTACK(new_car);
  4959.       }
  4960.       # rekursiv für den CDR aufrufen:
  4961.       { var reg2 object new_cdr = subst_customary_case(Cdr(STACK_1));
  4962.         if (eq(new_cdr,Cdr(STACK_1)) && eq(STACK_0,Car(STACK_1)))
  4963.           { obj = STACK_1; skipSTACK(2); return obj; }
  4964.           else
  4965.           # (CONS new_car new_cdr)
  4966.           { STACK_1 = new_cdr;
  4967.            {var reg1 object new_cons = allocate_cons();
  4968.             Car(new_cons) = popSTACK(); Cdr(new_cons) = popSTACK();
  4969.             return new_cons;
  4970.     } }   }}
  4971. #endif
  4972.  
  4973. #endif
  4974.  
  4975. # UP: Eine Substitution auf ein Muster anwenden.
  4976. # translate_pathname(&subst,muster)
  4977.   local object translate_pathname (object* subst, object muster, boolean for_logical_p);
  4978. # translate_host(&subst,muster,logical) etc. liefert den host etc. mit Ersetzungen
  4979. # und verkürzen subst passend. Falls nicht passend, liefert es nullobj.
  4980.   local object translate_host (object* subst, object muster, boolean logical);
  4981.   local object translate_device (object* subst, object muster, boolean logical);
  4982.   local object convert_subdir (object muster, boolean for_logical_p);
  4983.   local object translate_subdir (object* subst, object muster, boolean logical, boolean for_logical_p);
  4984.   local object translate_directory (object* subst, object muster, boolean logical, boolean for_logical_p);
  4985.   local object translate_nametype (object* subst, object muster, boolean logical);
  4986.   local object translate_version (object* subst, object muster, boolean logical);
  4987.   #define translate_host(subst,muster,logical)  (muster)
  4988.   local object translate_device(subst,muster,logical)
  4989.     var reg1 object* subst;
  4990.     var reg2 object muster;
  4991.     var reg4 boolean logical;
  4992.     {
  4993.       #if HAS_DEVICE
  4994.       #ifdef LOGICAL_PATHNAMES
  4995.       if (logical)
  4996.         { return muster; }
  4997.       #endif
  4998.       if (eq(muster,S(Kwild)) && mconsp(*subst))
  4999.         { if (m_simple_string_p(Car(*subst)))
  5000.             { var reg3 object erg = Car(*subst); *subst = Cdr(*subst);
  5001.               return erg;
  5002.             }
  5003.             else
  5004.             return nullobj;
  5005.         }
  5006.       #endif
  5007.       return muster;
  5008.     }
  5009.   local object translate_nametype(subst,muster,logical)
  5010.     var reg6 object* subst;
  5011.     var reg3 object muster;
  5012.     var reg7 boolean logical;
  5013.     { if (eq(muster,S(Kwild)) && mconsp(*subst))
  5014.         { if (m_simple_string_p(Car(*subst)))
  5015.             { var reg3 object erg = Car(*subst); *subst = Cdr(*subst);
  5016.               return erg;
  5017.             }
  5018.             else
  5019.             return nullobj;
  5020.         }
  5021.       if (simple_string_p(muster))
  5022.         { pushSTACK(muster); # muster retten
  5023.          {var reg5 object* muster_ = &STACK_0;
  5024.           var reg4 uintL len = TheSstring(muster)->length;
  5025.           var reg1 uintL index = 0;
  5026.           var reg7 uintL stringcount = 0; # Anzahl der Strings auf dem Stack
  5027.           loop
  5028.             { var reg9 uintL last_index = index;
  5029.               var reg2 uintB c;
  5030.               # Suche nächstes Wildcard-Zeichen:
  5031.               muster = *muster_;
  5032.               loop
  5033.                 { if (index == len) break;
  5034.                   c = TheSstring(muster)->data[index];
  5035.                   if (((c=='*') # Wildcard für beliebig viele Zeichen
  5036.                        || (!logical && singlewild_char_p(c)) # Wildcard für genau ein Zeichen
  5037.                       )
  5038.                       && mconsp(*subst)
  5039.                      )
  5040.                     break;
  5041.                   index++;
  5042.                 }
  5043.               # Nächsten Teilstring auf den Stack:
  5044.               pushSTACK(muster); pushSTACK(fixnum(last_index)); pushSTACK(fixnum(index));
  5045.               funcall(L(substring),3); # (SUBSTRING muster last_index index)
  5046.               pushSTACK(value1); stringcount++;
  5047.               # Fertig?
  5048.               if (index == len) break;
  5049.               # Wildcard ersetzen:
  5050.               if (m_simple_string_p(Car(*subst)))
  5051.                 { pushSTACK(Car(*subst)); *subst = Cdr(*subst); stringcount++; }
  5052.                 else
  5053.                 { skipSTACK(stringcount+1); return nullobj; }
  5054.               index++;
  5055.             }
  5056.           funcall(L(string_concat),stringcount); # (STRING-CONCAT alle Strings)
  5057.           skipSTACK(1);
  5058.           return value1;
  5059.         }}
  5060.       return muster;
  5061.     }
  5062.  
  5063.   # kann GC auslösen
  5064.   local object convert_subdir (object muster, boolean for_logical_p);
  5065.   local object convert_subdir(muster,for_logical_p)
  5066.     var reg2 object muster; 
  5067.     var reg3 boolean for_logical_p;
  5068.     {
  5069.       #ifdef PATHNAME_EXT83
  5070.       if (for_logical_p)
  5071.         { pushSTACK(muster);
  5072.           muster = allocate_cons();
  5073.           Car(muster) = popSTACK();
  5074.           Cdr(muster) = O(leer_string);
  5075.         }
  5076.       #endif
  5077.       return muster;
  5078.     }
  5079.  
  5080.   local object translate_subdir(subst,muster,logical,for_logical_p)
  5081.     var reg3 object* subst;
  5082.     var reg2 object muster;
  5083.     var reg4 boolean logical;
  5084.     var reg5 boolean for_logical_p;
  5085.     {
  5086.       #ifdef LOGICAL_PATHNAMES
  5087.       if (logical)
  5088.         { return translate_nametype(subst,muster,logical); }
  5089.       #endif
  5090.       #ifdef PATHNAME_EXT83
  5091.       if (atomp(muster) && !for_logical_p) return muster;
  5092.       muster = convert_subdir(muster,for_logical_p);
  5093.       pushSTACK(Car(muster)); pushSTACK(Cdr(muster));
  5094.       if (eq(STACK_1 = translate_nametype(subst,STACK_1,FALSE),nullobj)
  5095.           || eq(STACK_0 = translate_nametype(subst,STACK_0,FALSE),nullobj)
  5096.          )
  5097.         { skipSTACK(2); return nullobj; }
  5098.       {var reg1 object new_cons = allocate_cons();
  5099.        Car(new_cons) = STACK_1; Cdr(new_cons) = STACK_0; skipSTACK(2);
  5100.        return new_cons;
  5101.       }
  5102.       #endif
  5103.       #ifdef PATHNAME_NOEXT
  5104.       return translate_nametype(subst,muster,FALSE);
  5105.       #endif
  5106.     }
  5107.   local object translate_directory(subst,muster,logical,for_logical_p)
  5108.     var reg2 object* subst;
  5109.     var reg4 object muster;
  5110.     var reg6 boolean logical;
  5111.     var reg7 boolean for_logical_p;
  5112.     { var reg5 uintL itemcount = 0; # Anzahl der Elemente auf dem Stack
  5113.       # Startpoint:
  5114.       pushSTACK(Car(muster)); muster = Cdr(muster); itemcount++;
  5115.       # subdirs:
  5116.       while (consp(muster))
  5117.         { var reg3 object item = Car(muster);
  5118.           muster = Cdr(muster);
  5119.           if (eq(item,S(Kwild_inferiors)))
  5120.             { if (mconsp(*subst))
  5121.                 { if (listp(Car(*subst)))
  5122.                     { var reg1 object list = Car(*subst); *subst = Cdr(*subst);
  5123.                       while (consp(list))
  5124.                         { var reg1 object obj;
  5125.                           pushSTACK(list);
  5126.                           pushSTACK(muster);
  5127.                           obj = convert_subdir(Car(list),for_logical_p);
  5128.                           muster = popSTACK();
  5129.                           list = popSTACK();
  5130.                           pushSTACK(obj);
  5131.                           list = Cdr(list);
  5132.                           itemcount++; 
  5133.                         }
  5134.                     }
  5135.                     else
  5136.                     { skipSTACK(itemcount); return nullobj; }
  5137.                 }
  5138.                 else
  5139.                 { 
  5140.                   pushSTACK(muster);
  5141.                   item = convert_subdir(item,for_logical_p); itemcount++; 
  5142.                   muster = popSTACK();
  5143.                   pushSTACK(item);
  5144.                 }
  5145.             }
  5146.             else
  5147.             { pushSTACK(muster); # muster retten
  5148.               item = translate_subdir(subst,item,logical,for_logical_p);
  5149.               if (eq(item,nullobj)) { skipSTACK(itemcount+1); return nullobj; }
  5150.               muster = STACK_0; STACK_0 = item; itemcount++;
  5151.             }
  5152.         }
  5153.       return listof(itemcount);
  5154.     }
  5155.   local object translate_version(subst,muster,logical)
  5156.     var reg1 object* subst;
  5157.     var reg2 object muster;
  5158.     var reg4 boolean logical;
  5159.     {
  5160.       #ifdef LOGICAL_PATHNAMES
  5161.       if (logical)
  5162.         { if (eq(muster,S(Kwild)) && mconsp(*subst))
  5163.             { if (m_simple_string_p(Car(*subst)))
  5164.                 { var reg3 object erg = Car(*subst); *subst = Cdr(*subst);
  5165.                   pushSTACK(erg); funcall(L(parse_integer),1);
  5166.                   return value1;
  5167.                 }
  5168.                 else
  5169.                 return nullobj;
  5170.             }
  5171.           return muster;
  5172.         }
  5173.       #endif
  5174.       #if HAS_VERSION
  5175.       if (eq(muster,S(Kwild)) && mconsp(*subst))
  5176.         { if (m_simple_string_p(Car(*subst)))
  5177.             { var reg3 object erg = Car(*subst); *subst = Cdr(*subst);
  5178.               pushSTACK(erg); funcall(L(parse_integer),1);
  5179.               return value1;
  5180.             }
  5181.             else
  5182.             return nullobj;
  5183.         }
  5184.       #endif
  5185.       return muster;
  5186.     }
  5187.   local object translate_pathname(subst,muster,for_logical_p)
  5188.     var reg2 object* subst;
  5189.     var reg3 object muster;
  5190.     var reg4 boolean for_logical_p;
  5191.     { var reg4 boolean logical = FALSE;
  5192.       var reg1 object item;
  5193.       pushSTACK(*subst); # subst retten für Fehlermeldung
  5194.       pushSTACK(muster);
  5195.       #ifdef LOGICAL_PATHNAMES
  5196.       if (logpathnamep(muster)) { logical = TRUE; }
  5197.       #endif
  5198.       # Argumente für MAKE-PATHNAME zusammenbauen:
  5199.       #if 1 # HAS_HOST || defined(LOGICAL_PATHNAMES)
  5200.       item = translate_host(subst,xpathname_host(logical,muster),logical);
  5201.       if (eq(item,nullobj)) { goto subst_error; }
  5202.       pushSTACK(S(Khost)); pushSTACK(item);
  5203.       #endif
  5204.       #if HAS_DEVICE
  5205.       item = translate_device(subst,xpathname_device(logical,STACK_2),logical);
  5206.       if (eq(item,nullobj)) { skipSTACK(2); goto subst_error; }
  5207.       pushSTACK(S(Kdevice)); pushSTACK(item);
  5208.       #endif
  5209.       item = translate_directory(subst,xpathname_directory(logical,STACK_(2+2*HAS_DEVICE)),logical,for_logical_p);
  5210.       if (eq(item,nullobj)) { skipSTACK(2+2*HAS_DEVICE); goto subst_error; }
  5211.       pushSTACK(S(Kdirectory)); pushSTACK(item);
  5212.       item = translate_nametype(subst,xpathname_name(logical,STACK_(2+2*HAS_DEVICE+2)),logical);
  5213.       if (eq(item,nullobj)) { skipSTACK(2+2*HAS_DEVICE+2); goto subst_error; }
  5214.       pushSTACK(S(Kname)); pushSTACK(item);
  5215.       item = translate_nametype(subst,xpathname_type(logical,STACK_(2+2*HAS_DEVICE+4)),logical);
  5216.       if (eq(item,nullobj)) { skipSTACK(2+2*HAS_DEVICE+4); goto subst_error; }
  5217.       pushSTACK(S(Ktype)); pushSTACK(item);
  5218.       #if 1 # HAS_VERSION || defined(LOGICAL_PATHNAMES)
  5219.       item = translate_version(subst,xpathname_version(logical,STACK_(2+2*HAS_DEVICE+6)),logical);
  5220.       if (eq(item,nullobj)) { skipSTACK(2+2*HAS_DEVICE+6); goto subst_error; }
  5221.       pushSTACK(S(Kversion)); pushSTACK(item);
  5222.       #endif
  5223.       # Alle Ersetzungsstücke müssen verbraucht werden!
  5224.       if (mconsp(*subst)) { skipSTACK(2+2*HAS_DEVICE+8); goto subst_error; }
  5225.       # (MAKE-PATHNAME ...) bzw. (SYS::MAKE-LOGICAL-PATHNAME ...) aufrufen:
  5226.       #ifdef LOGICAL_PATHNAMES
  5227.       if (logical)
  5228.         funcall(L(make_logical_pathname),2+2*HAS_DEVICE+8);
  5229.         else
  5230.       #endif
  5231.         funcall(L(make_pathname),2+2*HAS_DEVICE+8);
  5232.       skipSTACK(2);
  5233.       return value1;
  5234.      subst_error: # Error wegen nullobj.
  5235.       # Stackaufbau: subst, muster.
  5236.       pushSTACK(STACK_1);
  5237.       pushSTACK(S(translate_pathname));
  5238.       //: DEUTSCH "~: Ersetzungsstücke ~ passen nicht in ~."
  5239.       //: ENGLISH "~: replacement pieces ~ do not fit into ~"
  5240.       //: FRANCAIS "~ : Les pièces ~ ne vont pas dans ~."
  5241.       fehler(error, GETTEXT("~: replacement pieces ~ do not fit into ~"));
  5242.     }
  5243.  
  5244. LISPFUN(translate_pathname,3,0,norest,key,2, (kw(all),kw(merge)))
  5245. # (TRANSLATE-PATHNAME beispiel muster1 muster2 [:all] [:merge]), CLtL2 S. 624
  5246. # :all = T --> liefere eine Liste aller passenden Pathnames
  5247. # :all = NIL --> Error, falls mehr als ein Pathname paßt
  5248. # :merge = NIL --> letzten MERGE-PATHNAMES Schritt überspringen
  5249.   { # Stackaufbau: beispiel, muster1, muster2, all, merge.
  5250.     var reg3 boolean logical = FALSE; # Flag, ob beispiel und muster logische Pathnames sind
  5251.     var reg4 boolean logical2 = FALSE; # Flag, ob muster2 ein logischer Pathname ist
  5252.     var reg5 boolean logical1 = FALSE;
  5253.     STACK_4 = coerce_xpathname(STACK_4);
  5254.     STACK_3 = coerce_xpathname(STACK_3);
  5255.     STACK_2 = coerce_xpathname(STACK_2);
  5256.     #ifdef LOGICAL_PATHNAMES
  5257.     if (logpathnamep(STACK_4)) logical1 = TRUE;
  5258.     if (logpathnamep(STACK_4) && logpathnamep(STACK_3))
  5259.       { logical = TRUE; }
  5260.       else
  5261.       # nicht beides logische Pathnames -> erst in normale Pathnames umwandeln:
  5262.       { STACK_4 = coerce_pathname(STACK_4);
  5263.         STACK_3 = coerce_pathname(STACK_3);
  5264.       }
  5265.     if (logpathnamep(STACK_2))
  5266.       { logical2 = TRUE; }
  5267.     #endif
  5268.     # 1. Schritt: Liste aller passenden Substitutionen bilden.
  5269.     pushSTACK(NIL); pushSTACK(NIL);
  5270.     host_diff(xpathname_host(logical,STACK_(3+2)),xpathname_host(logical,STACK_(4+2)),logical,&STACK_1,&STACK_0);
  5271.     while (mconsp(STACK_0))
  5272.       { pushSTACK(Car(STACK_0)); pushSTACK(NIL);
  5273.         device_diff(xpathname_device(logical,STACK_(3+4)),xpathname_device(logical,STACK_(4+4)),logical,&STACK_1,&STACK_0);
  5274.         while (mconsp(STACK_0))
  5275.           { pushSTACK(Car(STACK_0)); pushSTACK(NIL);
  5276.             directory_diff(xpathname_directory(logical,STACK_(3+6)),xpathname_directory(logical,STACK_(4+6)),logical,&STACK_1,&STACK_0);
  5277.             while (mconsp(STACK_0))
  5278.               { pushSTACK(Car(STACK_0)); pushSTACK(NIL);
  5279.                 nametype_diff(xpathname_name(logical,STACK_(3+8)),xpathname_name(logical,STACK_(4+8)),logical,&STACK_1,&STACK_0);
  5280.                 while (mconsp(STACK_0))
  5281.                   { pushSTACK(Car(STACK_0)); pushSTACK(NIL);
  5282.                     nametype_diff(xpathname_type(logical,STACK_(3+10)),xpathname_type(logical,STACK_(4+10)),logical,&STACK_1,&STACK_0);
  5283.                     while (mconsp(STACK_0))
  5284.                       { pushSTACK(Car(STACK_0));
  5285.                         version_diff(xpathname_version(logical,STACK_(3+11)),xpathname_version(logical,STACK_(4+11)),logical,&STACK_0,&STACK_10);
  5286.                         skipSTACK(1);
  5287.                         STACK_0 = Cdr(STACK_0);
  5288.                       }
  5289.                     skipSTACK(2);
  5290.                     STACK_0 = Cdr(STACK_0);
  5291.                   }
  5292.                 skipSTACK(2);
  5293.                 STACK_0 = Cdr(STACK_0);
  5294.               }
  5295.             skipSTACK(2);
  5296.             STACK_0 = Cdr(STACK_0);
  5297.           }
  5298.         skipSTACK(2);
  5299.         STACK_0 = Cdr(STACK_0);
  5300.       }
  5301.     skipSTACK(1);
  5302.     # Stackaufbau: ..., solutions.
  5303.     if (matomp(STACK_0))
  5304.       { pushSTACK(STACK_(3+1));
  5305.         pushSTACK(STACK_(4+1+1));
  5306.         pushSTACK(S(translate_pathname));
  5307.         //: DEUTSCH "~: ~ ist keine Spezialisierung von ~."
  5308.         //: ENGLISH "~: ~ is not a specialization of ~"
  5309.         //: FRANCAIS "~ : ~ ne spécialise pas ~."
  5310.         fehler(error, GETTEXT("~: ~ is not a specialization of ~"));
  5311.       }
  5312.     # 2.,3. Schritt:
  5313.     pushSTACK(NIL); # pathnames := '()
  5314.     while (mconsp(STACK_1)) # solutions durchgehen
  5315.       { var reg1 object solutions = STACK_1;
  5316.         STACK_1 = Cdr(solutions);
  5317.        {var reg2 object solution = reverse(Car(solutions)); # Liste solution umdrehen
  5318.         # 2. Schritt: Substitution in muster2 einfügen.
  5319.         #ifdef LOGICAL_PATHNAMES
  5320.         # Groß-/Kleinschreibung passend konvertieren:
  5321.         if (!logical)
  5322.           { if (logical2)
  5323.               { solution = subst_logical_case(solution); }
  5324.           }
  5325.           else
  5326.           { if (!logical2)
  5327.               { solution = subst_customary_case(solution); }
  5328.           }
  5329.         #endif
  5330.         pushSTACK(solution);
  5331.         STACK_0 = translate_pathname(&STACK_0,STACK_(2+1+2),logical1);
  5332.        }
  5333.         # 3. Schritt: (MERGE-PATHNAMES modifiziertes_muster2 beispiel :WILD T)
  5334.         if (!nullp(STACK_(0+1+2))) # :MERGE-Argument abfragen
  5335.           if (has_some_wildcards(STACK_0)) # evtl. ist MERGE-PATHNAMES unnötig
  5336.             { pushSTACK(STACK_(4+1+2)); pushSTACK(unbound);
  5337.               pushSTACK(S(Kwild)); pushSTACK(T);
  5338.               funcall(L(merge_pathnames),5);
  5339.               pushSTACK(value1);
  5340.             }
  5341.         # (PUSH pathname pathnames)
  5342.        {var reg1 object new_cons = allocate_cons();
  5343.         Car(new_cons) = popSTACK(); Cdr(new_cons) = STACK_0;
  5344.         STACK_0 = new_cons;
  5345.       }}
  5346.     # 4. Schritt: (DELETE-DUPLICATES pathnames :TEST #'EQUAL)
  5347.     pushSTACK(S(Ktest)); pushSTACK(L(equal));
  5348.     funcall(L(delete_duplicates),3);
  5349.     # Stackaufbau: ..., nil.
  5350.     if (eq(STACK_(1+1),unbound) || nullp(STACK_(1+1))) # :ALL-Argument abfragen
  5351.       { if (mconsp(Cdr(value1)))
  5352.           { pushSTACK(value1);
  5353.             pushSTACK(STACK_(2+2));
  5354.             pushSTACK(STACK_(3+3));
  5355.             pushSTACK(STACK_(4+4));
  5356.             pushSTACK(S(translate_pathname));
  5357.             //: DEUTSCH "(~ ~ ~ ~) ist nicht eindeutig: ~"
  5358.             //: ENGLISH "(~ ~ ~ ~) is ambiguous: ~"
  5359.             //: FRANCAIS "(~ ~ ~ ~) est ambigu: ~"
  5360.             fehler(error, GETTEXT("(~ ~ ~ ~) is ambiguous: ~"));
  5361.           }
  5362.         value1 = Car(value1);
  5363.       }
  5364.     mv_count=1;
  5365.     skipSTACK(5+1);
  5366.   }
  5367.  
  5368. # UP: Stellt fest, ob der Name eines Pathname =NIL ist.
  5369. # namenullp(pathname)
  5370. # > pathname: nicht-Logical Pathname
  5371.   # local boolean namenullp (object pathname);
  5372.   # local boolean namenullp(pathname)
  5373.   #   { return nullp(ThePathname(pathname)->pathname_name); }
  5374.   #define namenullp(path)  (nullp(ThePathname(path)->pathname_name))
  5375.  
  5376. # Fehler, wenn ein Directory nicht existiert
  5377. # > obj: Pathname oder (besser) fehlerhafte Komponente
  5378.   nonreturning_function(local, fehler_dir_not_exists, (object obj));
  5379.   local void fehler_dir_not_exists(obj)
  5380.     var reg1 object obj;
  5381.     { pushSTACK(obj); # Wert für Slot PATHNAME von FILE-ERROR
  5382.       pushSTACK(obj);
  5383.       //: DEUTSCH "Directory existiert nicht: ~"
  5384.       //: ENGLISH "nonexistent directory: ~"
  5385.       //: FRANCAIS "Le répertoire ~ n'existe pas."
  5386.       fehler(file_error, GETTEXT("nonexistent directory: ~"));
  5387.     }
  5388.  
  5389. # Fehler, wenn eine Datei bereits existiert
  5390. # > caller: Aufrufer (ein Symbol)
  5391. # > pathname: Pathname
  5392.   nonreturning_function(local, fehler_file_exists, (object caller, object pathname));
  5393.   local void fehler_file_exists(caller,pathname)
  5394.     var reg2 object caller;
  5395.     var reg1 object pathname;
  5396.     { pushSTACK(pathname); # Wert für Slot PATHNAME von FILE-ERROR
  5397.       pushSTACK(pathname);
  5398.       pushSTACK(caller);
  5399.       //: DEUTSCH "~: Eine Datei ~ existiert bereits."
  5400.       //: ENGLISH "~: File ~ already exists"
  5401.       //: FRANCAIS "~ : Le fichier ~ existe déjà."
  5402.       fehler(file_error, GETTEXT("~: File ~ already exists"));
  5403.     }
  5404.  
  5405. #ifdef LOGICAL_PATHNAMES
  5406. # Ein "absoluter Pathname" ist stets ein nicht-Logical Pathname, evtl.
  5407. # mit weiteren Einschränkungen.
  5408. #endif
  5409.  
  5410. #if defined(PATHNAME_MSDOS) || defined(PATHNAME_OS2)
  5411.  
  5412. # Ein "absoluter Pathname" ist ein Pathname, bei dem Device ein überprüfter
  5413. # String ist und Directory kein :RELATIVE, :CURRENT, :PARENT enthält.
  5414.  
  5415. # UP: Liefert den Namestring eines Pathname als ASCIZ-String.
  5416. # namestring_asciz(dir_namestring)
  5417. # > STACK_0: nicht-Logical Pathname
  5418. # > dir_namestring: Directory-Namestring (für DOS bzw. GEMDOS, ohne Seriennummer)
  5419. # < ergebnis: Namestring (für DOS bzw. GEMDOS, ohne Seriennummer, mit Nullbyte am Schluß)
  5420. # kann GC auslösen
  5421.   local object namestring_asciz (object dir_namestring);
  5422.   local object namestring_asciz(dir_namestring)
  5423.     var reg2 object dir_namestring;
  5424.     { var reg1 uintC stringcount;
  5425.       pushSTACK(dir_namestring); # Directory-Namestring als 1. String
  5426.       stringcount = file_namestring_parts(STACK_(0+1)); # Strings zum Filenamen
  5427.       pushSTACK(O(null_string)); # String mit Nullbyte
  5428.       return string_concat(1+stringcount+1); # zusammenhängen
  5429.     }
  5430.  
  5431. #if !(defined(WATCOM) && defined(WINDOWS))
  5432.  
  5433. # Working Directory auf einem gegebenen Drive abfragen:
  5434. # getwdof(&buf,drive)
  5435. # > uintB* &buf: Adresse eines Path-Buffers
  5436. # > uintB drive: Laufwerk (0=A, 1=B, ...)
  5437. # < ergebnis: <0 falls Fehler
  5438.   #ifdef DJUNIX
  5439.     #if DJGPP == 2
  5440.       local int getwdof (uintB* buf, uintB drive);
  5441.       local int getwdof(buf,drive)
  5442.         var uintB* buf;
  5443.         var uintB drive;
  5444.         { var char buf__[MAXPATHLEN+1];
  5445.           var reg1 uintB *buf_;
  5446.           var unsigned int current_drive;
  5447.           var unsigned int drive_count;
  5448.           _dos_getdrive(¤t_drive);
  5449.           _dos_setdrive(drive,&drive_count);
  5450.           getcwd(buf__,MAXPATHLEN);
  5451.           buf_=buf__+3;
  5452.           while (*buf_) { *buf++=*buf_++; }
  5453.           *buf='\0';
  5454.           _dos_setdrive(current_drive,&drive_count);
  5455.           return 0;
  5456.         }
  5457.     #else
  5458.       #define getwdof(buf,drive)  \
  5459.         ({__asm__ (# DOS Function 47H                                                         \
  5460.                    " movb $0x47,%%ah ; int $0x21 "                                            \
  5461.                    :                                                                # OUT     \
  5462.                    : "S" /* %esi */ ((uintB*)(buf)), "d" /* %dl */ ((uintB)(drive)) # IN      \
  5463.                    : "ax","bx","cx","di" /* %eax, %ebx, %ecx, %edi */               # CLOBBER \
  5464.                   );                                                                          \
  5465.           0;                                                                                  \
  5466.          })
  5467.     #endif
  5468.   #endif
  5469.   #ifdef EMUNIX
  5470.     #define getwdof(buf,drive)  _getcwd(buf,drive)
  5471.   #endif
  5472.   #ifdef WATCOM
  5473.     local int getwdof (uintB* buf, uintB drive);
  5474.     local int getwdof(buf,drive)
  5475.       var uintB* buf;
  5476.       var uintB drive;
  5477.       { var union REGS in;
  5478.         var union REGS out;
  5479.         in.regB.ah = 0x47; in.regB.dl = drive; in.regL.esi = (unsigned long) buf;
  5480.         intdos(&in,&out);
  5481.         return 0;
  5482.       }
  5483.   #endif
  5484.  
  5485. # Liefert das aktuelle Directory auf einem Laufwerk.
  5486. # getwd_of(path,drive)
  5487. # > uintB drive: Laufwerks-(groß-)buchstabe
  5488. # > uintB* path: Platz fürs aktuelle Directory
  5489. # < path: Pfad des aktuellen Directories, mit '/' als Trennzeichen und als Anfang
  5490. # < ergebnis: <0 falls Fehler
  5491.   #if defined(DJUNIX) || defined(WATCOM)
  5492.     #define getwd_of(path,drive)  ((path)[0] = '/', getwdof(&(path)[1],(drive)-'A'+1))
  5493.   #endif
  5494.   #ifdef EMUNIX
  5495.     #define getwd_of(path,drive)  _getcwd1(path,drive)
  5496.   #endif
  5497.  
  5498. #endif # !(WATCOM && WINDOWS)
  5499.  
  5500. # UP: Stellt fest, ob ein Laufwerk existiert.
  5501. # > uintB drive: Laufwerks-(groß-)buchstabe
  5502. # < boolean ergebnis: ob dieses Laufwerk existiert und ansprechbar ist
  5503.   local boolean good_drive (uintB drive);
  5504.   #ifdef EMUNIX
  5505.   local boolean good_drive(drive)
  5506.     var reg1 uintB drive;
  5507.     { # Methode (siehe HELPPC/misc.txt):
  5508.       # 1. save current drive  (INT 0x21,0x19)
  5509.       # 2. set current drive  (INT 0x21,0xE)
  5510.       # 3. get current drive  (INT 0x21,0x19)
  5511.       # 4. if current drive == drive requested
  5512.       #       then drive exists
  5513.       #       else drive doesn't exist
  5514.       # 5. reset original drive  (INT 0x21,0xE)
  5515.       var reg3 boolean result;
  5516.       begin_system_call();
  5517.      {var reg2 uintB orig_drive = _getdrive();
  5518.       _chdrive(drive);
  5519.       result = (_getdrive() == drive);
  5520.       _chdrive(orig_drive);
  5521.      }
  5522.       end_system_call();
  5523.       return result;
  5524.       # Alternative:
  5525.       # { var uintB drv[3];
  5526.       #   var uintB fsys[16];
  5527.       #   drv[0] = drive; drv[1] = ':'; drv[2] = '\0';
  5528.       #   begin_system_call();
  5529.       #  {var int result = _filesys(drv,&fsys,sizeof(fsys));
  5530.       #   end_system_call();
  5531.       #   return (result==0);
  5532.       # }}
  5533.     }
  5534.   #endif
  5535.   #if defined(DJUNIX) || defined(WATCOM)
  5536.   local boolean good_drive(drive)
  5537.     var reg1 uintB drive;
  5538.     { # Methode (siehe HELPPC/misc.txt):
  5539.       # 1. save current drive  (INT 0x21,0x19)
  5540.       # 2. set current drive  (INT 0x21,0xE)
  5541.       # 3. get current drive  (INT 0x21,0x19)
  5542.       # 4. if current drive == drive requested
  5543.       #       then drive exists
  5544.       #       else drive doesn't exist
  5545.       # 5. reset original drive  (INT 0x21,0xE)
  5546.       var union REGS in;
  5547.       var union REGS out;
  5548.       var reg2 uintB orig_drive;
  5549.       var reg3 boolean result;
  5550.       begin_system_call();
  5551.       in.regB.ah = 0x19; intdos(&in,&out); orig_drive = out.regB.al; # 1.
  5552.       in.regB.ah = 0x0E; in.regB.dl = drive; intdos(&in,&out);       # 2.
  5553.       in.regB.ah = 0x19; intdos(&in,&out);                           # 3.
  5554.       result = (out.regB.al == drive);                               # 4.
  5555.       in.regB.ah = 0x0E; in.regB.dl = orig_drive; intdos(&in,&out);  # 5.
  5556.       end_system_call();
  5557.       return result;
  5558.     }
  5559.   #endif
  5560.   #ifdef WIN32_DOS
  5561.   local boolean good_drive(drive)
  5562.     var reg1 uintB drive;
  5563.     { var char RootPathName[4];
  5564.       var reg2 boolean result;
  5565.       RootPathName[0]=drive;
  5566.       RootPathName[1]=':';
  5567.       RootPathName[2]='\\';
  5568.       RootPathName[3]='\0';
  5569.       begin_system_call();
  5570.       result = GetDriveType(RootPathName) > 0;
  5571.       end_system_call();
  5572.       return result;
  5573.     }
  5574.   #endif
  5575.  
  5576. # UP: Liefert das aktuelle Drive.
  5577. # < uintB drive: Laufwerks-(groß-)buchstabe
  5578.   local uintB default_drive (void);
  5579.  #ifdef EMUNIX
  5580.   local uintB default_drive()
  5581.     { var reg1 uintB result;
  5582.       begin_system_call();
  5583.       result = _getdrive();
  5584.       end_system_call();
  5585.       return result;
  5586.     }
  5587.  #endif
  5588.  #if defined(DJUNIX) || defined(WATCOM)
  5589.   #if 1
  5590.     local uintB default_drive()
  5591.       { var union REGS in;
  5592.         var union REGS out;
  5593.         begin_system_call();
  5594.         in.regB.ah = 0x19;
  5595.         intdos(&in,&out);
  5596.         end_system_call();
  5597.         return 'A'+out.regB.al;
  5598.       }
  5599.   #else # nur defined(WATCOM)
  5600.     local uintB default_drive()
  5601.       { var unsigned int drive;
  5602.         begin_system_call();
  5603.         _dos_getdrive(&drive);
  5604.         end_system_call();
  5605.         return 'A'+drive-1;
  5606.       }
  5607.   #endif
  5608.  #endif
  5609.  #ifdef WIN32_DOS
  5610.    local uintB default_drive()
  5611.     { var char path_buffer[3+MAXPATHLEN]; # vgl. GETWD(3)
  5612.       begin_system_call();
  5613.       GetCurrentDirectory (MAXPATHLEN,path_buffer);
  5614.       end_system_call();
  5615.       return path_buffer[0];
  5616.     }
  5617.  #endif
  5618.  
  5619. # UP: Liefert das aktuelle Directory auf einem gegebenen Drive.
  5620. # > uintB drive: Laufwerks-(groß-)buchstabe
  5621. # < ergebnis: aktuelles Directory (als Pathname)
  5622. # kann GC auslösen
  5623.   local object default_directory_of (uintB drive);
  5624.   local object default_directory_of(drive)
  5625.     var reg1 uintB drive;
  5626.     # Working Directory (von DOS) ist das aktuelle Directory:
  5627.     { var char path_buffer[3+MAXPATHLEN]; # vgl. GETWD(3)
  5628.       #if !(defined(WATCOM) && defined(WINDOWS)) && !defined(WIN32_DOS)
  5629.         path_buffer[0] = drive; path_buffer[1] = ':';
  5630.         # Working Directory in path_buffer ablegen:
  5631.         begin_system_call();
  5632.         getwd_of(&path_buffer[2],drive);
  5633.         end_system_call();
  5634.       #elif defined(WIN32_DOS)
  5635.         begin_system_call();
  5636.         GetCurrentDirectory (MAXPATHLEN,path_buffer);
  5637.         end_system_call();
  5638.       #else # defined(WATCOM) && defined(WINDOWS)
  5639.         # Methode:
  5640.         # 1. save current drive  (INT 0x21,0x19)
  5641.         # 2. set current drive  (INT 0x21,0xE)
  5642.         # 3. get current directory on current drive  (getcwd)
  5643.         # 4. reset original drive  (INT 0x21,0xE)
  5644.         { var union REGS in;
  5645.           var union REGS out;
  5646.           var reg2 uintB orig_drive;
  5647.           begin_system_call();
  5648.           in.regB.ah = 0x19; intdos(&in,&out); orig_drive = out.regB.al; # 1.
  5649.           in.regB.ah = 0x0E; in.regB.dl = drive-'A'+1; intdos(&in,&out); # 2.
  5650.           getcwd(&path_buffer[0],sizeof(path_buffer));                   # 3.
  5651.           in.regB.ah = 0x0E; in.regB.dl = orig_drive; intdos(&in,&out);  # 4.
  5652.           end_system_call();
  5653.         }
  5654.         ASSERT(path_buffer[0]==drive);
  5655.         ASSERT(path_buffer[1]==':');
  5656.         ASSERT(path_buffer[2]=='\\');
  5657.         # evtl. noch ein '\' am Schluß anfügen:
  5658.         { var reg2 char* path_end = &path_buffer[asciz_length(path_buffer)];
  5659.           if (!(path_end[-1]=='\\')) { path_end[0] = '\\'; path_end[1] = '\0'; }
  5660.         }
  5661.       #endif
  5662.       # Hack von DJ (siehe GO32/EXPHDLR.C) und EM (siehe LIB/MISC/_GETCWD1.C):
  5663.       # wandelt alle '\' in '/' und alle Groß- in Kleinbuchstaben (nur Kosmetik,
  5664.       # da DOS und unser PARSE-NAMESTRING auch Filenamen mit '/' statt '\'
  5665.       # verstehen).
  5666.       # in Pathname umwandeln:
  5667.       return asciz_dir_to_pathname(&path_buffer[0]);
  5668.     }
  5669.  
  5670. # UP: Füllt Default-Drive und Default-Directory in einen Pathname ein.
  5671. # use_default_dir(pathname)
  5672. # > pathname: nicht-Logical Pathname mit Device /= :WILD
  5673. # < ergebnis: neuer absoluter Pathname
  5674. # kann GC auslösen
  5675.   local object use_default_dir (object pathname);
  5676.   local object use_default_dir(pathname)
  5677.     var reg4 object pathname;
  5678.     { # erst den Pathname kopieren:
  5679.       pathname = copy_pathname(pathname);
  5680.       pushSTACK(pathname);
  5681.       # Stackaufbau: pathname.
  5682.       # Default fürs Device:
  5683.       if (nullp(ThePathname(pathname)->pathname_device)) # kein Device angegeben?
  5684.         # Nimm das Default-Drive stattdessen:
  5685.         { ThePathname(pathname)->pathname_device = O(default_drive); }
  5686.       # Default fürs Directory:
  5687.         { var reg3 object subdirs = ThePathname(pathname)->pathname_directory;
  5688.           # Fängt pathname-directory mit :RELATIVE an?
  5689.           if (eq(Car(subdirs),S(Krelative)))
  5690.             # ja -> Ersetze :RELATIVE durch das Default-Directory:
  5691.             { var reg5 uintB drive = TheSstring(ThePathname(pathname)->pathname_device)->data[0];
  5692.               var reg2 object default_dir = default_directory_of(drive);
  5693.               # default_dir (ein Pathname) ist fertig.
  5694.               # Ersetze :RELATIVE durch default-subdirs, d.h.
  5695.               # bilde  (append default-subdirs (cdr subdirs))
  5696.               #      = (nreconc (reverse default-subdirs) (cdr subdirs))
  5697.               pushSTACK(Cdr(subdirs));
  5698.              {var reg1 object temp = ThePathname(default_dir)->pathname_directory;
  5699.               temp = reverse(temp);
  5700.               subdirs = nreconc(temp,popSTACK());
  5701.             }}
  5702.           # Liste durchgehen und dabei neu aufconsen, dabei '.\' und '..\'
  5703.           # und '...\' verarbeiten (nicht dem DOS bzw. GEMDOS überlassen):
  5704.           pushSTACK(subdirs);
  5705.           pushSTACK(NIL);
  5706.           # Stackaufbau: Pathname, subdir-oldlist, subdir-newlist.
  5707.           while (mconsp(STACK_1)) # Bis oldlist am Ende ist:
  5708.             { var reg2 object subdir = Car(STACK_1); # nächstes subdir
  5709.               if
  5710.                  #if defined(PATHNAME_MSDOS)
  5711.                  (eq(subdir,S(Kcurrent)))
  5712.                  #else
  5713.                  (equal(subdir,O(punkt_string)))
  5714.                  #endif
  5715.                 # = :CURRENT -> newlist unverändert lassen
  5716.                 {}
  5717.               elif
  5718.                    #if defined(PATHNAME_MSDOS)
  5719.                    (eq(subdir,S(Kparent)))
  5720.                    #else
  5721.                    (equal(subdir,O(punktpunkt_string)))
  5722.                    #endif
  5723.                 # = :PARENT -> newlist um eins verkürzen:
  5724.                 { if (matomp(Cdr(STACK_0))) # newlist (bis auf :ABSOLUTE) leer ?
  5725.                     { # :PARENT von "\" aus liefert Error
  5726.                       pushSTACK(STACK_2); # Wert für Slot PATHNAME von FILE-ERROR
  5727.                       pushSTACK(O(backslash_string)); # "\\"
  5728.                       pushSTACK(directory_namestring(STACK_(2+2))); # Directory von pathname
  5729.                       //: DEUTSCH "Directory ~ oberhalb ~ existiert nicht."
  5730.                       //: ENGLISH "no directory ~ above ~"
  5731.                       //: FRANCAIS "Il n'y a pas de répertoire ~ au delà de ~."
  5732.                       fehler(file_error, GETTEXT("no directory ~ above ~"));
  5733.                     }
  5734.                   if (eq(Car(STACK_0),S(Kwild_inferiors))) # newlist fängt mit '...\' an ?
  5735.                     { # :PARENT von "...\" aus liefert Error
  5736.                       pushSTACK(STACK_2); # Wert für Slot PATHNAME von FILE-ERROR
  5737.                       pushSTACK(directory_namestring(STACK_(2+1))); # Directory von pathname
  5738.                       # '"..\\" nach "...\\" ist unzulässig: ~'
  5739.                       //: DEUTSCH "\"..\\\\\" nach \"...\\\\\" ist unzulässig: ~"
  5740.                       //: ENGLISH "\"..\\\\\" after \"...\\\\\" is invalid: ~"
  5741.                       //: FRANCAIS "\"..\\\\\" après \"...\\\\\" n'est pas permis : ~"
  5742.                       fehler(file_error,  GETTEXT("\"..\\\\\" after \"...\\\\\" is invalid: ~"));
  5743.                     }
  5744.                   STACK_0 = Cdr(STACK_0);
  5745.                 }
  5746.               else # (auch wenn :ABSOLUTE !)
  5747.                 { # newlist um eins verlängern:
  5748.                   pushSTACK(subdir);
  5749.                  {var reg1 object new_cons = allocate_cons();
  5750.                   Car(new_cons) = popSTACK();
  5751.                   Cdr(new_cons) = STACK_0;
  5752.                   STACK_0 = new_cons;
  5753.                 }}
  5754.               STACK_1 = Cdr(STACK_1);
  5755.             }
  5756.           subdirs = nreverse(popSTACK()); # newlist, wieder umdrehen
  5757.           skipSTACK(1);
  5758.           # Stackaufbau: pathname.
  5759.           pathname = popSTACK();
  5760.           ThePathname(pathname)->pathname_directory = subdirs; # in den Pathname eintragen
  5761.         }
  5762.       return pathname;
  5763.     }
  5764.  
  5765. # UP: Stellt sicher, daß das Directory eines Pathname existiert.
  5766. # Sonst Fehlermeldung.
  5767. # assure_dir_exists_(tolerantp,dirtolerantp)
  5768. # > STACK_0: absoluter Pathname ohne Wildcards im Directory
  5769. # > tolerantp: Flag, ob ein Fehler vermieden werden soll
  5770. # > dirtolerantp: Don't complain if file ends up being a directory
  5771. # < ergebnis:
  5772. #     falls Name=NIL: Directory-Namestring (für DOS)
  5773. #     falls Name/=NIL: Namestring (für DOS, mit Nullbyte am Schluß)
  5774. #     falls tolerantp evtl.: nullobj
  5775. # kann GC auslösen
  5776.   local object assure_dir_exists_ (boolean tolerantp, boolean dirtolerantp);
  5777.   local object assure_dir_exists_(tolerantp,dirtolerantp)
  5778.     var reg4 boolean tolerantp;
  5779.     var reg5 boolean dirtolerantp;
  5780.     { var reg2 uintC stringcount = directory_namestring_parts(STACK_0); # Strings fürs Directory
  5781.       var reg1 object dir_namestring = string_concat(stringcount); # zusammenhängen
  5782.       # Existenztest:
  5783.       # 1. Subdir-List leer -> OK
  5784.       #    (Muß abgefangen werden, denn stat() auf Rootdir liefert Fehler.)
  5785.       # 2. OS/2: Subdir-List = ("PIPE") -> OK
  5786.       #    (Dieses Spezialverzeichnis "\\PIPE\\" ist in Wirklichkeit keines.)
  5787.       # 3. Sonst stat() probieren.
  5788.       if (!(nullp(Cdr(ThePathname(STACK_0)->pathname_directory))
  5789.             #ifdef PATHNAME_OS2
  5790.             || equal(Cdr(ThePathname(STACK_0)->pathname_directory),O(pipe_subdirs))
  5791.             #endif
  5792.          ) )
  5793.         {var struct stat statbuf;
  5794.          var reg3 uintB* endptr = &TheSstring(dir_namestring)->data[TheSstring(dir_namestring)->length-1];
  5795.          *endptr = '\0'; # '\' am Schluß durch Nullbyte ersetzen
  5796.          begin_system_call();
  5797.          if (stat(TheAsciz(dir_namestring),&statbuf) < 0)
  5798.            { end_system_call();
  5799.              if (tolerantp && (errno==ENOENT)) { return nullobj; }
  5800.              OS_error();
  5801.            }
  5802.          end_system_call();
  5803.          *endptr = '\\'; # wieder mit '\' abschließen
  5804.          if (!S_ISDIR(statbuf.st_mode)) # gefundene Datei kein Unterdirectory ?
  5805.            { if (tolerantp) { return nullobj; }
  5806.              fehler_dir_not_exists(dir_namestring);
  5807.         }  }
  5808.       if (namenullp(STACK_0))
  5809.         { return dir_namestring; }
  5810.         else
  5811.         { return namestring_asciz(dir_namestring); }
  5812.     }
  5813.  
  5814. # UP: Stellt sicher, daß das Directory eines Pathname existiert.
  5815. # Sonst Fehlermeldung.
  5816. # assure_dir_exists(tolerantp)
  5817. # > STACK_0: absoluter Pathname ohne Wildcards im Directory
  5818. # > tolerantp: Flag, ob ein Fehler vermieden werden soll
  5819. # < ergebnis:
  5820. #     falls Name=NIL: Directory-Namestring (für RISCOS, mit '.' am Schluß)
  5821. #     falls Name/=NIL: Namestring (für RISCOS, mit Nullbyte am Schluß)
  5822. #     falls tolerantp evtl.: nullobj
  5823. # < filestatus: Falls Name/=NIL: NULL falls das File nicht existiert,
  5824. #                                sonst ein Pointer auf eine STAT-Information.
  5825. # kann GC auslösen
  5826.   local object assure_dir_exists (boolean tolerantp);
  5827.   local object assure_dir_exists(tolerantp)
  5828.     var reg5 boolean tolerantp;
  5829.     { return assure_dir_exists_(tolerantp,FALSE);
  5830.     }
  5831.  
  5832. # UP: Liefert den Directory-Namestring eines Pathname unter der Annahme,
  5833. #     daß das Directory dieses Pathname existiert.
  5834. # assume_dir_exists()
  5835. # > STACK_0: absoluter Pathname ohne Wildcards im Directory
  5836. # < ergebnis:
  5837. #     falls Name=NIL: Directory-Namestring (für DOS bzw. GEMDOS, ohne Seriennummer)
  5838. #     falls Name/=NIL: Namestring (für DOS bzw. GEMDOS, ohne Seriennummer, mit Nullbyte am Schluß)
  5839. # kann GC auslösen
  5840.   global object assume_dir_exists (void);
  5841.   global object assume_dir_exists()
  5842.     { var reg2 uintC stringcount =
  5843.         directory_namestring_parts(STACK_0); # Strings fürs Directory
  5844.       var reg1 object dir_namestring = string_concat(stringcount); # zusammenhängen
  5845.       if (namenullp(STACK_0))
  5846.         { return dir_namestring; }
  5847.         else
  5848.         { return namestring_asciz(dir_namestring); }
  5849.     }
  5850.  
  5851. #endif
  5852.  
  5853. #ifdef PATHNAME_AMIGAOS
  5854.  
  5855. # UP: Liefert den Truename eines Directory-Locks.
  5856. # > set_break_sem_4(): schon ausgeführt
  5857. # > lock: Directory-Lock, wird freigegeben
  5858. # < ergebnis: Directory (als Pathname)
  5859. # kann GC auslösen
  5860.   local object directory_truename (BPTR lock);
  5861.   local object directory_truename(lock)
  5862.     var reg6 BPTR lock;
  5863.     { # Von hier aus hochhangeln:
  5864.       pushSTACK(NIL); # Subdir-Liste := NIL
  5865.       { var LONGALIGNTYPE(struct FileInfoBlock) fib;
  5866.         var reg5 struct FileInfoBlock * fibptr = LONGALIGN(&fib);
  5867.         loop
  5868.           { # Directory selbst ansehen:
  5869.             begin_system_call();
  5870.            {var reg1 LONG ergebnis = Examine(lock,fibptr);
  5871.             end_system_call();
  5872.             if (!ergebnis) { OS_error(); }
  5873.            }
  5874.             # seinen Namen verwenden:
  5875.            {var reg4 object name = asciz_to_string(&fibptr->fib_FileName[0]);
  5876.             # zum Parent-Directory hochsteigen:
  5877.             var reg3 BPTR parentlock;
  5878.             begin_system_call();
  5879.             parentlock = ParentDir(lock);
  5880.             UnLock(lock);
  5881.             end_system_call();
  5882.             if (!(parentlock==BPTR_NULL))
  5883.               # name ist der Name eines Subdirectories
  5884.               { # vor die Subdir-Liste pushen:
  5885.                 pushSTACK(name);
  5886.                {var reg1 object new_cons = allocate_cons();
  5887.                 Car(new_cons) = popSTACK();
  5888.                 Cdr(new_cons) = STACK_0;
  5889.                 STACK_0 = new_cons;
  5890.                }
  5891.                 lock = parentlock; # und vom Parent Directory aus weitermachen
  5892.               }
  5893.               else
  5894.               { begin_system_call();
  5895.                 if (IoErr()) { OS_error(); } # Fehler aufgetreten?
  5896.                 end_system_call();
  5897.                 # name ist der Name eines DOS-Volumes.
  5898.                 pushSTACK(name);
  5899.                 break;
  5900.               }
  5901.       }   }}
  5902.       clr_break_sem_4(); # Unterbrechungen wieder zulassen
  5903.       # Stackaufbau: subdirs, devicename.
  5904.      {# subdirs mit :ABSOLUTE anfangen lassen:
  5905.       var reg1 object new_cons = allocate_cons();
  5906.       Car(new_cons) = S(Kabsolute); Cdr(new_cons) = STACK_1;
  5907.       STACK_1 = new_cons;
  5908.      }
  5909.      {var reg1 object default_dir = allocate_pathname(); # neuer Pathname mit Name=NIL und Typ=NIL
  5910.       ThePathname(default_dir)->pathname_device = popSTACK();
  5911.       ThePathname(default_dir)->pathname_directory = popSTACK();
  5912.       return default_dir;
  5913.     }}
  5914.  
  5915. # UP: Liefert das aktuelle Directory.
  5916. # < ergebnis: aktuelles Directory (als Pathname)
  5917. # kann GC auslösen
  5918.   local object default_directory (void);
  5919.   local object default_directory()
  5920.     { # Lock fürs aktuelle Directory holen:
  5921.       set_break_sem_4(); # Unterbrechungen währenddessen verhindern
  5922.       begin_system_call();
  5923.      {var reg1 BPTR lock = Lock("",ACCESS_READ);
  5924.       if (lock==BPTR_NULL)
  5925.         { if (!(IoErr()==ERROR_OBJECT_NOT_FOUND)) { OS_error(); }
  5926.           pushSTACK(unbound); # "Wert" für Slot PATHNAME von FILE-ERROR
  5927.           //: DEUTSCH "Zugriff auf aktuelles Verzeichnis nicht möglich."
  5928.           //: ENGLISH "Couldn't access current directory"
  5929.           //: FRANCAIS "Le répertoire courant n'est pas accessible."
  5930.           fehler(file_error, GETTEXT("Couldn't access current directory"));
  5931.         }
  5932.       end_system_call();
  5933.       return directory_truename(lock); # macht clr_break_sem_4(); und UnLock(lock);
  5934.     }}
  5935.  
  5936. # UP: Füllt Default-Directory in einen Pathname ein.
  5937. # use_default_dir(pathname)
  5938. # > pathname: nicht-Logical Pathname
  5939. # < ergebnis: neuer absoluter Pathname
  5940. # kann GC auslösen
  5941.   local object use_default_dir (object pathname);
  5942.   local object use_default_dir(pathname)
  5943.     var reg3 object pathname;
  5944.     { # erst den Pathname kopieren:
  5945.       pathname = copy_pathname(pathname);
  5946.       # Dann das Default-Directory in den Pathname einbauen:
  5947.       { var reg2 object subdirs = ThePathname(pathname)->pathname_directory;
  5948.         # Fängt pathname-directory mit :RELATIVE an?
  5949.         if (eq(Car(subdirs),S(Krelative)))
  5950.           { # ja -> Ersetze :RELATIVE durch default-subdirs, d.h.
  5951.             # bilde  (append default-subdirs (cdr subdirs))
  5952.             #      = (nreconc (reverse default-subdirs) (cdr subdirs))
  5953.             pushSTACK(pathname);
  5954.             pushSTACK(Cdr(subdirs));
  5955.            {var reg1 object temp = default_directory();
  5956.             temp = ThePathname(temp)->pathname_directory;
  5957.             temp = reverse(temp);
  5958.             subdirs = nreconc(temp,popSTACK());
  5959.             pathname = popSTACK();
  5960.             # in den Pathname eintragen:
  5961.             ThePathname(pathname)->pathname_directory = subdirs;
  5962.           }}
  5963.       }
  5964.       return pathname;
  5965.     }
  5966.  
  5967. # UP: Macht aus einem Directory-Namestring einen, der für AMIGAOS geeignet ist.
  5968. # OSnamestring(namestring)
  5969. # > namestring: neu erzeugter Directory-Namestring, mit '/' oder ':' am
  5970. #               Schluß, ein Simple-String
  5971. # < ergebnis: Namestring zu diesem Directory, im AmigaOS-Format: letzter '/'
  5972. #             gestrichen, falls überflüssig, ASCIZ-String
  5973. # kann GC auslösen
  5974.   local object OSnamestring (object namestring);
  5975.   local object OSnamestring(namestring)
  5976.     var reg1 object namestring;
  5977.     { var reg2 uintL len = TheSstring(namestring)->length;
  5978.       if (len==0) goto ok; # Leerstring -> nichts streichen
  5979.      {var reg3 uintB ch = TheSstring(namestring)->data[len-1];
  5980.       if (!(ch=='/')) goto ok; # kein '/' am Schluß -> nichts streichen
  5981.       if (len==1) goto ok; # "/" bedeutet Parent -> nicht streichen
  5982.       ch = TheSstring(namestring)->data[len-2];
  5983.       if ((ch=='/') || (ch==':')) # davor ein '/' oder ':'
  5984.         goto ok; # -> bedeutet Parent -> nicht streichen
  5985.       # '/' am Schluß streichen, dann string_to_asciz:
  5986.         namestring = copy_string(namestring); # Länge bleibt dabei gleich!
  5987.         TheSstring(namestring)->data[len-1] = '\0';
  5988.         return namestring;
  5989.       ok: # nichts streichen
  5990.         return string_to_asciz(namestring);
  5991.     }}
  5992.  
  5993. # UP: Stellt sicher, daß das Directory eines Pathname existiert.
  5994. # assure_dir_exists(tolerantp,dirtolerantp)
  5995. # > STACK_0: nicht-Logical Pathname, bei dem Directory kein :RELATIVE enthält.
  5996. # > tolerantp: Flag, ob ein Fehler vermieden werden soll
  5997. # > dirtolerantp: Don't complain if file ends up being a directory
  5998. # > subr_self: Aufrufer (ein SUBR)
  5999. # < STACK_0: (evtl. derselbe) Pathname, aber aufgelöst.
  6000. # < ergebnis:
  6001. #     falls Name=NIL: Directory-Namestring (für AMIGAOS, mit '/' am Schluß)
  6002. #     falls Name/=NIL: Namestring (für AMIGAOS, mit Nullbyte am Schluß)
  6003. #     falls tolerantp evtl.: nullobj
  6004. # < filestatus: Falls Name/=NIL: NULL falls das File nicht existiert,
  6005. #                                sonst ein Pointer auf eine STAT-Information.
  6006. # kann GC auslösen
  6007.   local var struct FileInfoBlock * filestatus;
  6008.   local object assure_dir_exists_ (boolean tolerantp,boolean dirtolerantp);
  6009.   local object assure_dir_exists_(tolerantp,dirtolerantp)
  6010.     var reg5 boolean tolerantp;
  6011.     var reg6 boolean dirtolerantp;
  6012.     { # Zur Auflösung von :PARENTs, die über Root hinaussteigen,
  6013.       # müssen wir das Betriebssystem bemühen. Daher:
  6014.       var reg3 object dir_namestring;
  6015.       {var reg1 uintC stringcount = directory_namestring_parts(STACK_0); # Strings fürs Directory
  6016.        dir_namestring = string_concat(stringcount);
  6017.       }
  6018.       pushSTACK(dir_namestring);
  6019.       dir_namestring = OSnamestring(dir_namestring); # ohne überflüssigen '/' am Schluß
  6020.       # Lock für dieses Directory holen:
  6021.       set_break_sem_4(); # Unterbrechungen währenddessen verhindern
  6022.       begin_system_call();
  6023.      {var reg4 BPTR lock = Lock(TheAsciz(dir_namestring),ACCESS_READ);
  6024.       if (lock==BPTR_NULL)
  6025.         { var reg2 LONG errcode = IoErr();
  6026.           end_system_call();
  6027.           switch (errcode)
  6028.             { case ERROR_OBJECT_NOT_FOUND:
  6029.                 clr_break_sem_4();
  6030.                 if (tolerantp) { skipSTACK(1); return nullobj; }
  6031.                 fehler_dir_not_exists(STACK_0);
  6032.               case ERROR_ACTION_NOT_KNOWN:
  6033.                 # Ein Device, bei dem man keine Locks für Subdirectories holen
  6034.                 # kann! Hierbei muß es sich wohl um ein spezielles Device handeln
  6035.                 # (PIPE, CON, AUX, etc.).
  6036.                 # Wir stoppen die Subdirectory-Überprüfungen. Nicht einmal mehr
  6037.                 # Examine() rufen wir auf. Wir gehen im Gegenteil davon aus, daß
  6038.                 # das File im gewöhnlichen Sinne (noch) nicht existiert.
  6039.                 clr_break_sem_4(); # Unterbrechungen zulassen, da wir nun doch kein Lock belegt haben
  6040.                 if (namenullp(STACK_(0+1))) # kein File angesprochen?
  6041.                   { return popSTACK(); } # ja -> fertig
  6042.                   else
  6043.                   { var reg1 uintC stringcount = 1; # directory_namestring schon auf dem STACK
  6044.                     stringcount += file_namestring_parts(STACK_(0+1)); # Strings für den Filename
  6045.                     pushSTACK(O(null_string)); stringcount++; # und Nullbyte
  6046.                    {var reg2 object namestring = string_concat(stringcount); # zusammenhängen
  6047.                     filestatus = (struct FileInfoBlock *)NULL; # File existiert nicht, sagen wir
  6048.                     return namestring;
  6049.                   }}
  6050.               default:
  6051.                 OS_error();
  6052.         }   }
  6053.       end_system_call();
  6054.       dir_namestring = popSTACK();
  6055.       # und überprüfen, ob's ein Directory ist:
  6056.       { var LONGALIGNTYPE(struct FileInfoBlock) fib;
  6057.         var reg2 struct FileInfoBlock * fibptr = LONGALIGN(&fib);
  6058.         begin_system_call();
  6059.        {var reg1 LONG ergebnis = Examine(lock,fibptr);
  6060.         if (!ergebnis) { UnLock(lock); OS_error(); }
  6061.         if (!(fibptr->fib_DirEntryType > 0)) # etwa kein Directory?
  6062.           { UnLock(lock);
  6063.             end_system_call();
  6064.             if (tolerantp) { return nullobj; }
  6065.             # STACK_0 = Wert für Slot PATHNAME von FILE-ERROR
  6066.             pushSTACK(dir_namestring);
  6067.             pushSTACK(TheSubr(subr_self)->name);
  6068.             //: DEUTSCH "~: ~ ist ein File und kein Directory."
  6069.             //: ENGLISH "~: ~ names a file, not a directory"
  6070.             //: FRANCAIS "~ : ~ est un fichier et non un répertoire."
  6071.             fehler(file_error, GETTEXT("~: ~ names a file, not a directory"));
  6072.           }
  6073.         end_system_call();
  6074.       }}
  6075.       # Lock zum Truename machen:
  6076.       {var reg1 object new_pathname = directory_truename(lock); # macht clr_break_sem_4();
  6077.        var reg2 object old_pathname = STACK_0;
  6078.        ThePathname(new_pathname)->pathname_name = ThePathname(old_pathname)->pathname_name;
  6079.        ThePathname(new_pathname)->pathname_type = ThePathname(old_pathname)->pathname_type;
  6080.        STACK_0 = new_pathname;
  6081.      }}
  6082.      {var reg4 object pathname = STACK_0;
  6083.       # Information zum angesprochenen File holen:
  6084.       if (namenullp(pathname)) # kein File angesprochen?
  6085.         { return directory_namestring(pathname); } # ja -> fertig
  6086.       { var reg2 uintC stringcount = 0;
  6087.         stringcount += directory_namestring_parts(pathname); # Strings fürs Directory
  6088.         stringcount += file_namestring_parts(pathname); # Strings für den Filename
  6089.         pushSTACK(O(null_string)); stringcount++; # und Nullbyte
  6090.        {var reg1 object namestring = string_concat(stringcount); # zusammenhängen
  6091.         # Lock für dieses File holen:
  6092.           begin_system_call();
  6093.         { var reg3 BPTR lock = Lock(TheAsciz(namestring),ACCESS_READ);
  6094.           if (lock==BPTR_NULL)
  6095.             { if (!(IoErr()==ERROR_OBJECT_NOT_FOUND)) { OS_error(); }
  6096.               end_system_call();
  6097.               # File existiert nicht.
  6098.               filestatus = (struct FileInfoBlock *)NULL; return namestring;
  6099.             }
  6100.           end_system_call();
  6101.           # File existiert.
  6102.           # Information holen:
  6103.          {local var LONGALIGNTYPE(struct FileInfoBlock) status;
  6104.           var reg1 struct FileInfoBlock * statusptr = LONGALIGN(&status);
  6105.           begin_system_call();
  6106.           if (! Examine(lock,statusptr) ) { UnLock(lock); OS_error(); }
  6107.           UnLock(lock);
  6108.           end_system_call();
  6109.           if (statusptr->fib_DirEntryType > 0 && !dirtolerantp) # Ist es ein Directory?
  6110.             { # STACK_0 = Wert für Slot PATHNAME von FILE-ERROR
  6111.               pushSTACK(whole_namestring(STACK_0));
  6112.               pushSTACK(TheSubr(subr_self)->name);
  6113.               //: DEUTSCH "~: ~ ist ein Directory und kein File."
  6114.               //: ENGLISH "~: ~ names a directory, not a file"
  6115.               //: FRANCAIS "~ : ~ désigne un répertoire et non un fichier."
  6116.               fehler(file_error, GETTEXT("~: ~ names a directory, not a file"));
  6117.             }
  6118.             else
  6119.             # normales File
  6120.             { pushSTACK(namestring);
  6121.               # Die Groß-/Kleinschreibung des Truename wird bestimmt durch
  6122.               # das bereits existierende File.
  6123.               pushSTACK(asciz_to_string(&statusptr->fib_FileName[0]));
  6124.               split_name_type(1);
  6125.              {var reg1 object pathname = STACK_(0+3); # der kopierte Pathname
  6126.               ThePathname(pathname)->pathname_type = popSTACK();
  6127.               ThePathname(pathname)->pathname_name = popSTACK();
  6128.               # Fertig.
  6129.               filestatus = statusptr;
  6130.               return popSTACK(); # namestring
  6131.             }}
  6132.      }}}}}
  6133.     }
  6134.  
  6135. # UP: Stellt sicher, daß das Directory eines Pathname existiert.
  6136. # Sonst Fehlermeldung.
  6137. # assure_dir_exists(tolerantp)
  6138. # > STACK_0: absoluter Pathname ohne Wildcards im Directory
  6139. # > tolerantp: Flag, ob ein Fehler vermieden werden soll
  6140. # < ergebnis:
  6141. #     falls Name=NIL: Directory-Namestring (für RISCOS, mit '.' am Schluß)
  6142. #     falls Name/=NIL: Namestring (für RISCOS, mit Nullbyte am Schluß)
  6143. #     falls tolerantp evtl.: nullobj
  6144. # < filestatus: Falls Name/=NIL: NULL falls das File nicht existiert,
  6145. #                                sonst ein Pointer auf eine STAT-Information.
  6146. # kann GC auslösen
  6147.   local object assure_dir_exists (boolean tolerantp);
  6148.   local object assure_dir_exists(tolerantp)
  6149.     var reg5 boolean tolerantp;
  6150.     { return assure_dir_exists_(tolerantp,FALSE);
  6151.     }
  6152.  
  6153. # Dasselbe unter der Annahme, daß das Directory bereits existiert.
  6154. # (Keine Vereinfachung, da wir ja den Truename bestimmen müssen.)
  6155.   global object assume_dir_exists (void);
  6156.   global object assume_dir_exists()
  6157.     { subr_self = L(open); return assure_dir_exists(FALSE); }
  6158.  
  6159. #endif
  6160.  
  6161. #ifdef PATHNAME_UNIX
  6162.  
  6163. # UP: Liefert das aktuelle Directory.
  6164. # < ergebnis: aktuelles Directory (als Pathname)
  6165. # kann GC auslösen
  6166.   local object default_directory (void);
  6167.   local object default_directory()
  6168.     # Working Directory (von UNIX) ist das aktuelle Directory:
  6169.     { var char path_buffer[MAXPATHLEN]; # vgl. GETWD(3)
  6170.       # Working Directory in path_buffer ablegen:
  6171.       begin_system_call();
  6172.       if ( getwd(&path_buffer[0]) ==NULL)
  6173.         { pushSTACK(O(punkt_string)); # Wert für Slot PATHNAME von FILE-ERROR
  6174.           pushSTACK(asciz_to_string(&path_buffer[0])); # Meldung
  6175.           //: DEUTSCH "UNIX-Fehler bei GETWD: ~"
  6176.           //: ENGLISH "UNIX error while GETWD: ~"
  6177.           //: FRANCAIS "Erreur UNIX pendant GETWD : ~"
  6178.           fehler(file_error, GETTEXT("UNIX error while GETWD: ~"));
  6179.         }
  6180.       end_system_call();
  6181.       # Es muß mit '/' anfangen:
  6182.       if (!(path_buffer[0] == '/'))
  6183.         { pushSTACK(O(punkt_string)); # Wert für Slot PATHNAME von FILE-ERROR
  6184.           pushSTACK(asciz_to_string(&path_buffer[0]));
  6185.           //: DEUTSCH "UNIX GETWD lieferte ~"
  6186.           //: ENGLISH "UNIX GETWD returned ~"
  6187.           //: FRANCAIS "GETWD d'UNIX a retourné ~"
  6188.           fehler(file_error, GETTEXT("UNIX GETWD returned ~"));
  6189.         }
  6190.       # in Pathname umwandeln:
  6191.       return asciz_dir_to_pathname(&path_buffer[0]);
  6192.     }
  6193.  
  6194. # UP: Füllt Default-Directory in einen Pathname ein.
  6195. # use_default_dir(pathname)
  6196. # > pathname: nicht-Logical Pathname
  6197. # < ergebnis: neuer Pathname, bei dem Directory kein :RELATIVE enthält.
  6198. #             (kurz: "absoluter Pathname")
  6199. # kann GC auslösen
  6200.   local object use_default_dir (object pathname);
  6201.   local object use_default_dir(pathname)
  6202.     var reg3 object pathname;
  6203.     { # erst den Pathname kopieren:
  6204.       pathname = copy_pathname(pathname);
  6205.       # Dann das Default-Directory in den Pathname einbauen:
  6206.       { var reg2 object subdirs = ThePathname(pathname)->pathname_directory;
  6207.         # Fängt pathname-directory mit :RELATIVE an?
  6208.         if (eq(Car(subdirs),S(Krelative)))
  6209.           { # ja -> Ersetze :RELATIVE durch default-subdirs, d.h.
  6210.             # bilde  (append default-subdirs (cdr subdirs))
  6211.             #      = (nreconc (reverse default-subdirs) (cdr subdirs))
  6212.             pushSTACK(pathname);
  6213.             pushSTACK(Cdr(subdirs));
  6214.            {var reg1 object temp = default_directory();
  6215.             temp = ThePathname(temp)->pathname_directory;
  6216.             temp = reverse(temp);
  6217.             subdirs = nreconc(temp,popSTACK());
  6218.             pathname = popSTACK();
  6219.             # in den Pathname eintragen:
  6220.             ThePathname(pathname)->pathname_directory = subdirs;
  6221.           }}
  6222.       }
  6223.       return pathname;
  6224.     }
  6225.  
  6226. # UP: Stellt sicher, daß das Directory eines Pathname existiert, und löst
  6227. # dabei symbolische Links auf.
  6228. # assure_dir_exists_(tolerantp,dirtolerantp)
  6229. # > STACK_0: nicht-Logical Pathname, bei dem Directory kein :RELATIVE enthält.
  6230. # > tolerantp: Flag, ob ein Fehler vermieden werden soll
  6231. # > dirtolerantp:  Don't complain if file ends being a directory
  6232. # > subr_self: Aufrufer (ein SUBR)
  6233. # < STACK_0: (evtl. derselbe) Pathname, wobei weder fürs Directory noch
  6234. #            für den Filenamen ein symbolisches Link zu verfolgen ist.
  6235. # < ergebnis:
  6236. #     falls Name=NIL: Directory-Namestring (für UNIX, mit '/' am Schluß)
  6237. #     falls Name/=NIL: Namestring (für UNIX, mit Nullbyte am Schluß)
  6238. #     falls tolerantp evtl.: nullobj
  6239. # < filestatus: Falls Name/=NIL: NULL falls das File nicht existiert,
  6240. #                                sonst ein Pointer auf eine STAT-Information.
  6241. # kann GC auslösen
  6242.   local var struct stat * filestatus;
  6243.   local object assure_dir_exists_ (boolean tolerantp, boolean dirtolerantp);
  6244.   local object assure_dir_exists_(tolerantp,dirtolerantp)
  6245.     var reg7 boolean tolerantp;
  6246.     var reg8 boolean dirtolerantp;
  6247.     { var reg6 uintC allowed_links = MAXSYMLINKS; # Anzahl der noch erlaubten symbolischen Links
  6248.       loop # Schleife über die aufzulösenden symbolischen Links
  6249.         { # Truepath des Directory bestimmen:
  6250.           var char path_buffer[MAXPATHLEN]; # vgl. REALPATH(3)
  6251.           { var reg2 uintC stringcount = directory_namestring_parts(STACK_0); # Strings zum Directory
  6252.             pushSTACK(O(punkt_string)); # und "."
  6253.             pushSTACK(O(null_string)); # und Nullbyte
  6254.            {var reg1 object string = string_concat(stringcount+1+1); # zusammenhängen
  6255.             # symbolische Links darin auflösen:
  6256.             begin_system_call();
  6257.             if ( realpath(TheAsciz(string),&path_buffer[0]) ==NULL)
  6258.               { end_system_call();
  6259.                 if (!(errno==ENOENT)) { OS_error(); }
  6260.                 if (tolerantp) { return nullobj; }
  6261.                 fehler_dir_not_exists(asciz_dir_to_pathname(&path_buffer[0])); # fehlerhafte Komponente
  6262.               }
  6263.             end_system_call();
  6264.           }}
  6265.           # Neuer Directory-Path muß mit '/' anfangen:
  6266.           if (!(path_buffer[0] == '/'))
  6267.             { # STACK_0 = Wert für Slot PATHNAME von FILE-ERROR
  6268.               pushSTACK(asciz_to_string(&path_buffer[0]));
  6269.               //: DEUTSCH "UNIX REALPATH lieferte ~"
  6270.               //: ENGLISH "UNIX REALPATH returned ~"
  6271.               //: FRANCAIS "REALPATH d'UNIX a retourné ~"
  6272.               fehler(file_error, GETTEXT("UNIX REALPATH returned ~"));
  6273.             }
  6274.           # Am Schluß evtl. ein '/' anfügen:
  6275.           {var reg1 char* pathptr = &path_buffer[0];
  6276.            var reg2 uintL len = 0; # Stringlänge
  6277.            until (*pathptr == 0) { pathptr++; len++; } # ASCIZ-Stringende suchen
  6278.            if (!((len>0) && (pathptr[-1]=='/')))
  6279.              { *pathptr = '/'; len++; } # ein '/' anfügen
  6280.           # und in einen String umwandeln:
  6281.            { var reg4 object new_string = make_string((uintB*)(&path_buffer[0]),len);
  6282.           # Pathname draus machen und dessen Directory verwenden:
  6283.             {var reg3 object new_pathname = coerce_pathname(new_string);
  6284.              ThePathname(STACK_0)->pathname_directory
  6285.                = ThePathname(new_pathname)->pathname_directory;
  6286.           }}}
  6287.           # Information zum angesprochenen File holen:
  6288.           if (namenullp(STACK_0)) # kein File angesprochen?
  6289.             { return directory_namestring(STACK_0); } # ja -> fertig
  6290.           { var reg5 object pathname = STACK_0;
  6291.             var reg2 uintC stringcount = 0;
  6292.             stringcount += directory_namestring_parts(pathname); # Strings fürs Directory
  6293.             stringcount += file_namestring_parts(pathname); # Strings für den Filename
  6294.             pushSTACK(O(null_string)); stringcount++; # und Nullbyte
  6295.            {var reg1 object namestring = string_concat(stringcount); # zusammenhängen
  6296.             # Information holen:
  6297.             local struct stat status;
  6298.             begin_system_call();
  6299.             if (!( lstat(TheAsciz(namestring),&status) ==0))
  6300.               { if (!(errno==ENOENT)) { OS_error(); }
  6301.                 # File existiert nicht.
  6302.                 end_system_call();
  6303.                 filestatus = (struct stat *)NULL; return namestring;
  6304.               }
  6305.             end_system_call();
  6306.             # File existiert.
  6307.             if (S_ISDIR(status.st_mode) && !dirtolerantp) # Ist es ein Directory?
  6308.               { # STACK_0 = Wert für Slot PATHNAME von FILE-ERROR
  6309.                 pushSTACK(whole_namestring(STACK_0));
  6310.                 pushSTACK(TheSubr(subr_self)->name);
  6311.                 //: DEUTSCH "~: ~ ist ein Directory und kein File."
  6312.                 //: ENGLISH "~: ~ names a directory, not a file"
  6313.                 //: FRANCAIS "~ : ~ est un répertoire et non un fichier."
  6314.                 fehler(file_error, GETTEXT("~: ~ names a directory, not a file"));
  6315.               }
  6316.             #ifdef HAVE_LSTAT
  6317.             elif (S_ISLNK(status.st_mode)) # Ist es ein symbolisches Link?
  6318.               # ja -> weiterverfolgen:
  6319.               { if (allowed_links==0) # keine Links mehr erlaubt?
  6320.                   { errno = ELOOP_VALUE; OS_error(); } # ja -> UNIX-Error ELOOP simulieren
  6321.                 allowed_links--; # danach ist ein Link weniger erlaubt
  6322.                {var reg4 uintL linklen = status.st_size; # vermutliche Länge des Link-Inhalts
  6323.                 retry_readlink:
  6324.                   pushSTACK(namestring); # Namestring retten
  6325.                 { var reg3 object linkbuf = allocate_string(linklen); # Buffer für den Link-Inhalt
  6326.                   namestring = popSTACK();
  6327.                   # Link-Inhalt lesen:
  6328.                   begin_system_call();
  6329.                  {var reg1 int result = readlink(TheAsciz(namestring),TheAsciz(linkbuf),linklen);
  6330.                   end_system_call();
  6331.                   if (result<0) { OS_error(); }
  6332.                   if (!(result == (int)linklen)) # manchmal (AIX, NFS) stimmt status.st_size nicht
  6333.                     { linklen = result; goto retry_readlink; }
  6334.                   # Daraus ein Pathname machen:
  6335.                   # (MERGE-PATHNAMES (PARSE-NAMESTRING linkbuf) pathname)
  6336.                   pushSTACK(linkbuf); funcall(L(parse_namestring),1);
  6337.                   pushSTACK(value1); pushSTACK(STACK_(0+1)); funcall(L(merge_pathnames),2);
  6338.                   STACK_0 = value1;
  6339.               }}}}
  6340.             #endif
  6341.             else
  6342.               # normales File
  6343.               { filestatus = &status; return namestring; }
  6344.           }}
  6345.     }   }
  6346.  
  6347. # UP: Stellt sicher, daß das Directory eines Pathname existiert.
  6348. # Sonst Fehlermeldung.
  6349. # assure_dir_exists(tolerantp)
  6350. # > STACK_0: absoluter Pathname ohne Wildcards im Directory
  6351. # > tolerantp: Flag, ob ein Fehler vermieden werden soll
  6352. # < ergebnis:
  6353. #     falls Name=NIL: Directory-Namestring (für RISCOS, mit '.' am Schluß)
  6354. #     falls Name/=NIL: Namestring (für RISCOS, mit Nullbyte am Schluß)
  6355. #     falls tolerantp evtl.: nullobj
  6356. # < filestatus: Falls Name/=NIL: NULL falls das File nicht existiert,
  6357. #                                sonst ein Pointer auf eine STAT-Information.
  6358. # kann GC auslösen
  6359.   local object assure_dir_exists (boolean tolerantp);
  6360.   local object assure_dir_exists(tolerantp)
  6361.     var reg5 boolean tolerantp;
  6362.     { return assure_dir_exists_(tolerantp,FALSE);
  6363.     }
  6364.  
  6365. # Dasselbe unter der Annahme, daß das Directory bereits existiert.
  6366. # (Keine Vereinfachung, da das File ein symbolisches Link in ein anderes
  6367. # Directory sein kann, und dieses muß dann als existent überprüft werden.)
  6368.   global object assume_dir_exists (void);
  6369.   global object assume_dir_exists()
  6370.     { subr_self = L(open); return assure_dir_exists(FALSE); }
  6371.  
  6372. #endif
  6373.  
  6374. #ifdef PATHNAME_RISCOS
  6375.  
  6376. # Ein "absoluter Pathname" ist ein Pathname, bei dem Directory mit
  6377. # (:ABSOLUTE :ROOT ...) beginnt.
  6378.  
  6379. # UP: Liefert das aktuelle Directory.
  6380. # < ergebnis: aktuelles Directory (als Pathname)
  6381. # kann GC auslösen
  6382.   local object default_directory (void);
  6383.   local object default_directory()
  6384.     # Working Directory (von RISCOS) ist das aufgelöste "@":
  6385.     { var char path_buffer[MAXPATHLEN];
  6386.       # Working Directory in path_buffer ablegen:
  6387.       begin_system_call();
  6388.       if ( realpath("@",&path_buffer[0]) ==NULL) { OS_error(); }
  6389.       end_system_call();
  6390.       # in Pathname umwandeln:
  6391.       return asciz_dir_to_pathname(&path_buffer[0]);
  6392.     }
  6393.  
  6394. #if 0 # unbenutzt
  6395. # UP: Convert a valid RISCOS file namestring to an absolute pathname.
  6396. # canonicalise_filename(filename)
  6397. # > filename: Simple-Asciz-String
  6398. # < result: absolute pathname
  6399.   local object canonicalise_filename (object filename);
  6400.   local object canonicalise_filename(filename)
  6401.     var reg1 object filename;
  6402.     { var char path_buffer[MAXPATHLEN];
  6403.       begin_system_call();
  6404.       if ( realpath(TheAsciz(filename),&path_buffer[0]) ==NULL) { OS_error(); }
  6405.       end_system_call();
  6406.       # in Pathname umwandeln:
  6407.       return coerce_pathname(asciz_to_string(&path_buffer[0]));
  6408.     }
  6409. #endif
  6410.  
  6411. # UP: Convert a valid RISCOS directory namestring to an absolute pathname.
  6412. # canonicalise_dirname(pathname,dirname)
  6413. # > pathname: Pathname whose host name and device is to be used
  6414. # > dirname: Simple-String, ends with '.'
  6415. # < result: absolute pathname
  6416.   local object canonicalise_dirname (object pathname, object dirname);
  6417.   local object canonicalise_dirname(pathname,dirname)
  6418.     var reg5 object pathname;
  6419.     var reg4 object dirname;
  6420.     { var reg3 uintC stringcount = host_namestring_parts(pathname); # Strings für den Host
  6421.       # Device, vgl. directory_namestring_parts():
  6422.       { var reg1 object device = ThePathname(pathname)->pathname_device;
  6423.         if (!(nullp(device))) # NIL -> kein String
  6424.           { pushSTACK(O(doppelpunkt_string)); # ":"
  6425.             pushSTACK(device); # Device auf den Stack
  6426.             pushSTACK(O(punkt_string)); # "."
  6427.             stringcount += 3; # und mitzählen
  6428.       }   }
  6429.       pushSTACK(dirname);
  6430.      {var reg1 object dir_string = string_concat(stringcount+1);
  6431.       # Punkt am Schluß durch Nullbyte ersetzen:
  6432.       TheSstring(dir_string)->data[TheSstring(dir_string)->length-1] = '\0';
  6433.       # absolut machen:
  6434.       { var char path_buffer[MAXPATHLEN];
  6435.         begin_system_call();
  6436.         if ( realpath(TheAsciz(dir_string),&path_buffer[0]) ==NULL) { OS_error(); }
  6437.         end_system_call();
  6438.         # in Pathname umwandeln:
  6439.         return asciz_dir_to_pathname(&path_buffer[0]);
  6440.     }}}
  6441.  
  6442. # UP: Füllt Default-Directory in einen Pathname ein.
  6443. # use_default_dir(pathname)
  6444. # > pathname: nicht-Logical Pathname
  6445. # < ergebnis: neuer Pathname, bei dem Directory kein :RELATIVE u.ä. enthält.
  6446. #             (kurz: "absoluter Pathname")
  6447. # kann GC auslösen
  6448.   local object use_default_dir (object pathname);
  6449.   local object use_default_dir(pathname)
  6450.     var reg3 object pathname;
  6451.     { var reg5 boolean resolved_root = FALSE;
  6452.       retry:
  6453.       # erst den Pathname kopieren:
  6454.       pathname = copy_pathname(pathname);
  6455.      {var reg2 object subdirs = ThePathname(pathname)->pathname_directory;
  6456.       # Ist das Device angegeben, so muß das Directory mit (:ABSOLUTE :ROOT ...)
  6457.       # anfangen (oder mit (:RELATIVE ...) - das wird ersetzt).
  6458.       if (!nullp(ThePathname(pathname)->pathname_device))
  6459.         { if (eq(Car(subdirs),S(Krelative)))
  6460.             { pushSTACK(pathname); # pathname retten
  6461.               pushSTACK(allocate_cons());
  6462.              {var reg1 object new_cons = allocate_cons();
  6463.               subdirs = popSTACK();
  6464.               pathname = popSTACK(); # pathname zurück
  6465.               Car(subdirs) = S(Kabsolute); Cdr(subdirs) = new_cons;
  6466.               Car(new_cons) = S(Kroot); Cdr(new_cons) = Cdr(ThePathname(pathname)->pathname_directory);
  6467.               ThePathname(pathname)->pathname_directory = subdirs;
  6468.             }}
  6469.           elif (!(eq(Car(subdirs),S(Kabsolute)) && eq(Car(Cdr(subdirs)),S(Kroot))))
  6470.             { pushSTACK(pathname); # Wert für Slot PATHNAME von FILE-ERROR
  6471.               pushSTACK(pathname);
  6472.               pushSTACK(O(root_string));
  6473.               pushSTACK(TheSubr(subr_self)->name);
  6474.               //: DEUTSCH "~: Ist ein Device angegeben, muß das Directory mit ~ anfangen: ~"
  6475.               //: ENGLISH "~: If a device is specified, the directory must begin with ~: ~"
  6476.               //: FRANCAIS "~ : Quand un composant DEVICE est spécifié, le répertoire doit commencer par ~: ~"
  6477.               fehler(file_error, GETTEXT("~: If a device is specified, the directory must begin with ~: ~"));
  6478.             }
  6479.         }
  6480.       pushSTACK(pathname); # pathname retten
  6481.       {var reg4 object defaults;
  6482.        if (eq(Car(subdirs),S(Krelative)))
  6483.          { pushSTACK(Cdr(subdirs)); defaults = default_directory(); }
  6484.        else # (eq(Car(subdirs),S(Kabsolute)))
  6485.          { var reg1 object next = Car(Cdr(subdirs));
  6486.            pushSTACK(Cdr(Cdr(subdirs)));
  6487.            if (eq(next,S(Kroot))) # :ROOT -> "$." auflösen oder fertig
  6488.              { # "$." wird nur dann aufgelöst, wenn Host oder Device noch
  6489.                # unbekannt sind, aber nur einmal (um eine Endlosschleife zu
  6490.                # verhindern). Ob Host oder Device =NIL sind, ist nämlich
  6491.                # nicht so wichtig.
  6492.                if (!resolved_root
  6493.                    && (nullp(ThePathname(pathname)->pathname_host)
  6494.                        || nullp(ThePathname(pathname)->pathname_device)
  6495.                   )   )
  6496.                  { defaults = canonicalise_dirname(pathname,O(root_string));
  6497.                    resolved_root = TRUE;
  6498.                  }
  6499.                else
  6500.                  { goto resolved; }
  6501.              }
  6502.            elif (eq(next,S(Khome))) # :HOME -> "&." auflösen
  6503.              { defaults = canonicalise_dirname(pathname,O(home_string)); }
  6504.            elif (eq(next,S(Kcurrent))) # :CURRENT -> "@." auflösen
  6505.              { defaults = canonicalise_dirname(pathname,O(current_string)); }
  6506.            elif (eq(next,S(Klibrary))) # :LIBRARY -> "%." auflösen
  6507.              { defaults = canonicalise_dirname(pathname,O(library_string)); }
  6508.            elif (eq(next,S(Kprevious))) # :PREVIOUS -> "\\." auflösen
  6509.              { defaults = canonicalise_dirname(pathname,O(previous_string)); }
  6510.            else
  6511.              { NOTREACHED }
  6512.          }
  6513.        # Stackaufbau: pathname, rest-subdirs.
  6514.        # Nicht ganz so wie bei MERGE-PATHNAMES verfahren:
  6515.        # bilde  (append default-subdirs rest-subdirs)
  6516.        #      = (nreconc (reverse default-subdirs) rest-subdirs)
  6517.        pathname = STACK_1;
  6518.        ThePathname(pathname)->pathname_host = ThePathname(defaults)->pathname_host;
  6519.        ThePathname(pathname)->pathname_device = ThePathname(defaults)->pathname_device;
  6520.        defaults = ThePathname(defaults)->pathname_directory;
  6521.        defaults = reverse(defaults); subdirs = nreconc(defaults,popSTACK());
  6522.        pathname = popSTACK();
  6523.        ThePathname(pathname)->pathname_directory = subdirs;
  6524.        # Es könnte sein, daß auch jetzt noch nicht alles aufgelöst ist.
  6525.        goto retry;
  6526.      }}
  6527.      resolved: # Stackaufbau: pathname, subdir-oldlist.
  6528.       # Liste durchgehen und dabei neu aufconsen, dabei "^." verarbeiten.
  6529.       # (Sonst müßte dies assure_dir_exists() machen.)
  6530.       pushSTACK(S(Kroot)); pushSTACK(S(Kabsolute));
  6531.       { var reg1 object newlist = listof(2); pushSTACK(newlist); }
  6532.       # Stackaufbau: pathname, subdir-oldlist, subdir-newlist.
  6533.       while (mconsp(STACK_1)) # Bis oldlist am Ende ist:
  6534.         { var reg4 object subdir = Car(STACK_1); # nächstes subdir
  6535.           if (eq(subdir,S(Kparent)))
  6536.             # = :PARENT -> newlist um eins verkürzen:
  6537.             { if (matomp(Cdr(Cdr(STACK_0)))) # newlist (bis auf :ABSOLUTE und :ROOT) leer ?
  6538.                 { # :PARENT von "$." aus liefert Error
  6539.                   pushSTACK(STACK_2); # Wert für Slot PATHNAME von FILE-ERROR
  6540.                   pushSTACK(O(root_string)); # "$."
  6541.                   pushSTACK(directory_namestring(STACK_(2+2))); # Directory von pathname
  6542.                   //: DEUTSCH "Directory ~ oberhalb ~ existiert nicht."
  6543.                   //: ENGLISH "no directory ~ above ~"
  6544.                   //: FRANCAIS "Il n'y a pas de répertoire ~ au delà de ~."
  6545.                   fehler(file_error, GETTEXT("no directory ~ above ~"));
  6546.                 }
  6547.               STACK_0 = Cdr(STACK_0);
  6548.             }
  6549.           else
  6550.             { # newlist um eins verlängern:
  6551.               pushSTACK(subdir);
  6552.              {var reg1 object new_cons = allocate_cons();
  6553.               Car(new_cons) = popSTACK();
  6554.               Cdr(new_cons) = STACK_0;
  6555.               STACK_0 = new_cons;
  6556.             }}
  6557.           STACK_1 = Cdr(STACK_1);
  6558.         }
  6559.      {var reg2 object subdirs = nreverse(popSTACK()); # newlist, wieder umdrehen
  6560.       skipSTACK(1);
  6561.       pathname = popSTACK();
  6562.       ThePathname(pathname)->pathname_directory = subdirs; # in den Pathname eintragen
  6563.       return pathname;
  6564.     }}
  6565.  
  6566. # UP: Liefert den Namestring eines Pathname als ASCIZ-String.
  6567. # namestring_asciz(dir_namestring)
  6568. # > STACK_0: nicht-Logical Pathname
  6569. # > dir_namestring: Directory-Namestring
  6570. # < ergebnis: Namestring (für RISCOS, mit Name/Type vertauscht, mit Nullbyte am Schluß)
  6571. # kann GC auslösen
  6572.   local object namestring_asciz (object dir_namestring);
  6573.   local object namestring_asciz(dir_namestring)
  6574.     var reg3 object dir_namestring;
  6575.     { var reg1 object pathname = STACK_0;
  6576.       var reg2 uintC stringcount;
  6577.       pushSTACK(dir_namestring); # Directory-Namestring als 1. String
  6578.       stringcount = # und Strings zum Filenamen
  6579.         (nullp(ThePathname(pathname)->pathname_type)
  6580.          ? nametype_namestring_parts(ThePathname(pathname)->pathname_name,
  6581.                                      ThePathname(pathname)->pathname_type,
  6582.                                      ThePathname(pathname)->pathname_version)
  6583.          # Name und Typ vertauschen (der Typ wird zu einem Subdirectory-Namen):
  6584.          : nametype_namestring_parts(ThePathname(pathname)->pathname_type,
  6585.                                      ThePathname(pathname)->pathname_name,
  6586.                                      ThePathname(pathname)->pathname_version)
  6587.         );
  6588.       pushSTACK(O(null_string)); # und String mit Nullbyte
  6589.       return string_concat(1+stringcount+1); # zusammenhängen
  6590.     }
  6591.  
  6592. # UP: Stellt sicher, daß das Directory eines Pathname existiert.
  6593. # Sonst Fehlermeldung.
  6594. # assure_dir_exists_(tolerantp,dirtolerantp)
  6595. # > STACK_0: absoluter Pathname ohne Wildcards im Directory
  6596. # > tolerantp: Flag, ob ein Fehler vermieden werden soll
  6597. # > dirtolerantp: Don't complain if pathname is a directory
  6598. # < ergebnis:
  6599. #     falls Name=NIL: Directory-Namestring (für RISCOS, mit '.' am Schluß)
  6600. #     falls Name/=NIL: Namestring (für RISCOS, mit Nullbyte am Schluß)
  6601. #     falls tolerantp evtl.: nullobj
  6602. # < filestatus: Falls Name/=NIL: NULL falls das File nicht existiert,
  6603. #                                sonst ein Pointer auf eine STAT-Information.
  6604. # kann GC auslösen
  6605.   local var struct stat * filestatus;
  6606.   local object assure_dir_exists_ (boolean tolerantp, boolean dirtolerantp);
  6607.   local object assure_dir_exists_(tolerantp,dirtolerantp)
  6608.     var reg5 boolean tolerantp;
  6609.     var reg6 boolean dirtolerantp;
  6610.     { var reg4 object pathname = STACK_0;
  6611.       var reg2 uintC stringcount = host_namestring_parts(pathname); # Strings für den Host
  6612.       stringcount += directory_namestring_parts(pathname); # Strings fürs Directory
  6613.      {var reg1 object dir_namestring = string_concat(stringcount); # zusammenhängen
  6614.       # Existenztest:
  6615.       var struct stat statbuf;
  6616.       var reg1 uintL len = TheSstring(dir_namestring)->length;
  6617.       ASSERT((len > 0) && (TheSstring(dir_namestring)->data[len-1]=='.'));
  6618.       TheSstring(dir_namestring)->data[len-1] = '\0'; # '.' am Schluß durch Nullbyte ersetzen
  6619.       begin_system_call();
  6620.       if (stat(TheAsciz(dir_namestring),&statbuf) < 0)
  6621.         { end_system_call();
  6622.           if (tolerantp && (errno==ENOENT)) { return nullobj; }
  6623.           OS_error();
  6624.         }
  6625.       end_system_call();
  6626.       TheSstring(dir_namestring)->data[len-1] = '.'; # '.' wieder zurück
  6627.       if (!S_ISDIR(statbuf.st_mode)) # gefundene Datei kein Unterdirectory ?
  6628.         { if (tolerantp) { return nullobj; }
  6629.           fehler_dir_not_exists(dir_namestring);
  6630.         }
  6631.       # Information zum angesprochenen File holen:
  6632.       if (namenullp(STACK_0)) # kein File angesprochen?
  6633.         { return dir_namestring; } # ja -> fertig
  6634.         else
  6635.         { var reg3 object namestring = namestring_asciz(dir_namestring);
  6636.           # Information holen:
  6637.           local struct stat status;
  6638.           begin_system_call();
  6639.           if (stat(TheAsciz(namestring),&status) < 0)
  6640.             { if (!(errno==ENOENT)) { OS_error(); }
  6641.               # File existiert nicht.
  6642.               end_system_call();
  6643.               filestatus = (struct stat *)NULL; return namestring;
  6644.             }
  6645.           end_system_call();
  6646.           # File existiert.
  6647.           if (S_ISDIR(status.st_mode) && !dirtolerantp) # Ist es ein Directory?
  6648.             { # STACK_0 = Wert für Slot PATHNAME von FILE-ERROR
  6649.               pushSTACK(whole_namestring(STACK_0));
  6650.               pushSTACK(TheSubr(subr_self)->name);
  6651.               //: DEUTSCH "~: ~ ist ein Directory und kein File."
  6652.               //: ENGLISH "~: ~ names a directory, not a file"
  6653.               //: FRANCAIS "~ : ~ est un répertoire et non un fichier."
  6654.               fehler(file_error, GETTEXT("~: ~ names a directory, not a file"));
  6655.             }
  6656.           else
  6657.             # normales File
  6658.             { filestatus = &status; return namestring; }
  6659.     }}  }
  6660.  
  6661. # UP: Stellt sicher, daß das Directory eines Pathname existiert.
  6662. # Sonst Fehlermeldung.
  6663. # assure_dir_exists(tolerantp)
  6664. # > STACK_0: absoluter Pathname ohne Wildcards im Directory
  6665. # > tolerantp: Flag, ob ein Fehler vermieden werden soll
  6666. # < ergebnis:
  6667. #     falls Name=NIL: Directory-Namestring (für RISCOS, mit '.' am Schluß)
  6668. #     falls Name/=NIL: Namestring (für RISCOS, mit Nullbyte am Schluß)
  6669. #     falls tolerantp evtl.: nullobj
  6670. # < filestatus: Falls Name/=NIL: NULL falls das File nicht existiert,
  6671. #                                sonst ein Pointer auf eine STAT-Information.
  6672. # kann GC auslösen
  6673.   local object assure_dir_exists (boolean tolerantp)
  6674.   local object assure_dir_exists(tolerantp)
  6675.     var reg5 boolean tolerantp;
  6676.     { return assure_dir_exists_(tolerantp,FALSE);
  6677.     }
  6678.  
  6679. # Dasselbe unter der Annahme, daß das Directory bereits existiert.
  6680. # (Keine Vereinfachung, da wir ja den Truename bestimmen müssen.)
  6681.   global object assume_dir_exists (void);
  6682.   global object assume_dir_exists()
  6683.     { subr_self = L(open); return assure_dir_exists(FALSE); }
  6684.  
  6685. # Ein File "name.type" wird dem RISCOS als "type.name" vermittelt, dabei ist
  6686. # "type" der Name eines Unterverzeichnisses! Soll ein File "name.type" angelegt
  6687. # werden, muß daher zuerst das Unterverzeichnis "type" erzeugt werden.
  6688. # prepare_create(pathname);
  6689. # > pathname: ein Pathname
  6690. # kann GC auslösen
  6691.   local void prepare_create (object pathname);
  6692.   local object pathname_add_subdir (void);
  6693.   local void prepare_create(pathname)
  6694.     var reg1 object pathname;
  6695.     { if (!nullp(ThePathname(pathname)->pathname_type))
  6696.         { # call pathname_add_subdir:
  6697.           pushSTACK(pathname); pushSTACK(ThePathname(pathname)->pathname_type);
  6698.           pathname = pathname_add_subdir();
  6699.           ThePathname(pathname)->pathname_name = NIL;
  6700.           ThePathname(pathname)->pathname_type = NIL;
  6701.           # call MAKE-DIR if the directory does not exist:
  6702.           pushSTACK(subr_self); # subr_self retten
  6703.           pushSTACK(pathname);
  6704.           if (eq(assure_dir_exists(TRUE),nullobj))
  6705.             { funcall(L(make_dir),1); }
  6706.           else
  6707.             { skipSTACK(1); }
  6708.           subr_self = popSTACK(); # subr_self zurück
  6709.     }   }
  6710.  
  6711. #endif
  6712.  
  6713. #if defined(PATHNAME_MSDOS) || defined(PATHNAME_OS2)
  6714. #if 0 # unbenutzt
  6715. # UP: Macht aus einem Directory-Namestring einen, der für DOS geeignet ist.
  6716. # OSnamestring(namestring)
  6717. # > namestring: neu erzeugter Directory-Namestring, mit '\' am Schluß,
  6718. #               ein Simple-String
  6719. # < ergebnis: Namestring zu diesem Directory, im DOS-Format: letzter '\'
  6720. #             gestrichen, falls überflüssig, ASCIZ-String
  6721. # kann GC auslösen
  6722.   local object OSnamestring (object namestring);
  6723.   local object OSnamestring(namestring)
  6724.     var reg1 object namestring;
  6725.     { var reg2 uintL len = TheSstring(namestring)->length;
  6726.       if (len==0) goto ok; # Leerstring -> nichts streichen
  6727.      {var reg3 uintB ch = TheSstring(namestring)->data[len-1];
  6728.       if (!(ch=='\\')) goto ok; # kein '\' am Schluß -> nichts streichen
  6729.       if (len==1) goto ok; # "\" bedeutet Root -> nicht streichen
  6730.       ch = TheSstring(namestring)->data[len-2];
  6731.       if ((ch=='\\') || (ch==':')) # davor ein '\' oder ':'
  6732.         goto ok; # -> bedeutet Parent -> nicht streichen
  6733.       # '\' am Schluß streichen, dann string_to_asciz:
  6734.         namestring = copy_string(namestring); # Länge bleibt dabei gleich!
  6735.         TheSstring(namestring)->data[len-1] = '\0';
  6736.         return namestring;
  6737.       ok: # nichts streichen
  6738.         return string_to_asciz(namestring);
  6739.     }}
  6740. #endif
  6741. # UP: Setzt das Default-Drive und sein Default-Directory neu.
  6742. # change_default();
  6743. # > STACK_0: absoluter Pathname, bei dem Device ein String ist und Directory
  6744. #     kein :RELATIVE, :CURRENT, :PARENT enthält, und Name und Typ =NIL sind.
  6745. # kann GC auslösen
  6746.   local void change_default (void);
  6747.   local void change_default()
  6748.     { # Default-Directory zu diesem Drive neu setzen:
  6749.       { var reg1 object pathname = STACK_0;
  6750.         var reg3 uintC stringcount =
  6751.           directory_namestring_parts(pathname); # Strings fürs Directory
  6752.         # ohne überflüssiges '\' am Schluß, aber mit Nullbyte am Schluß
  6753.         if (mconsp(Cdr(ThePathname(pathname)->pathname_directory)))
  6754.           { STACK_0 = O(null_string); }
  6755.           else
  6756.           { pushSTACK(O(null_string)); stringcount++; }
  6757.        {var reg2 object string = string_concat(stringcount); # zusammenhängen
  6758.         # Default-Directory ändern:
  6759.         begin_system_call();
  6760.         if (!( chdir(TheAsciz(string)) ==0)) { OS_error(); }
  6761.         end_system_call();
  6762.       }}
  6763.       # Default-Drive neu setzen:
  6764.       O(default_drive) = ThePathname(STACK_0)->pathname_device;
  6765.       # *DEFAULT-PATHNAME-DEFAULTS* neu setzen:
  6766.       recalc_defaults_pathname();
  6767.     }
  6768. #endif
  6769. #ifdef PATHNAME_AMIGAOS
  6770. # UP: Setzt das Default-Directory neu.
  6771. # change_default();
  6772. # > STACK_0: absoluter Pathname, bei dem Directory kein :RELATIVE, :CURRENT,
  6773. #     :PARENT enthält, und Name und Typ =NIL sind.
  6774. # kann GC auslösen
  6775.   local void change_default (void);
  6776.   extern BPTR orig_dir_lock; # Lock auf das ursprüngliche Verzeichnis
  6777.                              # (das gehört nicht uns, nicht freigeben!)
  6778.   local void change_default()
  6779.     { var reg3 uintC stringcount =
  6780.         directory_namestring_parts(STACK_0); # Strings fürs Directory
  6781.       var reg2 object dir_namestring = string_concat(stringcount);
  6782.       dir_namestring = OSnamestring(dir_namestring); # Asciz, ohne überflüssigen '/' am Schluß
  6783.       # Default-Directory ändern:
  6784.       set_break_sem_4();
  6785.       begin_system_call();
  6786.       {var reg1 BPTR lock = Lock(TheAsciz(dir_namestring),ACCESS_READ);
  6787.        if (lock==BPTR_NULL) { OS_error(); }
  6788.        lock = CurrentDir(lock); # current directory neu setzen
  6789.        # Lock zum alten current directory merken bzw. aufgeben:
  6790.        if (orig_dir_lock == BPTR_NONE)
  6791.          { orig_dir_lock = lock; }
  6792.          else
  6793.          { UnLock(lock); }
  6794.       }
  6795.       end_system_call();
  6796.       clr_break_sem_4();
  6797.     }
  6798. #endif
  6799. #ifdef PATHNAME_UNIX
  6800. # UP: Setzt das Default-Directory neu.
  6801. # change_default();
  6802. # > STACK_0: absoluter Pathname, bei dem Directory kein :RELATIVE, :CURRENT,
  6803. #     :PARENT enthält, und Name und Typ =NIL sind.
  6804. # kann GC auslösen
  6805.   local void change_default (void);
  6806.   local void change_default()
  6807.     { var reg2 uintC stringcount = host_namestring_parts(STACK_0); # Strings für den Host
  6808.       stringcount += directory_namestring_parts(STACK_0); # Strings fürs Directory
  6809.       pushSTACK(O(null_string)); # und Nullbyte
  6810.      {var reg1 object string = string_concat(stringcount+1); # zusammenhängen
  6811.       # Default-Directory ändern:
  6812.       begin_system_call();
  6813.       if (!( chdir(TheAsciz(string)) ==0)) { OS_error(); }
  6814.       end_system_call();
  6815.     }}
  6816. #endif
  6817. #ifdef PATHNAME_RISCOS
  6818. # UP: Setzt das Default-Directory neu.
  6819. # change_default();
  6820. # > STACK_0: absoluter Pathname, bei dem Name und Typ =NIL sind.
  6821. # kann GC auslösen
  6822.   local void change_default (void);
  6823.   local void change_default()
  6824.     { var reg4 object pathname = STACK_0;
  6825.       var reg2 uintC stringcount = host_namestring_parts(pathname); # Strings für den Host
  6826.       stringcount += directory_namestring_parts(pathname); # Strings fürs Directory
  6827.      {var reg1 object dir_namestring = string_concat(stringcount); # zusammenhängen
  6828.       var reg3 uintL len = TheSstring(dir_namestring)->length;
  6829.       ASSERT((len > 0) && (TheSstring(dir_namestring)->data[len-1]=='.'));
  6830.       TheSstring(dir_namestring)->data[len-1] = '\0'; # '.' am Schluß durch Nullbyte ersetzen
  6831.       begin_system_call();
  6832.       if (!( chdir(TheAsciz(dir_namestring)) ==0)) { OS_error(); }
  6833.       end_system_call();
  6834.     }}
  6835. #endif
  6836.  
  6837. LISPFUN(namestring,1,1,norest,nokey,0,NIL)
  6838. # (NAMESTRING pathname), CLTL S. 417
  6839. # (NAMESTRING pathname t) -> Namestring im externen Format
  6840. #   GEMDOS: ohne Seriennummer, mit Default-Directory
  6841. #   Unix: mit Default-Directory
  6842.   { var reg2 object flag = popSTACK(); # optionales Argument flag
  6843.     var reg1 object pathname = coerce_pathname(popSTACK()); # Argument zu einem Pathname machen
  6844.     #if defined(PATHNAME_UNIX) || defined(PATHNAME_AMIGAOS) || defined(PATHNAME_RISCOS)
  6845.     if (!eq(flag,unbound) && !nullp(flag))
  6846.       # flag /= NIL -> fürs Betriebssystem:
  6847.       { check_no_wildcards(pathname); # mit Wildcards -> Fehler
  6848.         pathname = use_default_dir(pathname); # Default-Directory einfügen
  6849.         # (da GEMDOS/Unix/AMIGAOS das Default-Directory von LISP nicht kennt)
  6850.         value1 = whole_namestring(pathname);
  6851.       }
  6852.       else
  6853.     #endif
  6854.       # normal
  6855.       { value1 = whole_namestring(pathname); }
  6856.     mv_count=1;
  6857.   }
  6858.  
  6859. # Fehlermeldung wegen fehlendem Dateinamen
  6860. # fehler_noname(pathname);
  6861. # > pathname: Pathname
  6862.   nonreturning_function(local, fehler_noname, (object pathname));
  6863.   local void fehler_noname(pathname)
  6864.     var reg1 object pathname;
  6865.     { pushSTACK(pathname); # Wert für Slot PATHNAME von FILE-ERROR
  6866.       pushSTACK(pathname);
  6867.       //: DEUTSCH "Dateiname muß angegeben werden: ~"
  6868.       //: ENGLISH "no file name given: ~"
  6869.       //: FRANCAIS "Un nom de fichier doit être fourni : ~"
  6870.       fehler(file_error, GETTEXT("no file name given: ~"));
  6871.     }
  6872.  
  6873. # Test, ob ein File existiert:
  6874. # if_file_exists(namestring,statement1,statement2);
  6875. # > vorausgegangen: assure_dir_exists()
  6876. # > im STACK: Pathname, wie nach Ausführung von assure_dir_exists(), Name/=NIL
  6877. # > namestring: dessen Namestring als ASCIZ-String
  6878. # Falls das File existiert, wird statement1 ausgeführt, sonst statement2.
  6879.     #define if_file_exists(namestring,statement1,statement2)  \
  6880.       { if (file_exists(namestring)) { statement1; } else { statement2; } }
  6881.     #ifdef MSDOS
  6882.       local int access0 (CONST char* path);
  6883.       local int access0(path)
  6884.         var reg2 CONST char* path;
  6885.         { var reg1 int erg;
  6886.           begin_system_call();
  6887.           erg = access(path,0);
  6888.           end_system_call();
  6889.           return erg;
  6890.         }
  6891.       #define file_exists(namestring)  (access0(TheAsciz(namestring))==0)
  6892.     #endif
  6893.     #ifdef AMIGAOS
  6894.       #define file_exists(namestring)  (!(filestatus == (struct FileInfoBlock *)NULL))
  6895.     #endif
  6896.     #if defined(UNIX) || defined(RISCOS) || defined(WIN32_UNIX)
  6897.       #define file_exists(namestring)  (!(filestatus == (struct stat *)NULL))
  6898.     #endif
  6899.  
  6900. # Fehlermeldung wegen nicht existenter Datei
  6901. # fehler_file_not_exists();
  6902. # > STACK_0: Pathname
  6903. # > subr_self: Aufrufer (ein SUBR)
  6904.   nonreturning_function(local, fehler_file_not_exists, (void));
  6905.   local void fehler_file_not_exists()
  6906.     { # STACK_0 = Wert für Slot PATHNAME von FILE-ERROR
  6907.       pushSTACK(STACK_0); # pathname
  6908.       pushSTACK(TheSubr(subr_self)->name);
  6909.       //: DEUTSCH "~: Datei ~ existiert nicht."
  6910.       //: ENGLISH "~: file ~ does not exist"
  6911.       //: FRANCAIS "~ : Le fichier ~ n'existe pas."
  6912.       fehler(file_error, GETTEXT("~: file ~ does not exist"));
  6913.     }
  6914.  
  6915. LISPFUNN(truename,1)
  6916. # (TRUENAME pathname), CLTL S. 413
  6917.   { var reg1 object pathname = popSTACK(); # pathname-Argument
  6918.     if (streamp(pathname))
  6919.       # Stream -> extra behandeln:
  6920.       { # muß File-Stream sein:
  6921.         pathname = as_file_stream(pathname);
  6922.         # Streamtyp File-Stream
  6923.         value1 = TheStream(pathname)->strm_file_truename;
  6924.       }
  6925.       else
  6926.       { pathname = coerce_pathname(pathname); # zu einem Pathname machen
  6927.         check_no_wildcards(pathname); # mit Wildcards -> Fehler
  6928.         pathname = use_default_dir(pathname); # Default-Directory einfügen
  6929.         pushSTACK(pathname);
  6930.        {# Directory muß existieren:
  6931.         var reg3 object namestring = assure_dir_exists_(FALSE,TRUE); # Filename als ASCIZ-String
  6932.         if (namenullp(STACK_0))
  6933.           # Kein Name angegeben
  6934.           { if (!nullp(ThePathname(STACK_0)->pathname_type))
  6935.               { # STACK_0 = Wert für Slot PATHNAME von FILE-ERROR
  6936.                 pushSTACK(STACK_0); # pathname
  6937.                 pushSTACK(TheSubr(subr_self)->name);
  6938.                 //: DEUTSCH "~: Pathname mit TYPE, aber ohne NAME sinnlos: ~"
  6939.                 //: ENGLISH "~: pathname with type but without name makes no sense: ~"
  6940.                 //: FRANCAIS "~ : Un PATHNAME avec TYPE mais sans NAME est insensé: ~"
  6941.                 fehler(file_error, GETTEXT("~: pathname with type but without name makes no sense: ~"));
  6942.               }
  6943.             # Kein Name und kein Typ angegeben -> pathname als Ergebnis
  6944.           }
  6945.           else
  6946.           # Name angegeben.
  6947.           { # Überprüfe, ob die Datei existiert:
  6948.             if_file_exists(namestring, ; , { fehler_file_not_exists(); } );
  6949.             # Datei existiert -> Pathname als Wert
  6950.           }
  6951.         value1 = popSTACK();
  6952.       }}
  6953.     mv_count=1;
  6954.   }
  6955.  
  6956. LISPFUNN(probe_file,1)
  6957. # (PROBE-FILE filename), CLTL S. 424
  6958.   { var reg1 object pathname = popSTACK(); # pathname-Argument
  6959.     if (streamp(pathname))
  6960.       # Stream -> extra behandeln:
  6961.       { # muß File-Stream sein:
  6962.         pathname = as_file_stream(pathname);
  6963.         # Streamtyp File-Stream -> Truename nehmen:
  6964.        {var reg1 uintB flags = TheStream(pathname)->strmflags;
  6965.         pathname = TheStream(pathname)->strm_file_truename;
  6966.         if (flags & strmflags_open_B) # Datei geöffnet ?
  6967.           # ja -> Truename sofort als Ergebnis:
  6968.           { value1 = pathname; mv_count=1; return; }
  6969.         # nein -> noch testen, ob die Datei zum Truename existiert.
  6970.       }}
  6971.       else
  6972.       { pathname = coerce_pathname(pathname); } # zu einem Pathname machen
  6973.     # pathname ist jetzt ein Pathname.
  6974.     check_no_wildcards(pathname); # mit Wildcards -> Fehler
  6975.     pathname = use_default_dir(pathname); # Default-Directory einfügen
  6976.     if (namenullp(pathname)) { fehler_noname(pathname); } # Kein Name angegeben -> Fehler
  6977.     # Name angegeben.
  6978.     pushSTACK(pathname);
  6979.    {# Directory muß existieren:
  6980.     var reg3 object namestring = assure_dir_exists_(TRUE,TRUE); # Filename als ASCIZ-String
  6981.     if (eq(namestring,nullobj))
  6982.       # Pfad zur Datei existiert nicht -> NIL als Wert:
  6983.       { skipSTACK(1); value1 = NIL; mv_count=1; return; }
  6984.     # Überprüfe, ob die Datei existiert:
  6985.     if_file_exists(namestring,
  6986.       { value1 = popSTACK(); mv_count=1; }, # Datei existiert -> Pathname als Wert
  6987.       { skipSTACK(1); value1 = NIL; mv_count=1; return; } # sonst NIL als Wert
  6988.       );
  6989.   }}
  6990.  
  6991. # UP: Stellt fest, ob eine Datei geöffnet ist.
  6992. # openp(pathname)
  6993. #if defined(PATHNAME_MSDOS) || defined(PATHNAME_OS2)
  6994. # > pathname: absoluter Pathname, ohne Wildcards.
  6995. #endif
  6996. #ifdef PATHNAME_AMIGAOS
  6997. # > pathname: absoluter Pathname, ohne Wildcards, ohne :PARENT
  6998. #endif
  6999. #ifdef PATHNAME_UNIX
  7000. # > pathname: absoluter Pathname, ohne Wildcards, nach Auflösung
  7001. #             symbolischer Links
  7002. #endif
  7003. # < ergebnis: TRUE, falls ein geöffneter File-Stream auf diese Datei existiert.
  7004.   local boolean openp (object pathname);
  7005.   local boolean openp(pathname)
  7006.     var reg2 object pathname;
  7007.     { var reg1 object flist = O(open_files); # Liste aller offenen Files durchlaufen
  7008.       while (consp(flist))
  7009.         { var reg3 object f = Car(flist); # nächster offener Stream
  7010.           if_strm_file_p(f, # File-Stream ?
  7011.             { if (equal(TheStream(f)->strm_file_truename,pathname))
  7012.                 { return TRUE; }
  7013.             },
  7014.             ; );
  7015.           flist = Cdr(flist);
  7016.         }
  7017.       return FALSE;
  7018.     }
  7019.  
  7020. # Fehlermeldung wegen Löschversuch auf geöffnete Datei
  7021. # fehler_delete_open(pathname);
  7022. # > pathname: Truename der Datei
  7023.   nonreturning_function(local, fehler_delete_open, (object pathname));
  7024.   local void fehler_delete_open(pathname)
  7025.     var reg1 object pathname;
  7026.     { pushSTACK(pathname); # Wert für Slot PATHNAME von FILE-ERROR
  7027.       pushSTACK(pathname);
  7028.       //: DEUTSCH "Datei ~ kann nicht gelöscht werden, weil ein File-Stream auf sie geöffnet wurde."
  7029.       //: ENGLISH "cannot delete file ~ since there is file stream open to it"
  7030.       //: FRANCAIS "Le fichier ~ ne peut pas être effacé car il est encore ouvert comme «stream»."
  7031.       fehler(file_error, GETTEXT("cannot delete file ~ since there is file stream open to it"));
  7032.     }
  7033.  
  7034. LISPFUNN(delete_file,1)
  7035. # (DELETE-FILE filename), CLTL S. 424
  7036.   { var reg1 object pathname = popSTACK(); # pathname-Argument
  7037.     if (streamp(pathname))
  7038.       # Stream -> extra behandeln:
  7039.       { var object stream = as_file_stream(pathname); # muß File-Stream sein
  7040.         # Streamtyp File-Stream.
  7041.         # Falls Datei geöffnet, erst Datei schließen:
  7042.         if (TheStream(stream)->strmflags & strmflags_open_B) # Datei geöffnet ?
  7043.           { stream_close(&stream); }
  7044.         # Dann den Truename als zu löschende Datei nehmen:
  7045.         pathname = TheStream(stream)->strm_file_truename;
  7046.       }
  7047.       else
  7048.       { pathname = coerce_pathname(pathname); } # zu einem Pathname machen
  7049.     # pathname ist jetzt ein Pathname.
  7050.     check_no_wildcards(pathname); # mit Wildcards -> Fehler
  7051.     pathname = use_default_dir(pathname); # Default-Directory einfügen
  7052.     if (namenullp(pathname)) { fehler_noname(pathname); } # Kein Name angegeben -> Fehler
  7053.     # Name angegeben.
  7054.     pushSTACK(pathname);
  7055.    {# Directory muß existieren:
  7056.     var reg3 object namestring = assure_dir_exists(TRUE); # Filename als ASCIZ-String
  7057.     if (eq(namestring,nullobj))
  7058.       # Pfad zur Datei existiert nicht -> Wert NIL
  7059.       { skipSTACK(1); value1 = NIL; mv_count=1; return; }
  7060.     if (openp(STACK_0)) { fehler_delete_open(STACK_0); } # Keine offenen Dateien löschen!
  7061.     # Datei löschen:
  7062.     #ifdef AMIGAOS
  7063.     if (!file_exists(namestring))
  7064.       { skipSTACK(1); value1 = NIL; mv_count=1; return; } # File existiert nicht -> Wert NIL
  7065.     begin_system_call();
  7066.     if (! DeleteFile(TheAsciz(namestring)) ) { OS_error(); }
  7067.     end_system_call();
  7068.     #endif
  7069.     #if defined(UNIX) || defined(DJUNIX) || defined(EMUNIX) || defined(WATCOM) || defined(RISCOS) || defined(WIN32_DOS) || defined(WIN32_UNIX)
  7070.     begin_system_call();
  7071.     if (!( unlink(TheAsciz(namestring)) ==0))
  7072.       { if (!(errno==ENOENT)) { OS_error(); }
  7073.         end_system_call();
  7074.         # File existiert nicht -> Wert NIL
  7075.         skipSTACK(1); value1 = NIL; mv_count=1; return;
  7076.       }
  7077.     end_system_call();
  7078.     #endif
  7079.     # Datei existierte, wurde gelöscht -> Pathname (/=NIL) als Wert
  7080.     value1 = popSTACK(); mv_count=1;
  7081.   }}
  7082.  
  7083. # Fehlermeldung wegen Umbenennungsversuch einer geöffneten Datei
  7084. # fehler_rename_open(pathname);
  7085. # > pathname: Truename der Datei
  7086.   nonreturning_function(local, fehler_rename_open, (object pathname));
  7087.   local void fehler_rename_open(pathname)
  7088.     var reg1 object pathname;
  7089.     { pushSTACK(pathname); # Wert für Slot PATHNAME von FILE-ERROR
  7090.       pushSTACK(pathname);
  7091.       //: DEUTSCH "Datei ~ kann nicht umbenannt werden, weil ein File-Stream auf sie geöffnet wurde."
  7092.       //: ENGLISH "cannot rename file ~ since there is file stream open to it"
  7093.       //: FRANCAIS "Le fichier ~ ne peut pas être renommé car il est encore ouvert comme «stream»."
  7094.       fehler(file_error, GETTEXT("cannot rename file ~ since there is file stream open to it"));
  7095.     }
  7096.  
  7097. # UP: Führt eine Datei-Umbenennung durch.
  7098. # rename_file();
  7099. # > Stackaufbau: filename, newname, oldpathname.
  7100. # < Stackaufbau: filename, newname, oldpathname, newpathname,
  7101. #                oldtruename, oldnamestring, newtruename, newnamestring.
  7102.   local void rename_file (void);
  7103.   local void rename_file()
  7104.     { # 1. newpathname := (MERGE-PATHNAMES newname oldpathname)
  7105.       { pushSTACK(STACK_1); # newname als 1. Argument
  7106.         pushSTACK(STACK_(0+1)); # oldpathname als 2. Argument
  7107.         funcall(L(merge_pathnames),2);
  7108.         pushSTACK(value1);
  7109.       }
  7110.       # Stackaufbau: filename, newname, oldpathname, newpathname.
  7111.       # 2. oldpathname überprüfen:
  7112.       { var reg1 object oldpathname = STACK_1;
  7113.         check_no_wildcards(oldpathname); # mit Wildcards -> Fehler
  7114.         oldpathname = use_default_dir(oldpathname); # Default-Directory einfügen
  7115.         if (namenullp(oldpathname)) { fehler_noname(oldpathname); } # Kein Name angegeben -> Fehler
  7116.         # Name angegeben.
  7117.         pushSTACK(oldpathname);
  7118.        {# Directory muß existieren:
  7119.         var reg2 object old_namestring = assure_dir_exists(FALSE); # Filename als ASCIZ-String
  7120.         if (openp(STACK_0)) { fehler_rename_open(STACK_0); } # Keine offenen Dateien umbenennen!
  7121.         pushSTACK(old_namestring);
  7122.       }}
  7123.       # Stackaufbau: filename, newname, oldpathname, newpathname,
  7124.       #              oldtruename, oldnamestring.
  7125.       # 3. newpathname überprüfen:
  7126.       { var reg1 object newpathname = coerce_pathname(STACK_2);
  7127.         check_no_wildcards(newpathname); # mit Wildcards -> Fehler
  7128.         newpathname = use_default_dir(newpathname); # Default-Directory einfügen
  7129.         if (namenullp(newpathname)) { fehler_noname(newpathname); } # Kein Name angegeben -> Fehler
  7130.         # Name angegeben.
  7131.         pushSTACK(newpathname);
  7132.        {# Directory muß existieren:
  7133.         var reg2 object new_namestring = assure_dir_exists(FALSE); # Filename als ASCIZ-String
  7134.         pushSTACK(new_namestring);
  7135.       }}
  7136.       # Stackaufbau: filename, newname, oldpathname, newpathname,
  7137.       #              oldtruename, oldnamestring, newtruename, newnamestring.
  7138.       # 4. Datei umbenennen:
  7139.       #if defined(UNIX) || defined(AMIGAOS) || defined(DJUNIX) || defined(EMUNIX) || defined(WATCOM) || defined(RISCOS) || defined(WIN32_DOS) || defined(WIN32_UNIX)
  7140.       #if 0
  7141.       if (file_exists(STACK_0))
  7142.         # Datei existiert bereits -> nicht ohne Vorwarnung löschen
  7143.         { fehler_file_exists(S(rename_file),STACK_1); }
  7144.       #endif
  7145.       # Nun kann gefahrlos umbenannt werden:
  7146.       #ifdef PATHNAME_RISCOS
  7147.       prepare_create(STACK_4);
  7148.       #endif
  7149.       begin_system_call();
  7150.       #ifdef AMIGAOS
  7151.       if (! Rename(TheAsciz(STACK_2),TheAsciz(STACK_0)) ) { OS_error(); }
  7152.       #endif
  7153.       #if defined(UNIX) || defined(DJUNIX) || defined(EMUNIX) || defined(WATCOM) || defined(RISCOS) || defined(WIN32_DOS) || defined(WIN32_UNIX)
  7154.       if (!( rename(TheAsciz(STACK_2),TheAsciz(STACK_0)) ==0)) { OS_error(); }
  7155.       #endif
  7156.       end_system_call();
  7157.       #endif
  7158.     }
  7159.  
  7160. LISPFUNN(rename_file,2)
  7161. # (RENAME-FILE filename newname), CLTL S. 423
  7162.   { var reg1 object filename = STACK_1; # filename-Argument
  7163.     if (streamp(filename))
  7164.       # Stream -> extra behandeln:
  7165.       { # muß File-Stream sein:
  7166.         filename = as_file_stream(filename);
  7167.         # Streamtyp File-Stream -> Truename verwenden:
  7168.         filename = TheStream(filename)->strm_file_truename;
  7169.         pushSTACK(filename);
  7170.         # Umbenennen:
  7171.         rename_file();
  7172.         # Stream aktualisieren:
  7173.         filename = STACK_7;
  7174.         TheStream(filename)->strm_file_name = STACK_4; # newpathname als neuer Name
  7175.         TheStream(filename)->strm_file_truename = STACK_1; # newtruename als neuer Truename
  7176.         # Handle etc. unverändert lassen
  7177.       }
  7178.       else
  7179.       { filename = coerce_pathname(filename); # zu einem Pathname machen
  7180.         pushSTACK(filename);
  7181.         # Umbenennen:
  7182.         rename_file();
  7183.       }
  7184.     value1 = STACK_4; # newpathname als 1. Wert
  7185.     value2 = STACK_3; # oldtruename als 2. Wert
  7186.     value3 = STACK_1; # newtruename als 3. Wert
  7187.     mv_count=3; skipSTACK(8); # 3 Werte
  7188.   }
  7189.  
  7190. # UP: erzeugt ein File-Stream
  7191. # open_file(filename,direction,if_exists,if_not_exists,type,eltype_size)
  7192. # > filename: Filename, ein Pathname
  7193. # > direction: Modus (0 = :PROBE, 1 = :INPUT, 4 = :OUTPUT, 5 = :IO, 3 = :INPUT-IMMUTABLE)
  7194. # > if_exists: :IF-EXISTS-Argument
  7195. #         (0 = nichts, 1 = :ERROR, 2 = NIL,
  7196. #          3 = :RENAME, 4 = :RENAME-AND-DELETE, 5 = :NEW-VERSION,:SUPERSEDE,
  7197. #          6 = :APPEND, 7 = :OVERWRITE)
  7198. # > if_not_exists: :IF-DOES-NOT-EXIST-Argument
  7199. #         (0 = nichts, 1 = :ERROR, 2 = NIL, 3 = :CREATE)
  7200. # > type: nähere Typinfo
  7201. #         (STRMTYPE_SCH_FILE oder STRMTYPE_CH_FILE oder
  7202. #          STRMTYPE_IU_FILE oder STRMTYPE_IS_FILE)
  7203. # > eltype_size: (bei Integer-Streams) Größe der Elemente in Bits,
  7204. #         ein Fixnum >0 und <intDsize*uintC_max
  7205. # < ergebnis: Stream oder NIL
  7206. # kann GC auslösen
  7207.   local object open_file (object filename, uintB direction, uintB if_exists, uintB if_not_exists,
  7208.                           uintB type, object eltype_size);
  7209.   local object open_file(filename,direction,if_exists,if_not_exists,type,eltype_size)
  7210.     var reg5 object filename;
  7211.     var reg8 uintB direction;
  7212.     var reg3 uintB if_exists;
  7213.     var reg4 uintB if_not_exists;
  7214.     var reg7 uintB type;
  7215.     var reg9 object eltype_size;
  7216.     { pushSTACK(filename); # Filename retten
  7217.       check_no_wildcards(filename); # mit Wildcards -> Fehler
  7218.       filename = use_default_dir(filename); # Default-Directory einfügen
  7219.       if (namenullp(filename)) { fehler_noname(filename); } # Kein Name angegeben -> Fehler
  7220.       pushSTACK(filename); # absPathname retten
  7221.       # Stackaufbau: Pathname, absPathname.
  7222.       { # Directory muß existieren:
  7223.         var reg3 object namestring = # Filename als ASCIZ-String
  7224.           assure_dir_exists((direction == 0) && ((if_not_exists % 2) == 0)); # tolerant nur bei :PROBE und if_not_exists = 0 oder 2
  7225.         # Stackaufbau: Pathname, Truename.
  7226.         # Filename überprüfen und Handle holen:
  7227.         var reg2 object handle;
  7228.         var reg6 boolean append_flag = FALSE;
  7229.         switch (direction)
  7230.           { case 0: # Modus ist :PROBE
  7231.               if (eq(namestring,nullobj))
  7232.                 # Pfad zur Datei existiert nicht, und :IF-DOES-NOT-EXIST = nichts oder NIL
  7233.                 goto ergebnis_NIL;
  7234.               #if defined(UNIX) || defined(AMIGAOS) || defined(DJUNIX) || defined(EMUNIX) || defined(WATCOM) || defined(RISCOS) || defined(WIN32_DOS) || defined(WIN32_UNIX)
  7235.               if (!file_exists(namestring))
  7236.                 # Datei existiert nicht
  7237.                 { # :IF-DOES-NOT-EXIST-Argument entscheidet:
  7238.                   if (if_not_exists==1) # :ERROR -> Error
  7239.                     goto fehler_notfound;
  7240.                   if (!(if_not_exists==3)) # nichts oder NIL -> NIL
  7241.                     goto ergebnis_NIL;
  7242.                   #ifdef PATHNAME_RISCOS
  7243.                   pushSTACK(namestring); prepare_create(STACK_1); namestring = popSTACK();
  7244.                   #endif
  7245.                  {# :CREATE -> Datei mit open erzeugen und schließen:
  7246.                   #ifdef AMIGAOS
  7247.                   var reg1 Handle handle;
  7248.                   begin_system_call();
  7249.                   handle = Open(TheAsciz(namestring),MODE_NEWFILE);
  7250.                   if (handle == Handle_NULL) { OS_error(); } # Error melden
  7251.                   # Datei wurde erzeugt, handle ist das Handle.
  7252.                   # Datei wieder schließen:
  7253.                   (void) Close(handle);
  7254.                   end_system_call();
  7255.                   #endif
  7256.                   #if defined(UNIX) || defined(DJUNIX) || defined(EMUNIX) || defined(WATCOM) || defined(RISCOS) || defined(WIN32_DOS) || defined(WIN32_UNIX)
  7257.                   var reg1 int ergebnis;
  7258.                   begin_system_call();
  7259.                   #if defined(DJUNIX) || defined(EMUNIX) || defined(WATCOM) || defined(WIN32_DOS)
  7260.                   ergebnis = creat(TheAsciz(namestring),my_open_mask);
  7261.                   if (ergebnis<0) { OS_error(); } # Error melden
  7262.                   setmode(ergebnis,O_BINARY);
  7263.                   #endif
  7264.                   #if defined(UNIX) || defined(RISCOS) || defined(WIN32_UNIX)
  7265.                   ergebnis = OPEN(TheAsciz(namestring),
  7266.                                   O_WRONLY | O_CREAT | O_TRUNC,
  7267.                                   my_open_mask
  7268.                                  );
  7269.                   if (ergebnis<0) { OS_error(); } # Error melden
  7270.                   #ifdef WIN32_UNIX
  7271.                   setmode(ergebnis,O_BINARY);
  7272.                   #endif
  7273.                   #endif
  7274.                   # Datei wurde erzeugt, ergebnis ist das Handle.
  7275.                   # Datei wieder schließen:
  7276.                   ergebnis = CLOSE(ergebnis);
  7277.                   if (!(ergebnis==0)) { OS_error(); } # Error melden
  7278.                   end_system_call();
  7279.                   #endif
  7280.                 }}
  7281.               handle = NIL; # Handle := NIL
  7282.               break;
  7283.               #endif
  7284.             case 1: case 3: # Modus ist :INPUT
  7285.               #if defined(DJUNIX) || defined(EMUNIX) || defined(WATCOM) || defined(WIN32_DOS)
  7286.               { # erst mit open erfragen, ob die Datei existiert:
  7287.                 var reg1 sintW ergebnis;
  7288.                 # Datei zu öffnen versuchen:
  7289.                 begin_system_call();
  7290.                 ergebnis = open(TheAsciz(namestring),O_RDONLY);
  7291.                 if (ergebnis<0)
  7292.                   { if (errno == ENOENT) # nicht gefunden?
  7293.                       # Datei existiert nicht
  7294.                       { # :IF-DOES-NOT-EXIST-Argument entscheidet:
  7295.                         if (if_not_exists==2) # NIL -> NIL
  7296.                           goto ergebnis_NIL;
  7297.                         if (!(if_not_exists==3)) # nichts oder :ERROR -> Error
  7298.                           goto fehler_notfound;
  7299.                         # :CREATE -> Datei mit creat erzeugen:
  7300.                         ergebnis = creat(TheAsciz(namestring),my_open_mask);
  7301.                         if (ergebnis<0) { OS_error(); }
  7302.                       }
  7303.                       else
  7304.                       { OS_error(); } # sonstigen Error melden
  7305.                   }
  7306.                 setmode(ergebnis,O_BINARY);
  7307.                 end_system_call();
  7308.                 # Datei existiert, ergebnis ist das Handle
  7309.                 handle = allocate_handle(ergebnis); # Handle
  7310.                 break;
  7311.               }
  7312.               #endif
  7313.               #ifdef AMIGAOS
  7314.               { # erst mit Open erfragen, ob die Datei existiert:
  7315.                 var reg1 Handle handl;
  7316.                 begin_system_call();
  7317.                 handl = Open(TheAsciz(namestring),MODE_OLDFILE);
  7318.                 if (handl==Handle_NULL)
  7319.                   { if (IoErr()==ERROR_OBJECT_NOT_FOUND)
  7320.                       # Datei existiert nicht
  7321.                       { # :IF-DOES-NOT-EXIST-Argument entscheidet:
  7322.                         if (if_not_exists==2) # NIL -> NIL
  7323.                           goto ergebnis_NIL;
  7324.                         if (!(if_not_exists==3)) # nichts oder :ERROR -> Error
  7325.                           goto fehler_notfound;
  7326.                         # :CREATE -> Datei mit Open erzeugen:
  7327.                         handl = Open(TheAsciz(namestring),MODE_READWRITE);
  7328.                   }   }
  7329.                 if (handl==Handle_NULL) { OS_error(); } # Error melden
  7330.                 end_system_call();
  7331.                 # Datei existiert, handle ist das Handle
  7332.                 handle = allocate_handle(handl); # Handle als Lisp-Objekt
  7333.                 break;
  7334.               }
  7335.               #endif
  7336.               #if defined(UNIX) || defined(RISCOS) || defined(WIN32_UNIX)
  7337.               { var reg2 int o_flags = O_RDONLY;
  7338.                 if (!file_exists(namestring))
  7339.                   # Datei existiert nicht
  7340.                   { # :IF-DOES-NOT-EXIST-Argument entscheidet:
  7341.                     if (if_not_exists==2) # NIL -> NIL
  7342.                       goto ergebnis_NIL;
  7343.                     if (!(if_not_exists==3)) # nichts oder :ERROR -> Error
  7344.                       goto fehler_notfound;
  7345.                     # :CREATE -> Datei mit open erzeugen
  7346.                     #ifdef PATHNAME_RISCOS
  7347.                     pushSTACK(namestring); prepare_create(STACK_1); namestring = popSTACK();
  7348.                     #endif
  7349.                     o_flags |= O_CREAT;
  7350.                   }
  7351.                {var reg1 int ergebnis;
  7352.                 begin_system_call();
  7353.                 ergebnis = OPEN(TheAsciz(namestring),
  7354.                                 o_flags, # O_RDONLY bzw. O_RDONLY | O_CREAT
  7355.                                 my_open_mask
  7356.                                );
  7357.                 if (ergebnis<0) { OS_error(); } # Error melden
  7358.                 #ifdef WIN32_UNIX
  7359.                 setmode(ergebnis,O_BINARY);
  7360.                 #endif
  7361.                 end_system_call();
  7362.                 # Datei existiert, ergebnis ist das Handle
  7363.                 handle = allocate_handle(ergebnis); # Handle
  7364.               }}
  7365.               break;
  7366.               #endif
  7367.             default: # Modus ist :OUTPUT oder :IO
  7368.               { # Defaultwert für if_not_exists ist von if_exists abhängig:
  7369.                 if (if_not_exists==0) # falls if_not_exists nicht angegeben:
  7370.                   { if (if_exists<6) # if_exists = :APPEND oder :OVERWRITE -> if_not_exists unverändert
  7371.                       { if_not_exists = 3; } # weder :APPEND noch :OVERWRITE -> Default ist :CREATE
  7372.                   }
  7373.                 # Defaultwert für if_exists ist :NEW-VERSION :
  7374.                 if (if_exists==0) { if_exists = 5; }
  7375.                 #if defined(DJUNIX) || defined(EMUNIX) || defined(WATCOM) || defined(WIN32_DOS)
  7376.                 # Bei if_exists=5 und if_not_exists=3 kann man sofort
  7377.                 # CREAT ansteuern, sonst muß man vorher OPEN versuchen:
  7378.                 if (!((if_exists==5) && (if_not_exists==3)))
  7379.                   { begin_system_call();
  7380.                    {var reg1 sintW ergebnis = # Datei zu öffnen versuchen
  7381.                       open(TheAsciz(namestring),O_RDWR);
  7382.                     if (ergebnis<0)
  7383.                       { end_system_call();
  7384.                         if (errno == ENOENT) # nicht gefunden?
  7385.                           # Datei existiert nicht
  7386.                           { # :IF-DOES-NOT-EXIST-Argument entscheidet:
  7387.                             if (if_not_exists<2) # (Default bei :APPEND oder :OVERWRITE) oder :ERROR ?
  7388.                               goto fehler_notfound;
  7389.                             if (if_not_exists==2) # NIL -> NIL
  7390.                               goto ergebnis_NIL;
  7391.                             # :CREATE
  7392.                           }
  7393.                           else
  7394.                           { OS_error(); } # sonstigen Error melden
  7395.                       }
  7396.                       else
  7397.                       # Datei existiert, ergebnis ist das Handle
  7398.                       { # :IF-EXISTS-Argument entscheidet:
  7399.                         switch (if_exists)
  7400.                           { case 1: # :ERROR -> schließen und Error
  7401.                               { if (CLOSE(ergebnis) < 0) { OS_error(); } # Error melden
  7402.                                 end_system_call();
  7403.                                 goto fehler_exists;
  7404.                               }
  7405.                             case 2: # NIL -> schließen und NIL
  7406.                               { if (CLOSE(ergebnis) < 0) { OS_error(); } # Error melden
  7407.                                 end_system_call();
  7408.                                 goto ergebnis_NIL;
  7409.                               }
  7410.                             case 6: # :APPEND
  7411.                               append_flag = TRUE; # am Schluß ans Ende positionieren
  7412.                             case 7: # :OVERWRITE -> bestehende Datei benutzen
  7413.                               setmode(ergebnis,O_BINARY);
  7414.                               end_system_call();
  7415.                               handle = allocate_handle(ergebnis);
  7416.                               goto handle_ok;
  7417.                             default: ;
  7418.                               # :RENAME, :RENAME-AND-DELETE -> Datei umbenennen und dann neu eröffnen.
  7419.                               # :NEW-VERSION, :SUPERSEDE -> Datei auf Länge 0 kürzen.
  7420.                           }
  7421.                         # In beiden Fällen erst die Datei schließen:
  7422.                         if (CLOSE(ergebnis) < 0) { OS_error(); } # Error melden
  7423.                         end_system_call();
  7424.                         if ((if_exists==3) || (if_exists==4))
  7425.                           # :RENAME oder :RENAME-AND-DELETE -> umbenennen:
  7426.                           { # Truename mit ".BAK" erweitern:
  7427.                             var reg1 object filename = STACK_0;
  7428.                             if (openp(filename)) { fehler_rename_open(filename); } # Keine offenen Dateien umbenennen!
  7429.                             pushSTACK(namestring); # namestring retten
  7430.                             # filename := (merge-pathnames ".BAK" filename) :
  7431.                             filename = copy_pathname(filename); # kopieren
  7432.                             ThePathname(filename)->pathname_type = O(backuptype_string); # mit Extension "BAK"
  7433.                             if (openp(filename)) { fehler_delete_open(filename); } # Keine offenen Dateien löschen!
  7434.                             pushSTACK(filename);
  7435.                            {# Directory existiert schon:
  7436.                             var reg3 object new_namestring = assume_dir_exists(); # Filename als ASCIZ-String
  7437.                             # Datei mit diesem Namen löschen, falls vorhanden:
  7438.                             begin_system_call();
  7439.                             if ( unlink(TheAsciz(new_namestring)) <0) # Datei zu löschen versuchen
  7440.                               { if (!(errno==ENOENT)) # nicht gefunden -> OK
  7441.                                   { OS_error(); } # sonstigen Error melden
  7442.                               }
  7443.                             end_system_call();
  7444.                             # Datei vom alten auf diesen Namen umbenennen:
  7445.                             skipSTACK(1);
  7446.                             namestring = popSTACK(); # namestring zurück
  7447.                             begin_system_call();
  7448.                             if ( rename(TheAsciz(namestring),TheAsciz(new_namestring)) <0) # Datei umbenennen
  7449.                               { OS_error(); } # Error melden
  7450.                             end_system_call();
  7451.                             # :RENAME-AND-DELETE -> löschen:
  7452.                             if (if_exists==4)
  7453.                               { begin_system_call();
  7454.                                 if ( unlink(TheAsciz(new_namestring)) <0)
  7455.                                   { OS_error(); } # Error melden
  7456.                                 end_system_call();
  7457.                               }
  7458.                           }}
  7459.                       }
  7460.                   }}
  7461.                 # Datei mit CREAT erzeugen:
  7462.                 begin_system_call();
  7463.                 { var reg1 sintW ergebnis = # erzeugen
  7464.                     creat(TheAsciz(namestring),my_open_mask);
  7465.                   if (ergebnis<0) { OS_error(); } # Error melden
  7466.                   #if defined(WIN32_DOS) && 0
  7467.                   CLOSE(ergebnis);
  7468.                   ergebnis = open(TheAsciz(namestring), O_RDWR);
  7469.                   #endif
  7470.                   setmode(ergebnis,O_BINARY);
  7471.                   end_system_call();
  7472.                   # Datei neu erzeugt, ergebnis ist das Handle
  7473.                   handle = allocate_handle(ergebnis);
  7474.                 }
  7475.                 #endif
  7476.                 #if defined(UNIX) || defined(AMIGAOS) || defined(RISCOS) || defined(WIN32_UNIX)
  7477.                 if (file_exists(namestring))
  7478.                   # Datei existiert
  7479.                   { # :IF-EXISTS-Argument entscheidet:
  7480.                     switch (if_exists)
  7481.                       { case 1: # :ERROR -> Error
  7482.                           goto fehler_exists;
  7483.                         case 2: # NIL -> NIL
  7484.                           goto ergebnis_NIL;
  7485.                         case 3: case 4: # :RENAME oder :RENAME-AND-DELETE -> umbenennen:
  7486.                           #if defined(UNIX) || defined(AMIGAOS) || defined(RISCOS)
  7487.                           { # Truename mit "%" bzw. ".bak" bzw. "~" erweitern:
  7488.                             var reg1 object filename = STACK_0;
  7489.                             var reg3 object new_namestring;
  7490.                             if (openp(filename)) { fehler_rename_open(filename); } # Keine offenen Dateien umbenennen!
  7491.                             pushSTACK(namestring); # namestring retten
  7492.                             #if defined(UNIX) || defined(AMIGAOS) || defined(WIN32_UNIX)
  7493.                             # filename := (parse-namestring (concatenate 'string (namestring filename) "%")) :
  7494.                             filename = whole_namestring(filename); # als String
  7495.                             pushSTACK(filename); pushSTACK(O(backupextend_string)); # "%"
  7496.                             filename = string_concat(2); # dazuhängen
  7497.                             pushSTACK(filename); # retten
  7498.                             filename = coerce_pathname(filename); # wieder als Filename
  7499.                             if (openp(filename)) { fehler_delete_open(filename); } # Keine offenen Dateien löschen!
  7500.                             # Directory existiert schon. Hier keine weiteren Links verfolgen.
  7501.                             new_namestring = string_to_asciz(popSTACK()); # Filename als ASCIZ-String
  7502.                             #endif
  7503.                             #ifdef RISCOS
  7504.                             # Dem Namen ein "~" voranstellen:
  7505.                             filename = copy_pathname(filename);
  7506.                             pushSTACK(filename);
  7507.                             pushSTACK(O(backupprepend_string)); pushSTACK(ThePathname(filename)->pathname_name);
  7508.                             { var reg1 object new_name = string_concat(2);
  7509.                               filename = STACK_0;
  7510.                               ThePathname(filename)->pathname_name = new_name;
  7511.                             }
  7512.                             if (openp(filename)) { fehler_delete_open(filename); } # Keine offenen Dateien löschen!
  7513.                             new_namestring = assure_dir_exists(FALSE);
  7514.                             skipSTACK(1);
  7515.                             #endif
  7516.                             # Datei (oder Link) mit diesem Namen löschen, falls vorhanden:
  7517.                             #if defined(AMIGAOS)
  7518.                             begin_system_call();
  7519.                             if (! DeleteFile(TheAsciz(new_namestring)) )
  7520.                               { if (!(IoErr()==ERROR_OBJECT_NOT_FOUND)) { OS_error(); } # Error melden
  7521.                                 # nicht gefunden -> OK
  7522.                               }
  7523.                             end_system_call();
  7524.                             #endif
  7525.                             #if (defined(UNIX) && 0) || defined(RISCOS) # Das tut UNIX nachher automatisch, RISCOS aber nicht
  7526.                             begin_system_call();
  7527.                             if (!( unlink(TheAsciz(new_namestring)) ==0))
  7528.                               { if (!(errno==ENOENT)) { OS_error(); } # Error melden
  7529.                                 # nicht gefunden -> OK
  7530.                               }
  7531.                             end_system_call();
  7532.                             #endif
  7533.                             # Datei vom alten auf diesen Namen umbenennen:
  7534.                             namestring = popSTACK(); # namestring zurück
  7535.                             begin_system_call();
  7536.                             #ifdef AMIGAOS
  7537.                             if (! Rename(TheAsciz(namestring),TheAsciz(new_namestring)) )
  7538.                               { OS_error(); }
  7539.                             #endif
  7540.                             #if defined(UNIX) || defined(RISCOS)
  7541.                             if (!( rename(TheAsciz(namestring),TheAsciz(new_namestring)) ==0))
  7542.                               { OS_error(); }
  7543.                             #endif
  7544.                             # :RENAME-AND-DELETE -> löschen:
  7545.                             if (if_exists==4)
  7546.                               {
  7547.                                 #ifdef AMIGAOS
  7548.                                 if (! DeleteFile(TheAsciz(new_namestring)) )
  7549.                                   { OS_error(); }
  7550.                                 #endif
  7551.                                 #if defined(UNIX) || defined(RISCOS)
  7552.                                 if (!( unlink(TheAsciz(new_namestring)) ==0))
  7553.                                   { OS_error(); }
  7554.                                 #endif
  7555.                               }
  7556.                             end_system_call();
  7557.                           }
  7558.                           #endif
  7559.                           break;
  7560.                         case 6: # :APPEND
  7561.                           append_flag = TRUE; # am Schluß ans Ende positionieren
  7562.                         default: ;
  7563.                           # :OVERWRITE -> bestehende Datei benutzen
  7564.                           # :NEW-VERSION, :SUPERSEDE -> Datei auf Länge 0 kürzen.
  7565.                   }   }
  7566.                   else
  7567.                   # Datei existiert nicht
  7568.                   { # :IF-DOES-NOT-EXIST-Argument entscheidet:
  7569.                     if (if_not_exists<2) # (Default bei :APPEND oder :OVERWRITE) oder :ERROR ?
  7570.                       goto fehler_notfound;
  7571.                     if (if_not_exists==2) # NIL -> NIL
  7572.                       goto ergebnis_NIL;
  7573.                     # :CREATE
  7574.                   }
  7575.                 #ifdef PATHNAME_RISCOS
  7576.                 pushSTACK(namestring); prepare_create(STACK_1); namestring = popSTACK();
  7577.                 #endif
  7578.                 # Datei mit open öffnen:
  7579.                 { # if-exists-Handling: bei if_exists<=5 Inhalt löschen,
  7580.                   # sonst (bei :APPEND, :OVERWRITE) bestehenden Inhalt lassen.
  7581.                   # if-not-exists-Handling: neue Datei erzeugen.
  7582.                   #ifdef AMIGAOS
  7583.                   var reg1 Handle handl;
  7584.                   begin_system_call();
  7585.                   handl = Open(TheAsciz(namestring),
  7586.                                (if_exists<=5 ? MODE_NEWFILE : MODE_READWRITE)
  7587.                               );
  7588.                   if (handl==Handle_NULL) { OS_error(); } # Error melden
  7589.                   end_system_call();
  7590.                   handle = allocate_handle(handl);
  7591.                   #endif
  7592.                   #if defined(UNIX) || defined(RISCOS) || defined(WIN32_UNIX)
  7593.                   var reg1 int ergebnis;
  7594.                   begin_system_call();
  7595.                   ergebnis = OPEN(TheAsciz(namestring),
  7596.                                   (if_exists<=5 ? O_RDWR | O_CREAT | O_TRUNC
  7597.                                                 : O_RDWR | O_CREAT
  7598.                                   ),
  7599.                                   my_open_mask
  7600.                                  );
  7601.                   if (ergebnis<0) { OS_error(); } # Error melden
  7602.                   #ifdef WIN32_UNIX
  7603.                   setmode(ergebnis,O_BINARY);
  7604.                   #endif
  7605.                   end_system_call();
  7606.                   # Datei wurde geöffnet, ergebnis ist das Handle.
  7607.                   handle = allocate_handle(ergebnis);
  7608.                   #endif
  7609.                 }
  7610.                 #endif
  7611.                 break;
  7612.               }
  7613.             ergebnis_NIL: # Ergebnis NIL
  7614.               skipSTACK(2); # beide Pathnames vergessen
  7615.               return NIL;
  7616.             fehler_notfound: # Fehler, da Datei nicht gefunden
  7617.               # STACK_0 = Truename, Wert für Slot PATHNAME von FILE-ERROR
  7618.               pushSTACK(STACK_0);
  7619.               //: DEUTSCH "Eine Datei mit Namen ~ existiert nicht."
  7620.               //: ENGLISH "file ~ does not exist"
  7621.               //: FRANCAIS "Un fichier de nom ~ n'existe pas."
  7622.               fehler(file_error, GETTEXT("file ~ does not exist"));
  7623.             fehler_exists: # Fehler, da Datei bereits existiert
  7624.               # STACK_0 = Truename, Wert für Slot PATHNAME von FILE-ERROR
  7625.               pushSTACK(STACK_0);
  7626.               //: DEUTSCH "Eine Datei mit Namen ~ existiert bereits."
  7627.               //: ENGLISH "a file named ~ already exists"
  7628.               //: FRANCAIS "Un fichier de nom ~ existe déjà."
  7629.               fehler(file_error, GETTEXT("a file named ~ already exists"));
  7630.           }
  7631.         handle_ok:
  7632.         # handle und append_flag sind jetzt fertig.
  7633.         # Stream erzeugen:
  7634.         return make_file_stream(handle,direction,type,eltype_size,append_flag);
  7635.     } }
  7636.  
  7637. LISPFUN(open,1,0,norest,key,4,\
  7638.         (kw(direction),kw(element_type),kw(if_exists),kw(if_does_not_exist)) )
  7639. # (OPEN filename :direction :element-type :if-exists :if-does-not-exist),
  7640. # CLTL S. 418
  7641.   { var reg2 object filename = STACK_4; # filename
  7642.     if (streamp(filename))
  7643.       { # muß File-Stream sein:
  7644.         filename = as_file_stream(filename);
  7645.         # Streamtyp File-Stream -> Truename verwenden:
  7646.         filename = TheStream(filename)->strm_file_truename;
  7647.       }
  7648.       else
  7649.       { filename = coerce_pathname(filename); } # zu einem Pathname machen
  7650.     # filename ist jetzt ein Pathname.
  7651.    {var reg3 uintB direction;
  7652.     var reg4 uintB if_exists;
  7653.     var reg5 uintB if_not_exists;
  7654.     var reg6 uintB type;
  7655.     var reg7 object eltype_size = NIL;
  7656.     # :direction überprüfen und in direction übersetzen:
  7657.     { var reg1 object arg = STACK_3;
  7658.       if (eq(arg,unbound) || eq(arg,S(Kinput))) { direction = 1; }
  7659.       elif (eq(arg,S(Kinput_immutable))) { direction = 3; }
  7660.       elif (eq(arg,S(Koutput))) { direction = 4; }
  7661.       elif (eq(arg,S(Kio))) { direction = 5; }
  7662.       elif (eq(arg,S(Kprobe))) { direction = 0; }
  7663.       else
  7664.       { pushSTACK(arg); # Wert für Slot DATUM von TYPE-ERROR
  7665.         pushSTACK(O(type_direction)); # Wert für Slot EXPECTED-TYPE von TYPE-ERROR
  7666.         pushSTACK(arg); pushSTACK(S(open));
  7667.         //: DEUTSCH "~: Als :DIRECTION-Argument ist ~ unzulässig."
  7668.         //: ENGLISH "~: illegal :DIRECTION argument ~"
  7669.         //: FRANCAIS "~ : ~ n'est pas permis comme argument pour :DIRECTION."
  7670.         fehler(type_error, GETTEXT("~: illegal :DIRECTION argument ~"));
  7671.     } }
  7672.     # :element-type überprüfen und in type und eltype_size übersetzen:
  7673.     { var reg1 object arg = STACK_2;
  7674.       if (eq(arg,unbound) || eq(arg,S(string_char)) || eq(arg,S(Kdefault))) # STRING-CHAR, :DEFAULT
  7675.         { type = strmtype_sch_file; }
  7676.       elif (eq(arg,S(character))) # CHARACTER
  7677.         { type = strmtype_ch_file; }
  7678.       elif (eq(arg,S(bit))) # BIT
  7679.         { type = strmtype_iu_file; eltype_size = Fixnum_1; }
  7680.       elif (eq(arg,S(unsigned_byte))) # UNSIGNED-BYTE
  7681.         { type = strmtype_iu_file; eltype_size = fixnum(8); }
  7682.       elif (eq(arg,S(signed_byte))) # SIGNED-BYTE
  7683.         { type = strmtype_is_file; eltype_size = fixnum(8); }
  7684.       elif (consp(arg) && mconsp(Cdr(arg)) && nullp(Cdr(Cdr(arg)))) # zweielementige Liste
  7685.         { var reg2 object h = Car(arg);
  7686.           if (eq(h,S(mod))) # (MOD n)
  7687.             { type = strmtype_iu_file;
  7688.               h = Car(Cdr(arg)); # n
  7689.               # muß ein Integer >0 sein:
  7690.               if (!(integerp(h) && positivep(h) && !eq(h,Fixnum_0)))
  7691.                 goto bad_eltype;
  7692.               # eltype_size := (integer-length (1- n)) bilden:
  7693.               pushSTACK(filename); # filename retten
  7694.               pushSTACK(h); funcall(L(einsminus),1); # (1- n)
  7695.               pushSTACK(value1); funcall(L(integer_length),1); # (integer-length (1- n))
  7696.               eltype_size = value1;
  7697.               filename = popSTACK(); # filename zurück
  7698.             }
  7699.           elif (eq(h,S(unsigned_byte))) # (UNSIGNED-BYTE n)
  7700.             { type = strmtype_iu_file;
  7701.               eltype_size = Car(Cdr(arg));
  7702.             }
  7703.           elif (eq(h,S(signed_byte))) # (SIGNED-BYTE n)
  7704.             { type = strmtype_is_file;
  7705.               eltype_size = Car(Cdr(arg));
  7706.             }
  7707.           else goto bad_eltype;
  7708.           # eltype_size überprüfen:
  7709.           if (!(posfixnump(eltype_size) && !eq(eltype_size,Fixnum_0)
  7710.                 && ((oint_data_len < log2_intDsize+intCsize) # (Bei oint_data_len <= log2(intDsize)+intCsize-1
  7711.                     # ist stets eltype_size < 2^oint_data_len < intDsize*(2^intCsize-1).)
  7712.                     || (as_oint(eltype_size) < as_oint(fixnum(intDsize*(uintL)(bitm(intCsize)-1))))
  7713.              ) )   )
  7714.             goto bad_eltype;
  7715.         }
  7716.       else
  7717.         { bad_eltype:
  7718.           pushSTACK(STACK_2); pushSTACK(S(open));
  7719.           # type_error ?? 
  7720.           //: DEUTSCH "~: Als :ELEMENT-TYPE-Argument ist ~ unzulässig."
  7721.           //: ENGLISH "~: illegal :ELEMENT-TYPE argument ~"
  7722.           //: FRANCAIS "~ : ~ n'est pas permis comme argument pour :ELEMENT-TYPE."
  7723.           fehler(error,GETTEXT("~: illegal :ELEMENT-TYPE argument ~"));
  7724.     }   }
  7725.     # :if-exists überprüfen und in if_exists übersetzen:
  7726.     { var reg1 object arg = STACK_1;
  7727.       if (eq(arg,unbound)) { if_exists = 0; }
  7728.       elif (eq(arg,S(Kerror))) { if_exists = 1; }
  7729.       elif (eq(arg,NIL)) { if_exists = 2; }
  7730.       elif (eq(arg,S(Krename))) { if_exists = 3; }
  7731.       elif (eq(arg,S(Krename_and_delete))) { if_exists = 4; }
  7732.       elif (eq(arg,S(Knew_version)) || eq(arg,S(Ksupersede))) { if_exists = 5; }
  7733.       elif (eq(arg,S(Kappend))) { if_exists = 6; }
  7734.       elif (eq(arg,S(Koverwrite))) { if_exists = 7; }
  7735.       else
  7736.       { pushSTACK(arg); # Wert für Slot DATUM von TYPE-ERROR
  7737.         pushSTACK(O(type_if_exists)); # Wert für Slot EXPECTED-TYPE von TYPE-ERROR
  7738.         pushSTACK(arg); pushSTACK(S(open));
  7739.         //: DEUTSCH "~: Als :IF-EXISTS-Argument ist ~ unzulässig."
  7740.         //: ENGLISH "~: illegal :IF-EXISTS argument ~"
  7741.         //: FRANCAIS "~ : ~ n'est pas permis comme argument pour :IF-EXISTS."
  7742.         fehler(type_error, GETTEXT("~: illegal :IF-EXISTS argument ~"));
  7743.     } }
  7744.     # :if-does-not-exist überprüfen und in if_not_exists übersetzen:
  7745.     { var reg1 object arg = STACK_0;
  7746.       if (eq(arg,unbound)) { if_not_exists = 0; }
  7747.       elif (eq(arg,S(Kerror))) { if_not_exists = 1; }
  7748.       elif (eq(arg,NIL)) { if_not_exists = 2; }
  7749.       elif (eq(arg,S(Kcreate))) { if_not_exists = 3; }
  7750.       else
  7751.       { pushSTACK(arg); # Wert für Slot DATUM von TYPE-ERROR
  7752.         pushSTACK(O(type_if_does_not_exist)); # Wert für Slot EXPECTED-TYPE von TYPE-ERROR
  7753.         pushSTACK(arg); pushSTACK(S(open));
  7754.         //: DEUTSCH "~: Als :IF-DOES-NOT-EXIST-Argument ist ~ unzulässig."
  7755.         //: ENGLISH "~: illegal :IF-DOES-NOT-EXIST argument ~"
  7756.         //: FRANCAIS "~ : ~ n'est pas permis comme argument pour :IF-DOES-NOT-EXIST."
  7757.         fehler(type_error, GETTEXT("~: illegal :IF-DOES-NOT-EXIST argument ~"));
  7758.     } }
  7759.     # File öffnen:
  7760.     skipSTACK(5);
  7761.     value1 = open_file(filename,direction,if_exists,if_not_exists,type,eltype_size);
  7762.     mv_count=1;
  7763.   }}
  7764.  
  7765. # UP: Liefert eine Liste aller matchenden Pathnames.
  7766. # directory_search(pathname)
  7767. # > pathname: Pathname mit Device /= :WILD
  7768. #if defined(UNIX) || defined(WIN32_UNIX)
  7769. # > STACK_1: Circle-Flag
  7770. #endif
  7771. # > STACK_0: Full-Flag
  7772. # < ergebnis:
  7773. #     Falls name=NIL und type=NIL:     Liste aller matchenden Directories,
  7774. #     sonst (name=NIL -> name=:WILD):  Liste aller matchenden Dateien.
  7775. #     Jeweils als absoluter Pathname ohne Wildcards,
  7776. #     bzw. bei Dateien und Full-Flag /=NIL als Liste
  7777. #          (Pathname Write-Date Length)
  7778. #          mit  Pathname ohne :WILD/:WILD-INFERIORS-Komponenten,
  7779. #               Write-Date = Datum der Dateierstellung (ss mm hh dd mm yy),
  7780. #                 als Decoded-Time passend für ENCODE-UNIVERSAL-TIME,
  7781. #               Length = Länge der Datei (in Bytes).
  7782. # kann GC auslösen
  7783.   local object directory_search (object pathname);
  7784.   # Methode: Breadth-first-search, damit nur eine Suchoperation gleichzeitig
  7785.   # läuft.
  7786.   #
  7787.   #ifdef PATHNAME_EXT83
  7788.   #
  7789.   #ifdef WATCOM
  7790.     # Die findfirst/findnext-Routinen sollen gefälligst errno setzen:
  7791.     local int findfirst (const char * path, struct ffblk * buf, unsigned int attr);
  7792.     local int findnext (struct ffblk * buf);
  7793.     local int findfirst(path,buf,attr)
  7794.       var reg2 const char * path;
  7795.       var reg3 struct ffblk * buf;
  7796.       var reg4 unsigned int attr;
  7797.       { var reg1 unsigned int result = _dos_findfirst(path,attr,buf);
  7798.         if (result==0)
  7799.           { return 0; } # kein Error
  7800.           else
  7801.           { errno = result; # = _doserrno;
  7802.             return -1; # Error
  7803.       }   }
  7804.     local int findnext(buf)
  7805.       var reg2 struct ffblk * buf;
  7806.       { var reg1 unsigned int result = _dos_findnext(buf);
  7807.         if (result==0)
  7808.           { return 0; } # kein Error
  7809.           else
  7810.           { errno = result; # = _doserrno;
  7811.             return -1; # Error
  7812.       }   }
  7813.   #endif
  7814.   #
  7815.   # UP: Extrahiert Name und Typ aus dem DTA-Buffer.
  7816.   # Es wird angenommen, daß Name und Typ aus zulässigen Großbuchstaben
  7817.   # bestehen und eine Länge <= 8 bzw. 3 haben.
  7818.   # > asciz: Adresse des ASCIZ-Strings im DTA-Buffer
  7819.   # > def: Default-Typ
  7820.   # < -(STACK): Typ
  7821.   # < -(STACK): Name
  7822.   # Erniedrigt STACK um 2.
  7823.   # kann GC auslösen
  7824.     local void extract (const uintB* asciz, object def);
  7825.     local void extract(asciz,def)
  7826.       var reg3 const uintB* asciz;
  7827.       var reg4 object def;
  7828.       { pushSTACK(def); # Default-Typ in den Stack
  7829.        {# in Name.Typ aufspalten:
  7830.         var reg1 const uintB* ptr = asciz;
  7831.         var reg2 uintL count = 0;
  7832.         loop
  7833.           { var reg3 uintB ch = *ptr; # nächstes Zeichen
  7834.             if ((ch == 0) || (ch == '.')) # bei Nullbyte oder '.'
  7835.               break; # ist der Name zu Ende
  7836.             ptr++; count++; # weiterrücken
  7837.           }
  7838.         pushSTACK(make_string(asciz,count)); # String für Name erzeugen
  7839.         if (*ptr++ == 0) # mit Nullbyte beendet ?
  7840.           ; # ja -> Typ bleibt Default
  7841.           else
  7842.           { asciz = ptr; count = 0;
  7843.             until (*ptr++ == 0) { count++; } # bei Nullbyte ist der Typ zu Ende
  7844.             STACK_1 = make_string(asciz,count); # String für Typ erzeugen
  7845.           }
  7846.       }}
  7847.   #
  7848.   # UP: Sucht Subdirectories eines gegebenen Pathname.
  7849.   # subdirs(pathstring)
  7850.   # STACK_0 = Pathname, dessen Subdirectories zu suchen sind
  7851.   # STACK_1 = Liste, auf die die Pathnames der matchenden Subdirectories
  7852.   #           gepusht werden
  7853.   # > pathstring: Suchpfad als fertiger ASCIZ-String
  7854.   # verändert STACK_1, kann GC auslösen
  7855.     local void subdirs (object pathstring);
  7856.     local void subdirs(pathstring)
  7857.       var reg3 object pathstring;
  7858.       {
  7859.        #if defined(MSDOS) && !defined(WIN32_DOS)
  7860.         # Dateisuche gemäß DOS-Konvention:
  7861.         var struct ffblk DTA_buffer;
  7862.         set_break_sem_4(); # wegen DTA-Buffer gegen Unterbrechungen sperren
  7863.         # Suchanfang, suche nach Ordnern und normalen Dateien:
  7864.         begin_system_call();
  7865.         if (findfirst(TheAsciz(pathstring),&DTA_buffer,FA_DIREC|FA_ARCH|FA_RDONLY) <0)
  7866.           { if (!((errno==ENOENT)
  7867.                || (errno==ENOMORE)
  7868.                ) ) { OS_error(); } }
  7869.           else # Keine Datei gefunden -> Schleife nicht durchlaufen
  7870.           loop
  7871.             { # Stackaufbau: new-pathname-list, pathname.
  7872.               end_system_call();
  7873.               # gefundene Datei untersuchen:
  7874.               if (DTA_buffer.ff_attrib & FA_DIREC) # sollte ein Unterdirectory sein
  7875.                 if (!(DTA_buffer.ff_name[0] == '.')) # sollte nicht mit '.' anfangen
  7876.                   # (sonst ist es wohl '.' oder '..', wird übergangen)
  7877.                   { # in Name.Typ aufspalten, Default-Typ "" :
  7878.                     extract(&DTA_buffer.ff_name[0],O(leer_string));
  7879.                    {var reg1 object new_cons = allocate_cons();
  7880.                     Car(new_cons) = popSTACK(); Cdr(new_cons) = popSTACK();
  7881.                     # new_cons = (name . type)
  7882.                     pushSTACK(new_cons);
  7883.                     new_cons = allocate_cons();
  7884.                     Car(new_cons) = popSTACK();
  7885.                     # in ein-elementiger Liste new_cons = list1 = ((name . type))
  7886.                     pushSTACK(new_cons);
  7887.                    }# Stackaufbau: new-pathname-list, pathname, list1.
  7888.                     # letzten Pathname kopieren:
  7889.                    {var reg1 object temp = copy_pathname(STACK_1);
  7890.                     pushSTACK(temp);
  7891.                     # und darin Directory um list1 = ((name . type)) verlängern:
  7892.                     # (append pathname-dir list1) = (nreconc (reverse pathname-dir) list1)
  7893.                     temp = reverse(ThePathname(temp)->pathname_directory);
  7894.                     temp = nreconc(temp,STACK_1);
  7895.                     ThePathname(STACK_0)->pathname_directory = temp;
  7896.                    }# Stackaufbau: new-pathname-list, pathname, list1, newpathname.
  7897.                     # newpathname auf die Liste new-pathname-list pushen:
  7898.                    {var reg1 object new_cons = allocate_cons();
  7899.                     Car(new_cons) = popSTACK(); skipSTACK(1);
  7900.                     Cdr(new_cons) = STACK_1; STACK_1 = new_cons;
  7901.                   }}
  7902.               # nächstes File:
  7903.               begin_system_call();
  7904.               if (findnext(&DTA_buffer) <0)
  7905.                 { if (!((errno==ENOENT) 
  7906.                      || (errno==ENOMORE)
  7907.                      #ifdef EACESS  # DJGPP2
  7908.                      || (errno==EACESS)
  7909.                      #endif
  7910.                      ) ) { OS_error(); }
  7911.                   break; # Keine weitere Datei -> Schleifenende
  7912.                 }
  7913.             }
  7914.         end_system_call();
  7915.         clr_break_sem_4();
  7916.        #elif defined(WIN32_DOS)
  7917.         # Dateisuche gemäß DOS-Konvention:
  7918.         var WIN32_FIND_DATA ffd;
  7919.         var HANDLE hFindFile;
  7920.         set_break_sem_4(); # wegen DTA-Buffer gegen Unterbrechungen sperren
  7921.         # Suchanfang, suche nach Ordnern und normalen Dateien:
  7922.         begin_system_call();
  7923.         if ((hFindFile = FindFirstFile(TheAsciz(pathstring),&ffd)) == INVALID_HANDLE_VALUE) 
  7924.             { OS_error(); }
  7925.           else # Keine Datei gefunden -> Schleife nicht durchlaufen
  7926.           loop
  7927.             { # Stackaufbau: new-pathname-list, pathname.
  7928.               end_system_call();
  7929.               # gefundene Datei untersuchen:
  7930.               if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) # sollte ein Unterdirectory sein
  7931.                 if (!(ffd.cFileName[0] == '.')) # sollte nicht mit '.' anfangen
  7932.                   # (sonst ist es wohl '.' oder '..', wird übergangen)
  7933.                   { # in Name.Typ aufspalten, Default-Typ "" :
  7934.                     extract(&ffd.cFileName[0],O(leer_string));
  7935.                    {var reg1 object new_cons = allocate_cons();
  7936.                     Car(new_cons) = popSTACK(); Cdr(new_cons) = popSTACK();
  7937.                     # new_cons = (name . type)
  7938.                     pushSTACK(new_cons);
  7939.                     new_cons = allocate_cons();
  7940.                     Car(new_cons) = popSTACK();
  7941.                     # in ein-elementiger Liste new_cons = list1 = ((name . type))
  7942.                     pushSTACK(new_cons);
  7943.                    }# Stackaufbau: new-pathname-list, pathname, list1.
  7944.                     # letzten Pathname kopieren:
  7945.                    {var reg1 object temp = copy_pathname(STACK_1);
  7946.                     pushSTACK(temp);
  7947.                     # und darin Directory um list1 = ((name . type)) verlängern:
  7948.                     # (append pathname-dir list1) = (nreconc (reverse pathname-dir) list1)
  7949.                     temp = reverse(ThePathname(temp)->pathname_directory);
  7950.                     temp = nreconc(temp,STACK_1);
  7951.                     ThePathname(STACK_0)->pathname_directory = temp;
  7952.                    }# Stackaufbau: new-pathname-list, pathname, list1, newpathname.
  7953.                     # newpathname auf die Liste new-pathname-list pushen:
  7954.                    {var reg1 object new_cons = allocate_cons();
  7955.                     Car(new_cons) = popSTACK(); skipSTACK(1);
  7956.                     Cdr(new_cons) = STACK_1; STACK_1 = new_cons;
  7957.                   }}
  7958.               # nächstes File:
  7959.               begin_system_call();
  7960.               if (FindNextFile (hFindFile, &ffd) == FALSE)
  7961.                 { if (GetLastError() != ERROR_NO_MORE_FILES) { OS_error(); }
  7962.                   break; # Keine weitere Datei -> Schleifenende
  7963.                 }
  7964.             }
  7965.         end_system_call();
  7966.         clr_break_sem_4();
  7967.        #endif
  7968.       }
  7969.   #
  7970.   # UP: Sucht alle Subdirectories (beliebiger Tiefe) eines gegebenen Pathname.
  7971.   # allsubdirs(pathnamelist)
  7972.   # > pathnamelist: Liste, dessen CAR der gegebene Pathname ist.
  7973.   # Die Pathnames aller echten Subdirectories (beliebiger Tiefe) werden als
  7974.   # Liste destruktiv zwischen pathnamelist und (cdr pathnamelist) gehängt.
  7975.   # < ergebnis: das ursprüngliche (cdr pathnamelist)
  7976.   # kann GC auslösen
  7977.     local object allsubdirs (object pathnamelist);
  7978.     local object allsubdirs(pathnamelist)
  7979.       var reg1 object pathnamelist;
  7980.       { pushSTACK(pathnamelist);
  7981.         pushSTACK(NIL); # new-pathname-list := NIL
  7982.         {var reg2 object pathname = Car(pathnamelist);
  7983.          pushSTACK(pathname);
  7984.          # Stackaufbau: pathnamelist, new-pathname-list, pathname.
  7985.          {var reg3 uintC stringcount =
  7986.             directory_namestring_parts(pathname); # Directory-Namestring-Teile,
  7987.           pushSTACK(O(wild_wild_string)); # "*.*"
  7988.           pushSTACK(O(null_string)); # und Nullbyte
  7989.           {var reg4 object pathstring = string_concat(stringcount+1+1); # zusammenhängen
  7990.            subdirs(pathstring); # alle subdirs auf new-pathname-list pushen
  7991.         }}}
  7992.         skipSTACK(1); # pathname vergessen
  7993.         { var reg2 object new_pathname_list = popSTACK();
  7994.           pathnamelist = popSTACK();
  7995.           # Stackaufbau: (leer).
  7996.           # Mit  (setf (cdr pathnamelist)
  7997.           #            (nreconc new-pathname-list (cdr pathnamelist))
  7998.           #      )
  7999.           # die new-pathname-list umdrehen und gleichzeitig einhängen:
  8000.           new_pathname_list = nreconc(new_pathname_list,Cdr(pathnamelist));
  8001.           pushSTACK(Cdr(pathnamelist)); Cdr(pathnamelist) = new_pathname_list;
  8002.           pathnamelist = new_pathname_list;
  8003.         }
  8004.         # Stackaufbau: ursprüngliches (cdr pathnamelist).
  8005.         # Liste pathnamelist durchlaufen, bis bei STACK_0 angelangt,
  8006.         # und rekursiv alle Subdirectories bestimmen und einhängen:
  8007.         until (eq(pathnamelist,STACK_0))
  8008.           { pathnamelist = allsubdirs(pathnamelist); }
  8009.         skipSTACK(1);
  8010.         return pathnamelist;
  8011.       }
  8012.   #
  8013.   local object directory_search(pathname)
  8014.     var reg4 object pathname;
  8015.     { pathname = use_default_dir(pathname); # Default-Directory einfügen
  8016.       # pathname ist jetzt ein Pathname, bei dem Device ein überprüfter
  8017.       # String ist und Directory [die Seriennummer, aber] kein
  8018.       # :RELATIVE, :CURRENT, :PARENT enthält.
  8019.       pushSTACK(pathname);
  8020.       pushSTACK(ThePathname(pathname)->pathname_directory); # subdir-list
  8021.       # pathname kopieren:
  8022.       pushSTACK(copy_pathname(pathname));
  8023.       # und dessen Directory auf ([Seriennummer] :ABSOLUTE) verkürzen:
  8024.       {var reg1 object new_cons = allocate_cons(); # neues Cons mit CDR=NIL
  8025.        Car(new_cons) = S(Kabsolute); # :ABSOLUTE als CAR
  8026.        ThePathname(STACK_0)->pathname_directory = new_cons;
  8027.       }
  8028.       # und in einelementige Liste packen:
  8029.       {var reg1 object new_cons = allocate_cons();
  8030.        Car(new_cons) = STACK_0;
  8031.        STACK_0 = new_cons;
  8032.       }
  8033.       while
  8034.         # Stackaufbau: pathname, subdir-list, pathname-list.
  8035.         # Dabei enthalten die Pathnames aus pathname-list das Directory
  8036.         # nur so tief, daß es danach mit (cdr subdir-list) weitergeht.
  8037.         # Nächste subdir-Ebene abarbeiten:
  8038.         (consp (STACK_1 = Cdr(STACK_1))) # subdir-list verkürzen
  8039.         { # pathname-list durchgehen und dabei neue Liste aufbauen:
  8040.           pushSTACK(STACK_0); pushSTACK(NIL);
  8041.           loop
  8042.             { # Stackaufbau: ..., pathname-list-rest, new-pathname-list.
  8043.               var reg2 object pathname_list_rest = STACK_1;
  8044.               if (atomp(pathname_list_rest)) break;
  8045.               STACK_1 = Cdr(pathname_list_rest);
  8046.              {var reg5 object next_pathname = Car(pathname_list_rest); # nächster Pathname
  8047.               var reg3 object subdir_list = STACK_(1+2);
  8048.               pushSTACK(next_pathname); # in den Stack
  8049.               if (!eq(Car(subdir_list),S(Kwild_inferiors))) # nächstes subdir = :WILD-INFERIORS ?
  8050.                 { # normales subdir:
  8051.                   var reg1 uintC stringcount =
  8052.                     directory_namestring_parts(next_pathname); # Directory-Namestring-Teile (keine GC!)
  8053.                   stringcount +=
  8054.                     subdir_namestring_parts(subdir_list); # und Strings zum nächsten subdir
  8055.                   pushSTACK(O(null_string)); stringcount += 1; # und Nullbyte
  8056.                  {var reg6 object pathstring = string_concat(stringcount); # zusammenhängen
  8057.                   subdirs(pathstring); # alle subdirs auf new-pathname-list pushen
  8058.                   skipSTACK(1); # next-pathname vergessen
  8059.                 }}
  8060.                 else
  8061.                 { # subdir = :WILD-INFERIORS -> alle Subdirs bestimmen:
  8062.                   {var reg1 object list1 = allocate_cons();
  8063.                    Car(list1) = STACK_0;
  8064.                    STACK_0 = list1; # einelementige Liste (next-pathname)
  8065.                    allsubdirs(list1); # alle Subdirectories bestimmen
  8066.                   }
  8067.                   # Liste aller Subdirectories vor new-pathname-list
  8068.                   # in umgekehrter Reihenfolge davorhängen:
  8069.                   # (nreconc subdirlist new-pathname-list)
  8070.                   {var reg1 object newsubdirlist = popSTACK();
  8071.                    STACK_0 = nreconc(newsubdirlist,STACK_0);
  8072.                 } }
  8073.               # nächsten Pathname aus pathname-list-rest nehmen
  8074.             }}
  8075.          {var reg1 object new_pathname_list = popSTACK(); skipSTACK(1);
  8076.           # umdrehen und als nächste pathname-list verwenden:
  8077.           STACK_0 = nreverse(new_pathname_list);
  8078.         }}
  8079.       # Stackaufbau: pathname, nix, pathname-list.
  8080.       pathname = STACK_2;
  8081.       {var reg2 object name = ThePathname(pathname)->pathname_name;
  8082.        var reg3 object type = ThePathname(pathname)->pathname_type;
  8083.        if (nullp(name)) # Name=NIL ?
  8084.          { if (nullp(type)) # auch Typ=NIL ?
  8085.              { var reg1 object new_pathname_list = popSTACK(); # ja ->
  8086.                skipSTACK(2); return new_pathname_list; # schon fertig
  8087.              }
  8088.              else
  8089.              # nein -> verwende :WILD (statt NIL) als Name
  8090.              { name = S(Kwild); }
  8091.          }
  8092.        # Alle Files name.type in den gegebenen Subdirectories suchen:
  8093.        { var reg1 uintC stringcount =
  8094.            nametype_namestring_parts(name,type,ThePathname(pathname)->pathname_version); # Teilstrings zu Name und Typ
  8095.          pushSTACK(O(null_string)); stringcount++; # und Nullbyte
  8096.         {var reg2 object name_type_asciz = string_concat(stringcount);
  8097.          STACK_2 = name_type_asciz;
  8098.       }}}
  8099.       STACK_1 = STACK_0; # pathname-list
  8100.       STACK_0 = NIL; # new-pathname-list := NIL
  8101.       # Stackaufbau: name-type-asciz, pathname-list, new-pathname-list.
  8102.       loop
  8103.         { var reg5 object pathname_list_rest = STACK_1;
  8104.           if (atomp(pathname_list_rest)) break;
  8105.           STACK_1 = Cdr(pathname_list_rest);
  8106.          {var reg8 object next_pathname = Car(pathname_list_rest); # nächster Pathname
  8107.           var reg7 object name_type_asciz = STACK_2;
  8108.           pushSTACK(next_pathname); # in den Stack
  8109.           {var reg3 uintC stringcount =
  8110.              directory_namestring_parts(next_pathname); # Directory-Namestring-Teile (keine GC!)
  8111.            pushSTACK(name_type_asciz); stringcount += 1; # und name-type-asciz
  8112.            {var reg6 object pathstring = string_concat(stringcount); # zusammenhängen
  8113.             #if defined(MSDOS) && !defined(WIN32_DOS)
  8114.              # Dateisuche gemäß DOS-Konvention:
  8115.              var struct ffblk DTA_buffer;
  8116.              set_break_sem_4(); # wegen DTA-Buffer gegen Unterbrechungen sperren
  8117.              # Suchanfang, suche nur nach normalen Dateien:
  8118.              begin_system_call();
  8119.              if (findfirst(TheAsciz(pathstring),&DTA_buffer,FA_ARCH|FA_RDONLY) <0)
  8120.                { if (!((errno==ENOENT) 
  8121.                     || (errno==ENOMORE)
  8122.                     ) ) { OS_error(); } }
  8123.                else # Keine Datei gefunden -> Schleife nicht durchlaufen
  8124.                loop
  8125.                  { # Stackaufbau: ..., next-pathname.
  8126.                    end_system_call();
  8127.                    # gefundene Datei untersuchen:
  8128.                    { # in Name.Typ aufspalten, Default-Typ NIL :
  8129.                      extract(&DTA_buffer.ff_name[0],NIL);
  8130.                     {# letzten Pathname kopieren und Name und Typ eintragen:
  8131.                      var reg1 object new = copy_pathname(STACK_2);
  8132.                      ThePathname(new)->pathname_name = popSTACK();
  8133.                      ThePathname(new)->pathname_type = popSTACK();
  8134.                      # Full-Flag abtesten und evtl. mehr Information besorgen:
  8135.                      if (!nullp(STACK_(0+3+1)))
  8136.                        { pushSTACK(new); # newpathname als 1. Listenelement
  8137.                          pushSTACK(new); # newpathname als 2. Listenelement
  8138.                          { # Uhrzeit und Datum von DOS-Format in Decoded-Time umwandeln:
  8139.                            var decoded_time timepoint;
  8140.                            convert_timedate((uintW)DTA_buffer.ff_ftime,(uintW)DTA_buffer.ff_fdate,
  8141.                                         &timepoint);
  8142.                            pushSTACK(timepoint.Sekunden);
  8143.                            pushSTACK(timepoint.Minuten);
  8144.                            pushSTACK(timepoint.Stunden);
  8145.                            pushSTACK(timepoint.Tag);
  8146.                            pushSTACK(timepoint.Monat);
  8147.                            pushSTACK(timepoint.Jahr);
  8148.                            new = listof(6); # 6-elementige Liste bauen
  8149.                          }
  8150.                          pushSTACK(new); # als 3. Listenelement
  8151.                          pushSTACK(UL_to_I(*(uintL*)(&DTA_buffer.ff_fsize))); # Länge als 4. Listenelement
  8152.                          new = listof(4); # 4-elementige Liste bauen
  8153.                        }
  8154.                      # new auf die Liste new-pathname-list pushen:
  8155.                       pushSTACK(new);
  8156.                      {var reg1 object new_cons = allocate_cons();
  8157.                       Car(new_cons) = popSTACK();
  8158.                       Cdr(new_cons) = STACK_(0+1);
  8159.                       STACK_(0+1) = new_cons;
  8160.                    }}}
  8161.                    # nächstes File:
  8162.                    begin_system_call();
  8163.                    if (findnext(&DTA_buffer) <0)
  8164.                      { if (!((errno==ENOENT) 
  8165.                           || (errno==ENOMORE)
  8166.                           ) ) { OS_error(); }
  8167.                        break; # Keine weitere Datei -> Schleifenende
  8168.                      }
  8169.                  }
  8170.              end_system_call();
  8171.              clr_break_sem_4();
  8172.             #elif defined(WIN32_DOS)
  8173.              # Dateisuche gemäß DOS-Konvention:
  8174.              var WIN32_FIND_DATA ffd;
  8175.              var HANDLE hFindFile;
  8176.              set_break_sem_4(); # wegen DTA-Buffer gegen Unterbrechungen sperren
  8177.              # Suchanfang, suche nur nach normalen Dateien:
  8178.              begin_system_call();
  8179.              if ((hFindFile=FindFirstFile(TheAsciz(pathstring),&ffd)) == INVALID_HANDLE_VALUE)
  8180.                { OS_error(); }
  8181.                else # Keine Datei gefunden -> Schleife nicht durchlaufen
  8182.                loop
  8183.                  { # Stackaufbau: ..., next-pathname.
  8184.                    end_system_call();
  8185.                    # gefundene Datei untersuchen:
  8186.                    { # in Name.Typ aufspalten, Default-Typ NIL :
  8187.                      extract(&ffd.cFileName[0],NIL);
  8188.                     {# letzten Pathname kopieren und Name und Typ eintragen:
  8189.                      var reg1 object new = copy_pathname(STACK_2);
  8190.                      ThePathname(new)->pathname_name = popSTACK();
  8191.                      ThePathname(new)->pathname_type = popSTACK();
  8192.                      # Full-Flag abtesten und evtl. mehr Information besorgen:
  8193.                      if (!nullp(STACK_(0+3+1)))
  8194.                        { pushSTACK(new); # newpathname als 1. Listenelement
  8195.                          pushSTACK(new); # newpathname als 2. Listenelement
  8196.                          { # Uhrzeit und Datum von DOS-Format in Decoded-Time umwandeln:
  8197.                            var decoded_time timepoint;
  8198.                            var WORD date;
  8199.                            var WORD time;
  8200.                            FileTimeToDosDateTime(&ffd.ftLastWriteTime,&date,&time);
  8201.                            convert_timedate((uintW)time,(uintW)date,&timepoint);
  8202.                            pushSTACK(timepoint.Sekunden);
  8203.                            pushSTACK(timepoint.Minuten);
  8204.                            pushSTACK(timepoint.Stunden);
  8205.                            pushSTACK(timepoint.Tag);
  8206.                            pushSTACK(timepoint.Monat);
  8207.                            pushSTACK(timepoint.Jahr);
  8208.                            new = listof(6); # 6-elementige Liste bauen
  8209.                          }
  8210.                          pushSTACK(new); # als 3. Listenelement
  8211.                          pushSTACK(UL_to_I(*(uintL*)(&ffd.nFileSizeLow))); # Länge als 4. Listenelement
  8212.                          new = listof(4); # 4-elementige Liste bauen
  8213.                        }
  8214.                      # new auf die Liste new-pathname-list pushen:
  8215.                       pushSTACK(new);
  8216.                      {var reg1 object new_cons = allocate_cons();
  8217.                       Car(new_cons) = popSTACK();
  8218.                       Cdr(new_cons) = STACK_(0+1);
  8219.                       STACK_(0+1) = new_cons;
  8220.                    }}}
  8221.                    # nächstes File:
  8222.                    begin_system_call();
  8223.                    if (FindNextFile(hFindFile,&ffd) == FALSE)
  8224.                      { if (GetLastError() != ERROR_NO_MORE_FILES) 
  8225.                          { OS_error(); }
  8226.                        break; # Keine weitere Datei -> Schleifenende
  8227.                      }
  8228.                  }
  8229.              end_system_call();
  8230.              clr_break_sem_4();
  8231.             #endif
  8232.           }}
  8233.           skipSTACK(1); # next-pathname vergessen
  8234.         }}
  8235.       {# new-pathname-list wieder umdrehen:
  8236.        var reg1 object new_pathname_list = nreverse(popSTACK());
  8237.        skipSTACK(2); return new_pathname_list;
  8238.     } }
  8239.   #
  8240.   #endif # PATHNAME_EXT83
  8241.   #
  8242.   #if defined(PATHNAME_NOEXT) || defined(PATHNAME_RISCOS)
  8243.   #
  8244.   # UP: Erweitert das Directory eines Pathname um eine Komponente.
  8245.   # > STACK_1: ein Pathname
  8246.   # > STACK_0: neue Subdir-Komponente, ein Simple-String
  8247.   # < ergebnis: neuer Pathname mit um subdir verlängertem Directory
  8248.   # Erhöht STACK um 2
  8249.   # kann GC auslösen
  8250.   local object pathname_add_subdir (void);
  8251.   local object pathname_add_subdir()
  8252.     { # Pathname kopieren und dessen Directory gemäß
  8253.       # (append x (list y)) = (nreverse (cons y (reverse x))) verlängern:
  8254.       var reg2 object pathname = copy_pathname(STACK_1);
  8255.       STACK_1 = pathname;
  8256.       pushSTACK(reverse(ThePathname(pathname)->pathname_directory));
  8257.      {var reg1 object new_cons = allocate_cons();
  8258.       Cdr(new_cons) = popSTACK();
  8259.       Car(new_cons) = popSTACK();
  8260.       new_cons = nreverse(new_cons);
  8261.       pathname = popSTACK();
  8262.       ThePathname(pathname)->pathname_directory = new_cons;
  8263.       return pathname;
  8264.     }}
  8265.   #
  8266.   #if defined(UNIX) || defined(AMIGAOS) || defined(RISCOS) || defined(WIN32_UNIX)
  8267.   # UP: Erweitert einen Pathname um die File-Information.
  8268.   # > STACK_1: absoluter Pathname
  8269.   # > STACK_0: absoluter Pathname, Links aufgelöst
  8270.   # > *filestatus: dessen stat-Info
  8271.   # < STACK_0: Liste (Pathname Truename Write-Date Length [Kommentar]) im :FULL-Format
  8272.   local void with_stat_info (void);
  8273.   local void with_stat_info()
  8274.     { var reg2 object new;
  8275.       #if defined(UNIX) || defined(RISCOS) || defined(WIN32_UNIX)
  8276.       var reg3 uintL size = filestatus->st_size;
  8277.       #endif
  8278.       #ifdef AMIGAOS
  8279.       var reg3 uintL size = filestatus->fib_Size;
  8280.       #endif
  8281.       # Pathname schon in STACK_1, als 1. Listenelement
  8282.       # Truename schon in STACK_0, als 2. Listenelement
  8283.       { var decoded_time timepoint; # Write-Date in decodierter Form
  8284.         #if defined(UNIX) || defined(RISCOS) || defined(WIN32_UNIX)
  8285.         convert_time(&filestatus->st_mtime,&timepoint);
  8286.         #endif
  8287.         #ifdef AMIGAOS
  8288.         convert_time(&filestatus->fib_Date,&timepoint);
  8289.         #endif
  8290.         pushSTACK(timepoint.Sekunden);
  8291.         pushSTACK(timepoint.Minuten);
  8292.         pushSTACK(timepoint.Stunden);
  8293.         pushSTACK(timepoint.Tag);
  8294.         pushSTACK(timepoint.Monat);
  8295.         pushSTACK(timepoint.Jahr);
  8296.         new = listof(6); # 6-elementige Liste bauen
  8297.       }
  8298.       pushSTACK(new); # als 3. Listenelement
  8299.       pushSTACK(UL_to_I(size)); # Länge als 4. Listenelement
  8300.       #if defined(UNIX) || defined(RISCOS) || defined(WIN32_UNIX)
  8301.       new = listof(4); # 4-elementige Liste bauen
  8302.       #endif
  8303.       #ifdef AMIGAOS
  8304.       pushSTACK(asciz_to_string(&filestatus->fib_Comment[0])); # Kommentar als 5. Listenelement
  8305.       new = listof(5); # 5-elementige Liste bauen
  8306.       #endif
  8307.       pushSTACK(Car(new)); # pathname wieder in den Stack
  8308.       pushSTACK(new); # Liste in den Stack
  8309.     }
  8310.   #endif
  8311.   #
  8312.   local object directory_search(pathname)
  8313.     var reg9 object pathname;
  8314.     {
  8315.       #ifdef PATHNAME_RISCOS
  8316.       # If we search for a file with type /= NIL, we have to interpret the last
  8317.       # subdir as the type.
  8318.       var boolean name_and_type = FALSE;
  8319.       #endif
  8320.       pathname = use_default_dir(pathname); # Default-Directory einfügen
  8321.       # pathname ist jetzt neu und ein absoluter Pathname.
  8322.       pushSTACK(NIL); # result-list := NIL
  8323.       pushSTACK(pathname);
  8324.       # Falls name=NIL und type/=NIL: Setze name := "*".
  8325.       if (nullp(ThePathname(pathname)->pathname_name)
  8326.           && !nullp(ThePathname(pathname)->pathname_type)
  8327.          )
  8328.         { ThePathname(pathname)->pathname_name = O(wild_string); }
  8329.       #ifdef PATHNAME_RISCOS
  8330.       # If the name and type are both set, then make the type part of
  8331.       # the directory specification and set the new type to NIL.
  8332.       if (!nullp(ThePathname(pathname)->pathname_name)
  8333.           && !nullp(ThePathname(pathname)->pathname_type)
  8334.          )
  8335.         { name_and_type = TRUE;
  8336.           pushSTACK(pathname); pushSTACK(ThePathname(pathname)->pathname_type);
  8337.           STACK_0 = pathname = pathname_add_subdir();
  8338.           ThePathname(pathname)->pathname_type = NIL;
  8339.         }
  8340.       #endif
  8341.       # Zum Matchen: Name und Typ zu einem String zusammenfassen:
  8342.       if (nullp(ThePathname(pathname)->pathname_name))
  8343.         { pushSTACK(NIL); } # name=NIL -> auch type=NIL -> keine Files suchen
  8344.         else
  8345.         {var reg1 uintC stringcount = file_namestring_parts(pathname);
  8346.          var reg1 object nametype_string = string_concat(stringcount);
  8347.          pathname = STACK_0;
  8348.          pushSTACK(nametype_string);
  8349.         }
  8350.       pushSTACK(ThePathname(pathname)->pathname_directory); # subdir-list
  8351.       #ifdef PATHNAME_RISCOS
  8352.       STACK_0 = Cdr(STACK_0); # Liste fängt mit (:ABSOLUTE :ROOT ...) an, verkürze sie
  8353.       #endif
  8354.       # pathname kopieren und dabei Name und Typ streichen und
  8355.       # Directory zu (:ABSOLUTE) bzw. (:ABSOLUTE :ROOT) verkürzen:
  8356.       pathname = copy_pathname(pathname);
  8357.       ThePathname(pathname)->pathname_name = NIL;
  8358.       ThePathname(pathname)->pathname_type = NIL;
  8359.       ThePathname(pathname)->pathname_directory = O(directory_absolute);
  8360.       pushSTACK(pathname);
  8361.       # und in einelementige Liste packen:
  8362.       {var reg1 object new_cons = allocate_cons();
  8363.        Car(new_cons) = STACK_0;
  8364.        STACK_0 = new_cons;
  8365.       }
  8366.      {var reg7 boolean recursively = # Flag, ob die nächste Operation auf
  8367.         FALSE;                       # alle Subdirectories anzuwenden ist.
  8368.       loop
  8369.         # Stackaufbau: result-list, pathname, name&type, subdir-list, pathname-list.
  8370.         # result-list = Liste der fertigen Pathnames/Listen, umgedreht.
  8371.         # name&type = NIL oder Simple-String, gegen den die Filenamen zu matchen sind.
  8372.         # pathname-list = Liste der noch abzuarbeitenden Directories.
  8373.         # Dabei enthalten die Pathnames aus pathname-list das Directory
  8374.         # nur so tief, daß es danach mit (cdr subdir-list) weitergeht.
  8375.         { # Nächste subdir-Ebene abarbeiten:
  8376.           STACK_1 = Cdr(STACK_1); # subdir-list verkürzen
  8377.          {var reg6 signean next_task; # Was mit den Dirs aus pathname-list zu tun ist:
  8378.             # 0: nichts, fertig
  8379.             # 1: nach einem File gegebenen Namens/Typs sehen
  8380.             # -1: nach einem Subdirectory gegebenen Namens sehen
  8381.             # 2: nach allen Files suchen, die gegebenen Namen/Typ matchen
  8382.             # -2: nach allen Subdirectories suchen, die gegebenen Namen matchen
  8383.           if (matomp(STACK_1)) # subdir-list zu Ende?
  8384.             { var reg1 object nametype = STACK_2;
  8385.               if (nullp(nametype)) # name=NIL und type=NIL -> keine Files suchen
  8386.                 { next_task = 0; }
  8387.               #ifndef MSDOS
  8388.               elif (!has_wildcards(nametype))
  8389.                    # === !(has_wildcards(name) || ((!nullp(type)) && has_wildcards(type)))
  8390.                 { next_task = 1; } # File suchen
  8391.               #endif
  8392.               else
  8393.                 { next_task = 2; } # Files mit Wildcards suchen
  8394.             }
  8395.             else
  8396.             { var reg1 object next_subdir = Car(STACK_1);
  8397.               if (eq(next_subdir,S(Kwild_inferiors))) # '...' ?
  8398.                 # wird erst beim nächsten Durchlauf behandelt
  8399.                 { recursively = TRUE; goto passed_subdir; }
  8400.               #ifndef MSDOS
  8401.               if (
  8402.                   #ifdef PATHNAME_AMIGAOS
  8403.                   eq(next_subdir,S(Kparent)) ||
  8404.                   #endif
  8405.                   #ifdef PATHNAME_RISCOS
  8406.                   !simple_string_p(next_subdir) ||
  8407.                   #endif
  8408.                   !has_wildcards(next_subdir)
  8409.                  )
  8410.                 { next_task = -1; } # Subdir suchen
  8411.                 else
  8412.               #endif
  8413.                 { next_task = -2; } # Subdirs mit Wildcards suchen
  8414.             }
  8415.           # pathname-list durchgehen und dabei neue Liste aufbauen:
  8416.           pushSTACK(NIL);
  8417.           #if defined(UNIX) || defined(WIN32_UNIX)
  8418.           if (!nullp(STACK_(1+5+1))) # ;CIRCLE-Flag abfragen
  8419.             { # Hash-Tabelle aller bisher abgesuchten Directories (jeweils
  8420.               # als Cons (dev . ino)) führen:
  8421.               pushSTACK(S(Ktest)); pushSTACK(S(equal));
  8422.               funcall(L(make_hash_table),2); # (MAKE-HASH-TABLE :TEST 'EQUAL)
  8423.               pushSTACK(value1);
  8424.             }
  8425.             else
  8426.             { pushSTACK(NIL); }
  8427.           #define H 1
  8428.           #else
  8429.           #define H 0
  8430.           #endif
  8431.           pushSTACK(STACK_(0+1+H));
  8432.           loop
  8433.             { # Stackaufbau: ..., new-pathname-list, [ht,] pathname-list-rest.
  8434.               var reg9 object pathname_list_rest = STACK_0;
  8435.               if (atomp(pathname_list_rest)) break;
  8436.               STACK_0 = Cdr(pathname_list_rest); # Liste verkürzen
  8437.               pushSTACK(NIL); # pathnames-to-insert := NIL
  8438.               # Stackaufbau: ..., new-pathname-list, [ht,] pathname-list-rest, pathnames-to-insert.
  8439.              {var reg9 object pathname = Car(pathname_list_rest); # nächstes Directory
  8440.               pushSTACK(pathname); # in den Stack
  8441.               # Versuche, die Task ein wenig abzukürzen:
  8442.               if (!recursively)
  8443.                 { switch (next_task)
  8444.                     { case 0: # Dieses pathname liefern
  8445.                         #if defined(UNIX) || defined(WIN32_UNIX)
  8446.                         assure_dir_exists(FALSE); # erst noch Links auflösen
  8447.                         #endif
  8448.                         # und STACK_0 vor result-list pushen:
  8449.                         {var reg1 object new_cons = allocate_cons();
  8450.                          Car(new_cons) = popSTACK();
  8451.                          Cdr(new_cons) = STACK_(4+(1+H+2));
  8452.                          STACK_(4+(1+H+2)) = new_cons;
  8453.                         }
  8454.                         goto next_pathname;
  8455.                       #ifndef MSDOS
  8456.                       case 1: # In diesem pathname nach einem File sehen
  8457.                         ThePathname(pathname)->pathname_name = # Name (/=NIL) einsetzen
  8458.                           ThePathname(STACK_(3+(1+H+2)+1))->pathname_name;
  8459.                         ThePathname(pathname)->pathname_type = # Typ einsetzen
  8460.                           ThePathname(STACK_(3+(1+H+2)+1))->pathname_type;
  8461.                         pushSTACK(pathname);
  8462.                         #ifdef PATHNAME_RISCOS
  8463.                         if (name_and_type && nullp(ThePathname(pathname)->pathname_type))
  8464.                           # Move the last subdir into the type slot of the pathname.
  8465.                           { # subdirs := (butlast subdirs) = (nreverse (cdr (reverse subdirs)))
  8466.                             var reg1 object subdirs = reverse(ThePathname(pathname)->pathname_directory);
  8467.                             pathname = STACK_0;
  8468.                             ThePathname(pathname)->pathname_type = Car(subdirs);
  8469.                             ThePathname(pathname)->pathname_directory = nreverse(Cdr(subdirs));
  8470.                           }
  8471.                         #endif
  8472.                         assure_dir_exists(FALSE); # Links auflösen, File suchen
  8473.                         if (file_exists(_EMA_)) # falls File existiert
  8474.                           { if (!nullp(STACK_(0+5+(1+H+2)+2))) # :FULL gewünscht?
  8475.                               { with_stat_info(); } # ja -> STACK_0 erweitern
  8476.                             # und STACK_0 vor result-list pushen:
  8477.                            {var reg1 object new_cons = allocate_cons();
  8478.                             Car(new_cons) = STACK_0;
  8479.                             Cdr(new_cons) = STACK_(4+(1+H+2)+2);
  8480.                             STACK_(4+(1+H+2)+2) = new_cons;
  8481.                           }}
  8482.                         skipSTACK(2);
  8483.                         goto next_pathname;
  8484.                       case -1: # In diesem pathname nach einem Subdirectory sehen
  8485.                         { var reg2 object namestring = assure_dir_exists(FALSE); # Links auflösen, Directory-Namestring
  8486.                           pushSTACK(namestring); # Directory-Namestring
  8487.                           {var reg1 object subdir = Car(STACK_(1+(1+H+2)+1+1)); # (car subdir-list)
  8488.                            #if defined(PATHNAME_AMIGAOS) || defined(PATHNAME_RISCOS)
  8489.                            if (eq(subdir,S(Kparent))) # für Parent-Directory
  8490.                              {
  8491.                                #ifdef PATHNAME_AMIGAOS
  8492.                                pushSTACK(O(slash_string)); # zusätzliches "/" ans Ende
  8493.                                #endif
  8494.                                #ifdef PATHNAME_RISCOS
  8495.                                pushSTACK(O(parent_string)); # zusätzliches "^" ans Ende
  8496.                                #endif
  8497.                              }
  8498.                              else
  8499.                            #endif
  8500.                            pushSTACK(subdir);
  8501.                           }
  8502.                           pushSTACK(O(null_string)); # und Nullbyte
  8503.                           namestring = string_concat(3); # zusammenhängen
  8504.                           # Information holen:
  8505.                          #if defined(UNIX) || defined(RISCOS) || defined(WIN32_UNIX)
  8506.                          {var struct stat status;
  8507.                           begin_system_call();
  8508.                           if (!( stat(TheAsciz(namestring),&status) ==0))
  8509.                             { if (!(errno==ENOENT)) { OS_error(); }
  8510.                               end_system_call();
  8511.                               # Subdirectory existiert nicht -> OK.
  8512.                             }
  8513.                             else
  8514.                             # File existiert.
  8515.                             { end_system_call();
  8516.                               if (S_ISDIR(status.st_mode)) # Ist es ein Directory?
  8517.                                 # ja -> neuen Pathname dazu bilden:
  8518.                                 { # pathname kopieren und dessen Directory um
  8519.                                   # (car subdir-list) verlängern:
  8520.                                   pushSTACK(Car(STACK_(1+(1+H+2)+1)));
  8521.                                  {var reg1 object pathname = pathname_add_subdir();
  8522.                                   pushSTACK(pathname);
  8523.                                  }# Diesen neuen Pathname vor new-pathname-list pushen:
  8524.                                  {var reg1 object new_cons = allocate_cons();
  8525.                                   Car(new_cons) = STACK_0;
  8526.                                   Cdr(new_cons) = STACK_(H+2+1);
  8527.                                   STACK_(H+2+1) = new_cons;
  8528.                                 }}
  8529.                          }  }
  8530.                          #endif
  8531.                          #ifdef AMIGAOS
  8532.                          { var LONGALIGNTYPE(struct FileInfoBlock) fib;
  8533.                            var reg1 struct FileInfoBlock * fibptr = LONGALIGN(&fib);
  8534.                            set_break_sem_4();
  8535.                            begin_system_call();
  8536.                           {var reg2 BPTR lock = Lock(TheAsciz(namestring),ACCESS_READ);
  8537.                            if (lock==BPTR_NULL)
  8538.                              { if (!(IoErr()==ERROR_OBJECT_NOT_FOUND)) { OS_error(); }
  8539.                                end_system_call();
  8540.                                clr_break_sem_4();
  8541.                                # Subdirectory existiert nicht -> OK.
  8542.                              }
  8543.                              else
  8544.                              # File existiert.
  8545.                              { if (! Examine(lock,fibptr) ) { UnLock(lock); OS_error(); }
  8546.                                UnLock(lock);
  8547.                                end_system_call();
  8548.                                clr_break_sem_4();
  8549.                                if (fibptr->fib_DirEntryType > 0) # Ist es ein Directory?
  8550.                                  # ja -> neuen Pathname dazu bilden:
  8551.                                  { # pathname kopieren und dessen Directory um
  8552.                                    # (car subdir-list) verlängern:
  8553.                                    pushSTACK(Car(STACK_(1+(1+H+2)+1)));
  8554.                                   {var reg1 object pathname = pathname_add_subdir();
  8555.                                    pushSTACK(pathname);
  8556.                                   }# Diesen neuen Pathname vor new-pathname-list pushen:
  8557.                                   {var reg1 object new_cons = allocate_cons();
  8558.                                    Car(new_cons) = STACK_0;
  8559.                                    Cdr(new_cons) = STACK_(H+2+1);
  8560.                                    STACK_(H+2+1) = new_cons;
  8561.                                  }}
  8562.                          }}  }
  8563.                          #endif
  8564.                         }
  8565.                         skipSTACK(1);
  8566.                         goto next_pathname;
  8567.                         #endif
  8568.                 }   }
  8569.               # Um die Task zu erledigen, müssen alle Einträge dieses
  8570.               # Directory abgesucht werden:
  8571.               {{var reg1 object dir_namestring = assure_dir_exists(FALSE); # Links auflösen, Directory-Name bilden
  8572.                 pushSTACK(dir_namestring); # retten
  8573.                }# Stackaufbau: ..., pathname, dir_namestring.
  8574.                #if defined(UNIX) || defined(WIN32_UNIX)
  8575.                 if (!nullp(STACK_(1+5+(1+H+2)+2))) # ;CIRCLE-Flag abfragen
  8576.                   { # pathname in der Hash-Tabelle suchen:
  8577.                     pushSTACK(STACK_0); # Directory-Name
  8578.                     pushSTACK(O(punkt_string)); # und "."
  8579.                     pushSTACK(O(null_string)); # und Nullbyte
  8580.                     {var reg8 object namestring = string_concat(3); # zusammenhängen
  8581.                      var struct stat status;
  8582.                      begin_system_call();
  8583.                      if (!( stat(TheAsciz(namestring),&status) ==0)) # Information holen
  8584.                        { if (!(errno==ENOENT)) { OS_error(); }
  8585.                          end_system_call();
  8586.                          # Eintrag existiert doch nicht (das kann uns
  8587.                          # wohl nur bei symbolischen Links passieren)
  8588.                          # -> wird übergangen
  8589.                          skipSTACK(2); goto next_pathname;
  8590.                        }
  8591.                      end_system_call();
  8592.                      # Eintrag existiert (welch Wunder...)
  8593.                      pushSTACK(UL_to_I(status.st_dev)); # Device-Nummer und
  8594.                      pushSTACK(UL_to_I(status.st_ino)); # Inode-Nummer
  8595.                      {var reg1 object new_cons = allocate_cons(); # zusammenconsen
  8596.                       Cdr(new_cons) = popSTACK(); Car(new_cons) = popSTACK();
  8597.                       # und in der Hash-Tabelle aufsuchen und ablegen:
  8598.                       if (!nullp(shifthash(STACK_(2+2),new_cons,T)))
  8599.                         # war schon drin -> wird übergangen
  8600.                         { skipSTACK(2); goto next_pathname; }
  8601.                   } }}
  8602.                #endif
  8603.                 if (next_task==0)
  8604.                   # Pathname STACK_1 vor result-list pushen:
  8605.                   {var reg1 object new_cons = allocate_cons();
  8606.                    Car(new_cons) = STACK_1;
  8607.                    Cdr(new_cons) = STACK_(4+(1+H+2)+2);
  8608.                    STACK_(4+(1+H+2)+2) = new_cons;
  8609.                   }
  8610.                #if defined(UNIX) || defined(RISCOS) || defined(WIN32_UNIX)
  8611.                { var reg8 object namestring;
  8612.                  #if defined(UNIX) || defined(WIN32_UNIX)
  8613.                  pushSTACK(STACK_0); # Directory-Name
  8614.                  pushSTACK(O(punkt_string)); # und "."
  8615.                  pushSTACK(O(null_string)); # und Nullbyte
  8616.                  namestring = string_concat(3); # zusammenhängen
  8617.                  #endif
  8618.                  #ifdef RISCOS
  8619.                  var reg10 object wildcard_mask;
  8620.                  namestring = copy_string(STACK_0); # Directory-Name
  8621.                  TheSstring(namestring)->data[TheSstring(namestring)->length-1] = '\0'; # mit Nullbyte statt '.' am Schluß
  8622.                  # Statt wildcard_match() selber aufzurufen, überlassen wir das dem Betriebssystem:
  8623.                  pushSTACK(namestring); # retten
  8624.                  wildcard_mask = string_to_asciz(next_task<0 ? Car(STACK_(1+(1+H+2)+3)) : STACK_(2+(1+H+2)+3));
  8625.                  # In wildcard_mask die Wildchar-Characters '?' ins synonyme '#' umwandeln:
  8626.                  { var reg1 uintB* ptr = &TheSstring(wildcard_mask)->data[0];
  8627.                    var reg2 uintL count = TheSstring(wildcard_mask)->length;
  8628.                    dotimespL(count,count, { if (*ptr == '?') { *ptr = '#'; } ptr++; } );
  8629.                  }
  8630.                  namestring = popSTACK();
  8631.                  #endif
  8632.                  # Directory absuchen:
  8633.                 {var reg5 DIR* dirp;
  8634.                  set_break_sem_4();
  8635.                  begin_system_call();
  8636.                  #if defined(UNIX) || defined(WIN32_UNIX)
  8637.                  dirp = opendir(TheAsciz(namestring)); # Directory öffnen
  8638.                  #endif
  8639.                  #ifdef RISCOS
  8640.                  dirp = opendir(TheAsciz(namestring),TheAsciz(wildcard_mask)); # Directory zum Suchen öffnen
  8641.                  #endif
  8642.                  if (dirp == (DIR*)NULL) { OS_error(); }
  8643.                  end_system_call();
  8644.                  loop
  8645.                    { var reg2 SDIRENT* dp;
  8646.                      errno = 0;
  8647.                      begin_system_call();
  8648.                      dp = readdir(dirp); # nächsten Directory-Eintrag holen
  8649.                      end_system_call();
  8650.                      if (dp == (SDIRENT*)NULL) # Error oder Directory zu Ende
  8651.                        { if (!(errno==0
  8652.                                #ifdef WIN32_UNIX
  8653.                                ||errno==ENMFILE
  8654.                                #endif
  8655.                             ) ) { OS_error(); } else break; }
  8656.                      # Directory-Eintrag in String umwandeln:
  8657.                     {var reg4 object direntry;
  8658.                      {var reg3 uintL direntry_len;
  8659.                       #ifdef DIRENT_WITHOUT_NAMLEN
  8660.                       #ifndef DIRENT_RECLEN
  8661.                       #define DIRENT_RECLEN(dp) dp->d_reclen
  8662.                       #endif
  8663.                      
  8664.                       # Unter UNIX_LINUX reicht direntry_len := dp->d_reclen, aber i.a. ist
  8665.                       # direntry_len := min(dp->d_reclen,asciz_length(dp->d_name))  nötig:
  8666.                       {var reg1 const uintB* ptr = (const uintB*)(&dp->d_name[0]);
  8667.                        var reg1 uintL count;
  8668.                        direntry_len = 0;
  8669.                        dotimesL(count,DIRENT_RECLEN(dp),
  8670.                          { if (*ptr == '\0') break;
  8671.                            ptr++; direntry_len++;
  8672.                          });
  8673.                       }
  8674.                       #else
  8675.                       direntry_len = dp->d_namlen;
  8676.                       #endif
  8677.                       direntry = make_string((const uintB*)(&dp->d_name[0]),direntry_len);
  8678.                      }
  8679.                      #ifndef RISCOS
  8680.                      # "." und ".." übergehen:
  8681.                      if (!(equal(direntry,O(punkt_string))
  8682.                            || equal(direntry,O(punktpunkt_string))
  8683.                         ) )
  8684.                      #endif
  8685.                        { pushSTACK(direntry);
  8686.                          # Stackaufbau: ..., pathname, dir_namestring, direntry.
  8687.                          # Feststellen, ob es ein Directory oder ein File ist:
  8688.                          pushSTACK(STACK_1); # Directory-Namestring
  8689.                          pushSTACK(direntry); # direntry
  8690.                          pushSTACK(O(null_string)); # und Nullbyte
  8691.                         {var reg3 object namestring = string_concat(3); # zusammenhängen
  8692.                          # Information holen:
  8693.                          var struct stat status;
  8694.                          begin_system_call();
  8695.                          if (!( stat(TheAsciz(namestring),&status) ==0))
  8696.                            { if (!(errno==ENOENT ||errno==ELOOP_VALUE
  8697.                                    #ifdef EACCES # WIN32_UNIX, DJGPP2
  8698.                                    ||errno==EACCES
  8699.                                    #endif
  8700.                                 ) ) { OS_error(); }
  8701.                              end_system_call();
  8702.                              # Eintrag existiert doch nicht (das kann uns
  8703.                              # wohl nur bei symbolischen Links passieren)
  8704.                              # -> wird übergangen
  8705.                            }
  8706.                            else
  8707.                            { end_system_call();
  8708.                              # Eintrag existiert (welch Wunder...)
  8709.                              if (S_ISDIR(status.st_mode)) # Ist es ein Directory?
  8710.                                # Eintrag ist ein Directory.
  8711.                                { if (recursively) # alle rekursiven Subdirectories gewünscht?
  8712.                                    # ja -> zu einem Pathname machen und auf
  8713.                                    # pathnames-to-insert pushen (wird nachher
  8714.                                    # vor pathname-list-rest eingefügt):
  8715.                                    { pushSTACK(STACK_2); pushSTACK(STACK_(0+1)); # pathname und direntry
  8716.                                     {var reg1 object pathname = pathname_add_subdir();
  8717.                                      pushSTACK(pathname);
  8718.                                     }# Diesen neuen Pathname vor pathname-to-insert pushen:
  8719.                                     {var reg1 object new_cons = allocate_cons();
  8720.                                      Car(new_cons) = popSTACK();
  8721.                                      Cdr(new_cons) = STACK_(0+3);
  8722.                                      STACK_(0+3) = new_cons;
  8723.                                    }}
  8724.                                  if (next_task<0)
  8725.                                    {
  8726.                                      #ifndef RISCOS
  8727.                                      # (car subdir-list) mit direntry matchen:
  8728.                                      if (wildcard_match(Car(STACK_(1+(1+H+2)+3)),STACK_0))
  8729.                                      #endif
  8730.                                        # Subdirectory matcht -> zu einem Pathname
  8731.                                        # machen und auf new-pathname-list pushen:
  8732.                                        { pushSTACK(STACK_2); pushSTACK(STACK_(0+1)); # pathname und direntry
  8733.                                         {var reg1 object pathname = pathname_add_subdir();
  8734.                                          pushSTACK(pathname);
  8735.                                         }# Diesen neuen Pathname vor new-pathname-list pushen:
  8736.                                         {var reg1 object new_cons = allocate_cons();
  8737.                                          Car(new_cons) = popSTACK();
  8738.                                          Cdr(new_cons) = STACK_(H+2+3);
  8739.                                          STACK_(H+2+3) = new_cons;
  8740.                                    }   }}
  8741.                                }
  8742.                                else
  8743.                                # Eintrag ist ein (halbwegs) normales File.
  8744.                                { if (next_task>0)
  8745.                                    {
  8746.                                      #ifndef RISCOS
  8747.                                      # name&type mit direntry matchen:
  8748.                                      if (wildcard_match(STACK_(2+(1+H+2)+3),STACK_0))
  8749.                                      #endif
  8750.                                        # File matcht -> zu einem Pathname machen
  8751.                                        # und auf result-list pushen:
  8752.                                        {
  8753.                                          #ifndef PATHNAME_RISCOS
  8754.                                          pushSTACK(STACK_0); # direntry
  8755.                                          split_name_type(1); # in Name und Typ aufspalten
  8756.                                          {var reg1 object pathname = copy_pathname(STACK_(2+2));
  8757.                                           ThePathname(pathname)->pathname_type = popSTACK(); # Typ einsetzen
  8758.                                           ThePathname(pathname)->pathname_name = popSTACK(); # Name einsetzen
  8759.                                           pushSTACK(pathname);
  8760.                                           pushSTACK(pathname);
  8761.                                          }
  8762.                                          #else # PATHNAME_RISCOS
  8763.                                          {var reg1 object pathname = copy_pathname(STACK_2);
  8764.                                           pushSTACK(pathname);
  8765.                                           if (name_and_type && nullp(ThePathname(pathname)->pathname_type))
  8766.                                             # Move the last subdir into the type slot of the pathname.
  8767.                                             { # subdirs := (butlast subdirs) = (nreverse (cdr (reverse subdirs)))
  8768.                                               var reg1 object subdirs = reverse(ThePathname(pathname)->pathname_directory);
  8769.                                               pathname = STACK_0;
  8770.                                               ThePathname(pathname)->pathname_type = Car(subdirs);
  8771.                                               ThePathname(pathname)->pathname_directory = nreverse(Cdr(subdirs));
  8772.                                             }
  8773.                                           ThePathname(pathname)->pathname_name = STACK_1; # direntry
  8774.                                           pushSTACK(pathname);
  8775.                                          }
  8776.                                          #endif
  8777.                                          # Truename bilden (symbolische Links auflösen):
  8778.                                          assure_dir_exists(FALSE);
  8779.                                          if (file_exists(_EMA_)) # falls File (immer noch...) existiert
  8780.                                            { if (!nullp(STACK_(0+5+(1+H+2)+3+2))) # :FULL gewünscht?
  8781.                                                with_stat_info(); # ja -> STACK_0 erweitern
  8782.                                              # und STACK_0 vor result-list pushen:
  8783.                                             {var reg1 object new_cons = allocate_cons();
  8784.                                              Car(new_cons) = STACK_0;
  8785.                                              Cdr(new_cons) = STACK_(4+(1+H+2)+3+2);
  8786.                                              STACK_(4+(1+H+2)+3+2) = new_cons;
  8787.                                            }}
  8788.                                          skipSTACK(2);
  8789.                                    }   }
  8790.                            }   }
  8791.                          skipSTACK(1); # direntry vergessen
  8792.                    }}  }}
  8793.                  begin_system_call();
  8794.                  if (CLOSEDIR(dirp)) { OS_error(); }
  8795.                  end_system_call();
  8796.                  clr_break_sem_4();
  8797.                }}
  8798.                #endif
  8799.                #ifdef AMIGAOS
  8800.                 # Directory absuchen:
  8801.                { var reg7 object namestring = OSnamestring(STACK_0);
  8802.                  set_break_sem_4();
  8803.                  begin_system_call();
  8804.                 {var reg6 BPTR lock = Lock(TheAsciz(namestring),ACCESS_READ);
  8805.                  var LONGALIGNTYPE(struct FileInfoBlock) fib;
  8806.                  var reg5 struct FileInfoBlock * fibptr = LONGALIGN(&fib);
  8807.                  if (lock==BPTR_NULL) { OS_error(); }
  8808.                  if (! Examine(lock,fibptr) ) { OS_error(); }
  8809.                  end_system_call();
  8810.                  loop
  8811.                    { begin_system_call();
  8812.                      if (! ExNext(lock,fibptr) ) # Error oder Directory zu Ende?
  8813.                        break;
  8814.                      end_system_call();
  8815.                      # Directory-Eintrag in String umwandeln:
  8816.                     {var reg4 object direntry = asciz_to_string(&fibptr->fib_FileName[0]);
  8817.                      pushSTACK(direntry);
  8818.                      # Stackaufbau: ..., pathname, dir_namestring, direntry.
  8819.                      # Feststellen, ob es ein Directory oder ein File ist:
  8820.                      if ((fibptr->fib_DirEntryType > 0)
  8821.                          && (fibptr->fib_DirEntryType != ST_SOFTLINK)) # Ist es ein Directory?
  8822.                        # Eintrag ist ein Directory.
  8823.                        { if (recursively) # alle rekursiven Subdirectories gewünscht?
  8824.                            # ja -> zu einem Pathname machen und auf
  8825.                            # pathnames-to-insert pushen (wird nachher
  8826.                            # vor pathname-list-rest eingefügt):
  8827.                            { pushSTACK(STACK_2); pushSTACK(STACK_(0+1)); # pathname und direntry
  8828.                             {var reg1 object pathname = pathname_add_subdir();
  8829.                              pushSTACK(pathname);
  8830.                             }# Diesen neuen Pathname vor pathname-to-insert pushen:
  8831.                             {var reg1 object new_cons = allocate_cons();
  8832.                              Car(new_cons) = popSTACK();
  8833.                              Cdr(new_cons) = STACK_(0+3);
  8834.                              STACK_(0+3) = new_cons;
  8835.                            }}
  8836.                          if (next_task<0)
  8837.                            { # (car subdir-list) mit direntry matchen:
  8838.                              if (wildcard_match(Car(STACK_(1+(1+H+2)+3)),STACK_0))
  8839.                                # Subdirectory matcht -> zu einem Pathname
  8840.                                # machen und auf new-pathname-list pushen:
  8841.                                { pushSTACK(STACK_2); pushSTACK(STACK_(0+1)); # pathname und direntry
  8842.                                 {var reg1 object pathname = pathname_add_subdir();
  8843.                                  pushSTACK(pathname);
  8844.                                 }# Diesen neuen Pathname vor new-pathname-list pushen:
  8845.                                 {var reg1 object new_cons = allocate_cons();
  8846.                                  Car(new_cons) = popSTACK();
  8847.                                  Cdr(new_cons) = STACK_(H+2+3);
  8848.                                  STACK_(H+2+3) = new_cons;
  8849.                            }   }}
  8850.                        }
  8851.                        else
  8852.                        # Eintrag ist ein (halbwegs) normales File.
  8853.                        { if (next_task>0)
  8854.                            { # name&type mit direntry matchen:
  8855.                              if (wildcard_match(STACK_(2+(1+H+2)+3),STACK_0))
  8856.                                # File matcht -> zu einem Pathname machen
  8857.                                # und auf result-list pushen:
  8858.                                { pushSTACK(STACK_0); # direntry
  8859.                                  split_name_type(1); # in Name und Typ aufspalten
  8860.                                 {var reg1 object pathname = copy_pathname(STACK_(2+2));
  8861.                                  ThePathname(pathname)->pathname_type = popSTACK(); # Typ einsetzen
  8862.                                  ThePathname(pathname)->pathname_name = popSTACK(); # Name einsetzen
  8863.                                  pushSTACK(pathname);
  8864.                                  pushSTACK(pathname);
  8865.                                 }
  8866.                                 assure_dir_exists(FALSE); # Truename bilden (symbolische Links auflösen)
  8867.                                 { if (!nullp(STACK_(0+5+(1+H+2)+3+2))) # :FULL gewünscht?
  8868.                                     with_stat_info(); # ja -> STACK_0 erweitern
  8869.                                   # und STACK_0 vor result-list pushen:
  8870.                                  {var reg1 object new_cons = allocate_cons();
  8871.                                   Car(new_cons) = STACK_0;
  8872.                                   Cdr(new_cons) = STACK_(4+(1+H+2)+3+2);
  8873.                                   STACK_(4+(1+H+2)+3+2) = new_cons;
  8874.                                 }}
  8875.                                 skipSTACK(2);
  8876.                        }   }   }
  8877.                      skipSTACK(1); # direntry vergessen
  8878.                    }}
  8879.                  UnLock(lock);
  8880.                  if (!(IoErr()==ERROR_NO_MORE_ENTRIES)) { OS_error(); }
  8881.                  end_system_call();
  8882.                  clr_break_sem_4();
  8883.                }}
  8884.                #endif
  8885.                #if defined(MSDOS) && !defined(WIN32_DOS)
  8886.                 pushSTACK(STACK_0); # Directory-Name
  8887.                 pushSTACK(O(wild_wild_string)); # und "*.*"
  8888.                 pushSTACK(O(null_string)); # und Nullbyte
  8889.                {var reg8 object namestring = string_concat(3); # zusammenhängen
  8890.                 # Directory absuchen, gemäß DOS-Konvention:
  8891.                 var struct ffblk DTA_buffer;
  8892.                 set_break_sem_4(); # wegen DTA-Buffer gegen Unterbrechungen sperren
  8893.                 # Suchanfang, suche nach Ordnern und normalen Dateien:
  8894.                 begin_system_call();
  8895.                 if (findfirst(TheAsciz(namestring),&DTA_buffer,FA_DIREC|FA_ARCH|FA_RDONLY) <0)
  8896.                   { if (!((errno==ENOENT) 
  8897.                        || (errno==ENOMORE)
  8898.                        ) ) { OS_error(); } }
  8899.                   else # Keine Datei gefunden -> Schleife nicht durchlaufen
  8900.                   loop
  8901.                     { end_system_call();
  8902.                      {# Directory-Eintrag in String umwandeln:
  8903.                       var reg4 object direntry = asciz_to_string(&DTA_buffer.ff_name[0]);
  8904.                       # "." und ".." übergehen:
  8905.                       if (!(equal(direntry,O(punkt_string))
  8906.                             || equal(direntry,O(punktpunkt_string))
  8907.                          ) )
  8908.                         { pushSTACK(direntry);
  8909.                           # Stackaufbau: ..., pathname, dir_namestring, direntry.
  8910.                           if (DTA_buffer.ff_attrib & FA_DIREC) # Ist es ein Directory?
  8911.                             # Eintrag ist ein Directory.
  8912.                             { if (recursively) # alle rekursiven Subdirectories gewünscht?
  8913.                                 # ja -> zu einem Pathname machen und auf
  8914.                                 # pathnames-to-insert pushen (wird nachher
  8915.                                 # vor pathname-list-rest eingefügt):
  8916.                                 { pushSTACK(STACK_2); pushSTACK(STACK_(0+1)); # pathname und direntry
  8917.                                  {var reg1 object pathname = pathname_add_subdir();
  8918.                                   pushSTACK(pathname);
  8919.                                  }# Diesen neuen Pathname vor pathname-to-insert pushen:
  8920.                                  {var reg1 object new_cons = allocate_cons();
  8921.                                   Car(new_cons) = popSTACK();
  8922.                                   Cdr(new_cons) = STACK_(0+3);
  8923.                                   STACK_(0+3) = new_cons;
  8924.                                 }}
  8925.                               if (next_task<0)
  8926.                                 { # (car subdir-list) mit direntry matchen:
  8927.                                   if (wildcard_match(Car(STACK_(1+(1+H+2)+3)),STACK_0))
  8928.                                     # Subdirectory matcht -> zu einem Pathname
  8929.                                     # machen und auf new-pathname-list pushen:
  8930.                                     { pushSTACK(STACK_2); pushSTACK(STACK_(0+1)); # pathname und direntry
  8931.                                      {var reg1 object pathname = pathname_add_subdir();
  8932.                                       pushSTACK(pathname);
  8933.                                      }# Diesen neuen Pathname vor new-pathname-list pushen:
  8934.                                      {var reg1 object new_cons = allocate_cons();
  8935.                                       Car(new_cons) = popSTACK();
  8936.                                       Cdr(new_cons) = STACK_(H+2+3);
  8937.                                       STACK_(H+2+3) = new_cons;
  8938.                                 }   }}
  8939.                             }
  8940.                             else
  8941.                             # Eintrag ist ein (halbwegs) normales File.
  8942.                             { if (next_task>0)
  8943.                                 { # name&type mit direntry matchen:
  8944.                                   if (wildcard_match(STACK_(2+(1+H+2)+3),STACK_0))
  8945.                                     # File matcht -> zu einem Pathname machen
  8946.                                     # und auf result-list pushen:
  8947.                                     { pushSTACK(STACK_0); # direntry
  8948.                                       split_name_type(1); # in Name und Typ aufspalten
  8949.                                      {var reg1 object new = copy_pathname(STACK_(2+2));
  8950.                                       ThePathname(new)->pathname_type = popSTACK(); # Typ einsetzen
  8951.                                       ThePathname(new)->pathname_name = popSTACK(); # Name einsetzen
  8952.                                       # Full-Flag abtesten und evtl. mehr Information besorgen:
  8953.                                       if (!nullp(STACK_(0+5+(1+H+2)+3))) # :FULL gewünscht?
  8954.                                         { pushSTACK(new); # newpathname als 1. Listenelement
  8955.                                           pushSTACK(new); # newpathname als 2. Listenelement
  8956.                                           { # Uhrzeit und Datum von DOS-Format in Decoded-Time umwandeln:
  8957.                                             var decoded_time timepoint;
  8958.                                             convert_timedate((uintW)DTA_buffer.ff_ftime,(uintW)DTA_buffer.ff_fdate,
  8959.                                                              &timepoint);
  8960.                                             pushSTACK(timepoint.Sekunden);
  8961.                                             pushSTACK(timepoint.Minuten);
  8962.                                             pushSTACK(timepoint.Stunden);
  8963.                                             pushSTACK(timepoint.Tag);
  8964.                                             pushSTACK(timepoint.Monat);
  8965.                                             pushSTACK(timepoint.Jahr);
  8966.                                             new = listof(6); # 6-elementige Liste bauen
  8967.                                           }
  8968.                                           pushSTACK(new); # als 3. Listenelement
  8969.                                           pushSTACK(UL_to_I(*(uintL*)(&DTA_buffer.ff_fsize))); # Länge als 4. Listenelement
  8970.                                           new = listof(4); # 4-elementige Liste bauen
  8971.                                         }
  8972.                                       pushSTACK(new);
  8973.                                      }# und STACK_0 vor result-list pushen:
  8974.                                      {var reg1 object new_cons = allocate_cons();
  8975.                                       Car(new_cons) = popSTACK();
  8976.                                       Cdr(new_cons) = STACK_(4+(1+H+2)+3);
  8977.                                       STACK_(4+(1+H+2)+3) = new_cons;
  8978.                             }   }   }}
  8979.                           skipSTACK(1); # direntry vergessen
  8980.                         }
  8981.                       # nächstes File:
  8982.                       begin_system_call();
  8983.                       if (findnext(&DTA_buffer) <0)
  8984.                         { if (!((errno==ENOENT) 
  8985.                              || (errno==ENOMORE)
  8986.                              ) ) { OS_error(); }
  8987.                           break; # Keine weitere Datei -> Schleifenende
  8988.                         }
  8989.                     }}
  8990.                 end_system_call();
  8991.                 clr_break_sem_4();
  8992.                }
  8993.                #elif defined(WIN32_DOS)
  8994.                 pushSTACK(STACK_0); # Directory-Name
  8995.                 pushSTACK(O(wild_wild_string)); # und "*.*"
  8996.                 pushSTACK(O(null_string)); # und Nullbyte
  8997.                {var reg8 object namestring = string_concat(3); # zusammenhängen
  8998.                 var WIN32_FIND_DATA ffd;
  8999.                 var HANDLE hFindFile;
  9000.                 set_break_sem_4(); # wegen DTA-Buffer gegen Unterbrechungen sperren
  9001.                 # Suchanfang, suche nach Ordnern und normalen Dateien:
  9002.                 begin_system_call();
  9003.                 if ((hFindFile=FindFirstFile(TheAsciz(namestring),&ffd)) == INVALID_HANDLE_VALUE)
  9004.                   { OS_error(); }
  9005.                  else # Keine Datei gefunden -> Schleife nicht durchlaufen
  9006.                  loop
  9007.                    { # Stackaufbau: ..., next-pathname.
  9008.                      end_system_call();
  9009.                      {# Directory-Eintrag in String umwandeln:
  9010.                       var reg4 object direntry = asciz_to_string(&ffd.cFileName[0]);
  9011.                       # "." und ".." übergehen:
  9012.                       if (!(equal(direntry,O(punkt_string))
  9013.                             || equal(direntry,O(punktpunkt_string))
  9014.                          ) )
  9015.                         { pushSTACK(direntry);
  9016.                           # Stackaufbau: ..., pathname, dir_namestring, direntry.
  9017.                           if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) # Ist es ein Directory?
  9018.                             # Eintrag ist ein Directory.
  9019.                             { if (recursively) # alle rekursiven Subdirectories gewünscht?
  9020.                                 # ja -> zu einem Pathname machen und auf
  9021.                                 # pathnames-to-insert pushen (wird nachher
  9022.                                 # vor pathname-list-rest eingefügt):
  9023.                                 { pushSTACK(STACK_2); pushSTACK(STACK_(0+1)); # pathname und direntry
  9024.                                  {var reg1 object pathname = pathname_add_subdir();
  9025.                                   pushSTACK(pathname);
  9026.                                  }# Diesen neuen Pathname vor pathname-to-insert pushen:
  9027.                                  {var reg1 object new_cons = allocate_cons();
  9028.                                   Car(new_cons) = popSTACK();
  9029.                                   Cdr(new_cons) = STACK_(0+3);
  9030.                                   STACK_(0+3) = new_cons;
  9031.                                 }}
  9032.                               if (next_task<0)
  9033.                                 { # (car subdir-list) mit direntry matchen:
  9034.                                   if (wildcard_match(Car(STACK_(1+(1+H+2)+3)),STACK_0))
  9035.                                     # Subdirectory matcht -> zu einem Pathname
  9036.                                     # machen und auf new-pathname-list pushen:
  9037.                                     { pushSTACK(STACK_2); pushSTACK(STACK_(0+1)); # pathname und direntry
  9038.                                      {var reg1 object pathname = pathname_add_subdir();
  9039.                                       pushSTACK(pathname);
  9040.                                      }# Diesen neuen Pathname vor new-pathname-list pushen:
  9041.                                      {var reg1 object new_cons = allocate_cons();
  9042.                                       Car(new_cons) = popSTACK();
  9043.                                       Cdr(new_cons) = STACK_(H+2+3);
  9044.                                       STACK_(H+2+3) = new_cons;
  9045.                                 }   }}
  9046.                             }
  9047.                             else
  9048.                             # Eintrag ist ein (halbwegs) normales File.
  9049.                             { if (next_task>0)
  9050.                                 { # name&type mit direntry matchen:
  9051.                                   if (wildcard_match(STACK_(2+(1+H+2)+3),STACK_0))
  9052.                                     # File matcht -> zu einem Pathname machen
  9053.                                     # und auf result-list pushen:
  9054.                                     { pushSTACK(STACK_0); # direntry
  9055.                                       split_name_type(1); # in Name und Typ aufspalten
  9056.                                      {var reg1 object new = copy_pathname(STACK_(2+2));
  9057.                                       ThePathname(new)->pathname_type = popSTACK(); # Typ einsetzen
  9058.                                       ThePathname(new)->pathname_name = popSTACK(); # Name einsetzen
  9059.                                       # Full-Flag abtesten und evtl. mehr Information besorgen:
  9060.                                       if (!nullp(STACK_(0+5+(1+H+2)+3))) # :FULL gewünscht?
  9061.                                         { pushSTACK(new); # newpathname als 1. Listenelement
  9062.                                           pushSTACK(new); # newpathname als 2. Listenelement
  9063.                                           { # Uhrzeit und Datum von DOS-Format in Decoded-Time umwandeln:
  9064.                                             var decoded_time timepoint;
  9065.                                             var WORD date;
  9066.                                             var WORD time;
  9067.                                             FileTimeToDosDateTime(&ffd.ftLastWriteTime,&date,&time);
  9068.                                             convert_timedate((uintW)time,(uintW)date,&timepoint);
  9069.                                             pushSTACK(timepoint.Sekunden);
  9070.                                             pushSTACK(timepoint.Minuten);
  9071.                                             pushSTACK(timepoint.Stunden);
  9072.                                             pushSTACK(timepoint.Tag);
  9073.                                             pushSTACK(timepoint.Monat);
  9074.                                             pushSTACK(timepoint.Jahr);
  9075.                                             new = listof(6); # 6-elementige Liste bauen
  9076.                                           }
  9077.                                           pushSTACK(new); # als 3. Listenelement
  9078.                                           pushSTACK(UL_to_I(*(uintL*)(&ffd.nFileSizeLow))); # Länge als 4. Listenelement
  9079.                                           new = listof(4); # 4-elementige Liste bauen
  9080.                                         }
  9081.                                       pushSTACK(new);
  9082.                                      }# und STACK_0 vor result-list pushen:
  9083.                                      {var reg1 object new_cons = allocate_cons();
  9084.                                       Car(new_cons) = popSTACK();
  9085.                                       Cdr(new_cons) = STACK_(4+(1+H+2)+3);
  9086.                                       STACK_(4+(1+H+2)+3) = new_cons;
  9087.                             }   }   }}
  9088.                           skipSTACK(1); # direntry vergessen
  9089.                         }
  9090.                       # nächstes File:
  9091.                       begin_system_call();
  9092.                       if (FindNextFile(hFindFile,&ffd) == FALSE)
  9093.                         { if (GetLastError() != ERROR_NO_MORE_FILES) 
  9094.                             { OS_error(); }
  9095.                           break; # Keine weitere Datei -> Schleifenende
  9096.                         }
  9097.                     }}
  9098.                 end_system_call();
  9099.                 clr_break_sem_4();
  9100.                }
  9101.                #endif
  9102.               }
  9103.               skipSTACK(2); # pathname und dir-namestring vergessen
  9104.               next_pathname: ;
  9105.              }# Stackaufbau: ..., new-pathname-list, [ht,] pathname-list-rest, pathnames-to-insert.
  9106.               # Vor dem Weiterrücken mit pathname-list-rest :
  9107.               # pathname-list-rest := (nreconc pathnames-to-insert pathname-list-rest) :
  9108.              {var reg1 object pathnames_to_insert = popSTACK();
  9109.               STACK_0 = nreconc(pathnames_to_insert,STACK_0);
  9110.             }}
  9111.           skipSTACK(H+1); # leere pathname-list-rest und evtl. Hash-Tabelle vergessen
  9112.           #undef H
  9113.           # new-pathname-list umdrehen, ersetzt die geleerte pathname-list:
  9114.           {var reg1 object new_pathname_list = popSTACK();
  9115.            STACK_0 = nreverse(new_pathname_list); # neue pathname-list
  9116.           }
  9117.           # Mit dieser Subdir-Stufe sind wir fertig.
  9118.           if (matomp(STACK_1)) break; # (atom subdir-list) -> fertig.
  9119.           recursively = FALSE; # die nächste (vorläufig) nicht-rekursiv
  9120.           passed_subdir: ;
  9121.         }}
  9122.       # Stackaufbau: result-list, pathname, name&type, subdir-list, pathname-list.
  9123.       # subdir-list ist =NIL geworden, auch pathname-list = NIL (denn beim
  9124.       # letzten Schleifendurchlauf ist immer next_task=0,1,2, und dadurch
  9125.       # wurde nichts auf new-pathname-list gepusht).
  9126.       skipSTACK(4);
  9127.       return popSTACK(); # result-list als Ergebnis
  9128.     }}
  9129.   #
  9130.   #endif # PATHNAME_NOEXT
  9131.  
  9132. LISPFUN(directory,0,1,norest,key,2, (kw(circle),kw(full)) )
  9133. # (DIRECTORY [pathname [:circle] [:full]]), CLTL S. 427
  9134.   { # Stackaufbau: pathname, circle, full.
  9135.     #if defined(UNIX) || defined(WIN32_UNIX)
  9136.     # :CIRCLE-Argument hat Defaultwert NIL:
  9137.     if (eq(STACK_1,unbound)) { STACK_1 = NIL; }
  9138.     #endif
  9139.     # :FULL-Argument hat Defaultwert NIL:
  9140.     if (eq(STACK_0,unbound)) { STACK_0 = NIL; }
  9141.     # Pathname-Argument überprüfen:
  9142.    {var reg1 object pathname = STACK_2;
  9143.     if (eq(pathname,unbound))
  9144.       {
  9145.         #ifdef PATHNAME_EXT83
  9146.         pathname = O(wild_wild_string); # Default ist "*.*" bzw. "*.*;*"
  9147.         #endif
  9148.         #if defined(PATHNAME_NOEXT) || defined(PATHNAME_RISCOS)
  9149.         pathname = O(wild_string); # Default ist "*"
  9150.         #endif
  9151.       }
  9152.     pathname = coerce_pathname(pathname); # zu einem Pathname machen
  9153.     # Los geht's:
  9154.     #if defined(PATHNAME_MSDOS) || defined(PATHNAME_OS2)
  9155.     if (eq(ThePathname(pathname)->pathname_device,S(Kwild))) # Device = :WILD ?
  9156.       # alle Devices abzusuchen
  9157.       { STACK_2 = pathname;
  9158.         pushSTACK(NIL); # bisherige Pathname-Liste := NIL
  9159.         pushSTACK(STACK_(0+1)); # full (für directory_search)
  9160.         # Stackaufbau: pathname, circle, full, pathname-list, full.
  9161.         {var uintB drive;
  9162.          for (drive='A'; drive<='Z'; drive++) # alle Drives durchlaufen
  9163.            if (good_drive(drive))
  9164.              { pushSTACK(make_string(&drive,1)); # Device, einelementiger String
  9165.               {var reg2 object newpathname = copy_pathname(STACK_(2+2+1)); # Pathname kopieren
  9166.                ThePathname(newpathname)->pathname_device = popSTACK(); # Drive übernehmen
  9167.                # innerhalb eines Laufwerks suchen:
  9168.                {var reg3 object newpathnames = directory_search(newpathname);
  9169.                 # und Pathname-Liste vor STACK_1 hängen:
  9170.                 STACK_1 = nreconc(newpathnames,STACK_1);
  9171.         }    }}}
  9172.         value1 = nreverse(STACK_1); # Pathname-Liste wieder umdrehen
  9173.         skipSTACK(3+2);
  9174.       }
  9175.       else
  9176.       # nur ein Device abzusuchen
  9177.     #endif
  9178.       { value1 = directory_search(pathname); # matchende Pathnames bilden
  9179.         skipSTACK(3);
  9180.       }
  9181.     mv_count=1;
  9182.   }}
  9183.  
  9184. LISPFUN(cd,0,1,norest,nokey,0,NIL)
  9185. # (CD [pathname]) setzt das aktuelle Laufwerk und das aktuelle Directory.
  9186.   { var reg1 object pathname = popSTACK();
  9187.     if (eq(pathname,unbound)) { pathname = O(leer_string); } # "" als Default
  9188.     pathname = coerce_pathname(pathname); # zu einem Pathname machen
  9189.     # kopieren und Name und Typ auf NIL setzen:
  9190.     pathname = copy_pathname(pathname);
  9191.     ThePathname(pathname)->pathname_name = NIL;
  9192.     ThePathname(pathname)->pathname_type = NIL;
  9193.     check_no_wildcards(pathname); # mit Wildcards -> Fehler
  9194.     pathname = use_default_dir(pathname); # Pathname mit Seriennummer draus machen
  9195.     pushSTACK(pathname);
  9196.     assure_dir_exists(FALSE); # Directory muß existieren
  9197.     change_default(); # Default-Drive, Default-Directory setzen
  9198.     value1 = popSTACK(); mv_count=1; # neuer pathname als Wert
  9199.   }
  9200.  
  9201. # UP: Überprüft ein Pathname-Argument, ob Name und Typ beide =NIL sind,
  9202. # und ob das Directory "fast" existiert.
  9203. # shorter_directory_arg()
  9204. # > STACK_0 : Pathname-Argument
  9205. #if defined(MSDOS)
  9206. # < ergebnis: Directory-Namestring (für DOS, ASCIZ, ohne '\' am Schluß)
  9207. #endif
  9208. #if defined(UNIX) || defined(AMIGAOS) || defined(WIN32_UNIX)
  9209. # < ergebnis: Directory-Namestring (fürs OS, ASCIZ, ohne '/' am Schluß)
  9210. #endif
  9211. #if defined(RISCOS)
  9212. # < ergebnis: Directory-Namestring (fürs OS, ASCIZ, ohne '.' am Schluß)
  9213. #endif
  9214. # Erhöht STACK um 1.
  9215. # kann GC auslösen
  9216.   local object shorter_directory_arg (void);
  9217.   local object shorter_directory_arg()
  9218.     { var reg1 object pathname = coerce_pathname(popSTACK()); # Argument zu einem Pathname machen
  9219.       check_no_wildcards(pathname); # mit Wildcards -> Fehler
  9220.       pathname = use_default_dir(pathname); # Default-Directory einfügen
  9221.       # Überprüfe, ob Name=NIL und Typ=NIL :
  9222.       if (!(nullp(ThePathname(pathname)->pathname_name)
  9223.             && nullp(ThePathname(pathname)->pathname_type)
  9224.          ) )
  9225.         { pushSTACK(pathname); # Wert für Slot PATHNAME von FILE-ERROR
  9226.           pushSTACK(pathname);
  9227.           //: DEUTSCH "Das ist keine Directory-Angabe: ~"
  9228.           //: ENGLISH "not a directory: ~"
  9229.           //: FRANCAIS "Ceci ne désigne pas un répertoire : ~"
  9230.           fehler(file_error, GETTEXT("not a directory: ~"));
  9231.         }
  9232.       pushSTACK(pathname); # neuen Pathname retten
  9233.       # verkürze das Directory:
  9234.       {var reg2 object subdirs = ThePathname(pathname)->pathname_directory;
  9235.        if (nullp(Cdr(subdirs))) # Root-Directory ?
  9236.          { baddir:
  9237.            # STACK_0 = pathname, Wert für Slot PATHNAME von FILE-ERROR
  9238.            pushSTACK(STACK_0);
  9239.            //: DEUTSCH "Hier sind nur echte Unterdirectories zulässig, nicht ~"
  9240.            //: ENGLISH "root directory not allowed here: ~"
  9241.            //: FRANCAIS "Le répertoire racine n'est pas permis ici : ~"
  9242.            fehler(file_error, GETTEXT("root directory not allowed here: ~"));
  9243.          }
  9244.        subdirs = reverse(subdirs); # Liste kopieren und dabei umdrehen
  9245.        #if defined(PATHNAME_AMIGAOS) || defined(PATHNAME_RISCOS)
  9246.        if (eq(Car(subdirs),S(Kparent))) # letztes Subdir muß /= :PARENT sein
  9247.          goto baddir;
  9248.        #endif
  9249.        pushSTACK(subdirs); # Cons mit letztem Subdir als CAR retten
  9250.        subdirs = Cdr(subdirs); # alle Subdirs bis aufs letzte
  9251.        subdirs = nreverse(subdirs); # wieder in die richtige Reihenfolge bringen
  9252.        pathname = STACK_1;
  9253.        ThePathname(pathname)->pathname_directory = subdirs; # und in den Pathname setzen
  9254.        # Dieses Directory muß existieren:
  9255.        pushSTACK(pathname);
  9256.        # Stackaufbau: pathname, subdircons, pathname.
  9257.        {var reg3 object dir_namestring = assure_dir_exists(FALSE);
  9258.         # Baue ASCIZ-String des Subdir für OS:
  9259.         STACK_0 = dir_namestring; # bisheriger Directory-Namestring als 1. String
  9260.         {var reg4 uintC stringcount =
  9261.            subdir_namestring_parts(STACK_1); # und Strings zum letzten Subdir
  9262.          # und kein '\' am Schluß (für DOS)
  9263.          # und kein '/' am Schluß (fürs OS)
  9264.          pushSTACK(O(null_string)); # und Nullbyte als letzten String
  9265.          {var reg5 object dirstring = string_concat(1+stringcount+1); # zusammenhängen
  9266.           skipSTACK(2);
  9267.           return dirstring;
  9268.     } }}}}
  9269.  
  9270. LISPFUNN(make_dir,1)
  9271. # (MAKE-DIR pathname) legt ein neues Unterdirectory pathname an.
  9272.   { var reg1 object pathstring = shorter_directory_arg();
  9273.     #ifdef AMIGAOS
  9274.     set_break_sem_4();
  9275.     begin_system_call();
  9276.     {var reg2 BPTR lock = CreateDir(TheAsciz(pathstring)); # Unterdirectory erzeugen
  9277.      if (lock==BPTR_NULL) { OS_error(); }
  9278.      UnLock(lock); # Lock freigeben
  9279.     }
  9280.     end_system_call();
  9281.     clr_break_sem_4();
  9282.     #endif
  9283.     #if defined(UNIX) || defined(DJUNIX) || defined(EMUNIX) || defined(WATCOM) || defined(RISCOS) || defined(WIN32_DOS) || defined(WIN32_UNIX)
  9284.     begin_system_call();
  9285.     if (mkdir(TheAsciz(pathstring),0777)) # Unterdirectory erzeugen
  9286.       { OS_error(); }
  9287.     end_system_call();
  9288.     #endif
  9289.     value1 = T; mv_count=1; # 1 Wert T
  9290.   }
  9291.  
  9292. LISPFUNN(delete_dir,1)
  9293. # (DELETE-DIR pathname) entfernt das Unterdirectory pathname.
  9294.   { var reg1 object pathstring = shorter_directory_arg();
  9295.     #ifdef AMIGAOS
  9296.     # Noch Test, ob's auch ein Directory und kein File ist??
  9297.     begin_system_call();
  9298.     if (! DeleteFile(TheAsciz(pathstring)) ) # Unterdirectory löschen
  9299.       { OS_error(); }
  9300.     end_system_call();
  9301.     #endif
  9302.     #if defined(UNIX) || defined(DJUNIX) || defined(EMUNIX) || defined(WATCOM) || defined(WIN32_DOS) || defined(WIN32_UNIX)
  9303.     begin_system_call();
  9304.     if (rmdir(TheAsciz(pathstring))) # Unterdirectory löschen
  9305.       { OS_error(); }
  9306.     end_system_call();
  9307.     #endif
  9308.     #ifdef RISCOS
  9309.     begin_system_call();
  9310.     if (unlink(TheAsciz(pathstring))) # Unterdirectory löschen
  9311.       { OS_error(); }
  9312.     end_system_call();
  9313.     #endif
  9314.     value1 = T; mv_count=1; # 1 Wert T
  9315.   }
  9316.  
  9317. # UP: Initialisiert das Pathname-System.
  9318. # init_pathnames();
  9319. # kann GC auslösen
  9320.   global void init_pathnames (void);
  9321.   global void init_pathnames()
  9322.     {
  9323.       #if defined(PATHNAME_MSDOS) || defined(PATHNAME_OS2)
  9324.       { # Default-Drive initialisieren:
  9325.         var uintB drive = default_drive();
  9326.         O(default_drive) = make_string(&drive,1);
  9327.       }
  9328.       #endif
  9329.       # *DEFAULT-PATHNAME-DEFAULTS* initialisieren:
  9330.       recalc_defaults_pathname();
  9331.       #ifdef USER_HOMEDIR
  9332.       #if defined(UNIX) || defined(WIN32_UNIX)
  9333.       # Wir ziehen uns das Home-Directory und die benutzbare Shell aus dem
  9334.       # Environment. Es enthält (fast) immer mindestens folgende Variablen:
  9335.       #   LOGNAME = Username beim ersten Einloggen ("wahre" Identität des Benutzers)
  9336.       #   USER    = aktueller Username
  9337.       #   HOME    = aktuelles Home-Directory, aus /etc/passwd geholt
  9338.       #   SHELL   = aktuelle Standard-Shell, aus /etc/passwd geholt
  9339.       #   PATH    = Suchpfad bei Programmaufruf
  9340.       #   TERM    = Terminalemulation
  9341.       # Wir holen uns HOME (für "~" - Übersetzung) und SHELL (für EXECUTE).
  9342.       # Bei "~username" müssen wir das /etc/passwd - File absuchen.
  9343.       { # Im Environment nach Variable HOME suchen:
  9344.         begin_system_call();
  9345.        {var reg1 const char* homedir = getenv("HOME");
  9346.         end_system_call();
  9347.         if (!(homedir==NULL)) # gefunden?
  9348.           { O(user_homedir) = asciz_dir_to_pathname(homedir); } # ja -> eintragen
  9349.           else
  9350.           # nein -> Home-Directory aus dem Passwort-File holen:
  9351.           { # empfohlene Methode (siehe GETLOGIN(3V)): erst
  9352.             # getpwnam(getlogin()), dann getpwuid(getuid()) probieren.
  9353.             var reg2 const char* username;
  9354.             var reg1 struct passwd * userpasswd;
  9355.             begin_system_call();
  9356.             # 1. Versuch: getpwnam(getenv("USER"))
  9357.             username = getenv("USER"); # Username aus dem Environment holen
  9358.             if (!(username==NULL))
  9359.               { errno = 0; userpasswd = getpwnam(username); # passwd-Eintrag dazu
  9360.                 if (!(userpasswd==NULL)) goto userpasswd_ok; # gefunden -> ok
  9361.                 if (!(errno==0)) { OS_error(); } # Error melden
  9362.               }
  9363.             # 2. Versuch: getpwnam(getlogin())
  9364.             errno = 0; username = getlogin(); # Username aus /etc/utmp holen
  9365.             if (username==NULL)
  9366.               { if (!(errno==0)) { OS_error(); } } # Error melden
  9367.               else
  9368.               { errno = 0; userpasswd = getpwnam(username); # passwd-Eintrag dazu
  9369.                 if (!(userpasswd==NULL)) goto userpasswd_ok; # gefunden -> ok
  9370.                 if (!(errno==0)) { OS_error(); } # Error melden
  9371.               }
  9372.             # 3. Versuch: getpwuid(getuid())
  9373.             errno = 0; userpasswd = getpwuid(user_uid);
  9374.             if (!(userpasswd==NULL)) # gefunden?
  9375.               { userpasswd_ok:
  9376.                 end_system_call();
  9377.                 O(user_homedir) = asciz_dir_to_pathname(userpasswd->pw_dir); # ja -> Homedir als Pathname eintragen
  9378.               }
  9379.               else
  9380.               { if (!(errno==0)) { OS_error(); } # Error melden
  9381.                 end_system_call();
  9382.                 # nein -> aktuelles Directory nehmen:
  9383.                 O(user_homedir) = default_directory();
  9384.       }}  }   }
  9385.       #endif
  9386.       #endif
  9387.       #if defined(HAVE_SHELL) && !defined(AMIGAOS)
  9388.       #if defined(UNIX) || defined(WIN32_UNIX)
  9389.       # Die Kommando-Shell O(command_shell) bleibt unverändert, sonst
  9390.       # handelt man sich zu viele Portabilitätsprobleme ein.
  9391.       { # Im Environment nach Variable SHELL suchen:
  9392.         begin_system_call();
  9393.        {var reg1 const char* shell = getenv("SHELL");
  9394.         end_system_call();
  9395.         if (!(shell==NULL)) # gefunden?
  9396.           { O(user_shell) = asciz_to_string(shell); } # ja -> eintragen
  9397.           # sonst bleibt O(user_shell) auf dem Defaultwert "/bin/csh".
  9398.       }}
  9399.       #endif
  9400.       #ifdef MSDOS
  9401.       { # Im Environment nach Variable COMSPEC suchen:
  9402.         begin_system_call();
  9403.        {var reg1 const char* shell = getenv("COMSPEC");
  9404.         end_system_call();
  9405.         if (!(shell==NULL)) # gefunden?
  9406.           { O(command_shell) = asciz_to_string(shell); } # ja -> eintragen
  9407.           # sonst bleibt O(command_shell) auf dem Defaultwert "\\COMMAND.COM".
  9408.       }}
  9409.       #endif
  9410.       #endif
  9411.     }
  9412.  
  9413. #if defined(DJUNIX) || defined(WATCOM) || defined(EMUNIX_OLD_8d)
  9414. # UP: Legt Datum/Uhrzeit der Datei mit dem Handle handle im 4-Byte-Buffer ab.
  9415. # get_file_write_datetime(handle);
  9416. # > handle: Handle eines (offenen) Files
  9417. # < file_datetime: Datum und Uhrzeit der Datei
  9418.   local var struct { uintW time; uintW date; } file_datetime; # Buffer fürs Ergebnis
  9419.   local void get_file_write_datetime (uintW handle);
  9420.   #if defined(DJUNIX) || defined(WATCOM)
  9421.   #include <dos.h>
  9422.   local void get_file_write_datetime(handle)
  9423.     var reg1 uintW handle;
  9424.     {
  9425.      #ifndef GNU
  9426.       var union REGS in;
  9427.       var union REGS out;
  9428.       in.regB.ah = 0x57; in.regB.al = 0; # DOS Function 57H
  9429.       in.regW.bx = handle;
  9430.       intdos(&in,&out);
  9431.       file_datetime.time = out.regW.cx;
  9432.       file_datetime.date = out.regW.dx;
  9433.      #else # dasselbe, nur effizienter
  9434.       var uintW time;
  9435.       var uintW date;
  9436.       __asm__ (# DOS Function 57H
  9437.                " movw $0x5700,%%ax ; int $0x21 "
  9438.                : "=c" /* %cx */ (time), "=d" /* %dx */ (date)     # OUT
  9439.                :                                                  # IN
  9440.                : "ax","bx","si","di" /* %eax, %ebx, %esi, %edi */ # CLOBBER
  9441.               );
  9442.       file_datetime.time = time;
  9443.       file_datetime.date = date;
  9444.      #endif
  9445.     }
  9446.   #endif
  9447.   #ifdef EMUNIX_OLD_8d
  9448.   extern int __filetime ( /* int handle, int flag, struct _ftd * */ );
  9449.   #define get_file_write_datetime(handle)  __filetime(handle,0,&file_datetime)
  9450.   #endif
  9451. #endif
  9452. #ifdef AMIGAOS
  9453.   local var struct DateStamp file_datetime; # Buffer für Datum/Uhrzeit einer Datei
  9454. #endif
  9455. #if defined(UNIX) || defined(EMUNIX_NEW_8e) || defined(RISCOS) || defined(WIN32_DOS) || defined(WIN32_UNIX)
  9456.   local var time_t file_datetime; # Buffer für Datum/Uhrzeit einer Datei
  9457. #endif
  9458.  
  9459. LISPFUNN(file_write_date,1)
  9460. # (FILE-WRITE-DATE file), CLTL S. 424
  9461.   { var reg1 object pathname = popSTACK(); # pathname-Argument
  9462.     if (streamp(pathname))
  9463.       # Stream -> extra behandeln:
  9464.       { # muß File-Stream sein:
  9465.         pathname = as_file_stream(pathname);
  9466.         # Streamtyp File-Stream
  9467.        #if !defined(AMIGAOS)
  9468.         if ((TheStream(pathname)->strmflags & strmflags_open_B)
  9469.             && (!nullp(TheStream(pathname)->strm_file_handle))
  9470.            )
  9471.           # offener File-Stream
  9472.           { # direkt mit dem Handle arbeiten:
  9473.             #if defined(DJUNIX) || defined(WATCOM) || defined(EMUNIX_OLD_8d)
  9474.             begin_system_call();
  9475.             get_file_write_datetime(TheHandle(TheStream(pathname)->strm_file_handle));
  9476.             end_system_call();
  9477.             #endif
  9478.             #if defined(UNIX) || defined(EMUNIX_NEW_8e) || defined(RISCOS) || defined(WIN32_DOS) || defined(WIN32_UNIX)
  9479.             var struct stat status;
  9480.             begin_system_call();
  9481.             if (!( fstat(TheHandle(TheStream(pathname)->strm_file_handle),&status) ==0))
  9482.               { OS_error(); }
  9483.             end_system_call();
  9484.             file_datetime = status.st_mtime;
  9485.             #endif
  9486.           }
  9487.           else
  9488.        #endif
  9489.           # geschlossener File-Stream -> Truename als Pathname verwenden
  9490.           { pathname = TheStream(pathname)->strm_file_truename;
  9491.             goto is_pathname;
  9492.           }
  9493.       }
  9494.       else
  9495.       { pathname = coerce_pathname(pathname); # zu einem Pathname machen
  9496.         is_pathname: # pathname ist jetzt wirklich ein Pathname
  9497.         check_no_wildcards(pathname); # mit Wildcards -> Fehler
  9498.         pathname = use_default_dir(pathname); # Default-Directory einfügen
  9499.         if (namenullp(pathname)) { fehler_noname(pathname); } # Kein Name angegeben -> Fehler
  9500.         # Name angegeben.
  9501.         pushSTACK(pathname);
  9502.        {# Directory muß existieren:
  9503.         var reg3 object namestring = assure_dir_exists(FALSE); # Filename als ASCIZ-String
  9504.         #ifdef MSDOS
  9505.          #if defined(DJUNIX) || defined(WATCOM) || defined(EMUNIX_OLD_8d)
  9506.           # Datei öffnen:
  9507.           begin_system_call();
  9508.           { var reg2 sintW ergebnis = # Datei zu öffnen versuchen
  9509.               open(TheAsciz(namestring),O_RDONLY);
  9510.             if (ergebnis < 0) { OS_error(); } # Error melden
  9511.             # Nun enthält ergebnis das Handle des geöffneten Files.
  9512.             get_file_write_datetime(ergebnis); # Datum/Uhrzeit holen
  9513.             if (CLOSE(ergebnis) < 0) { OS_error(); } # Datei gleich wieder schließen
  9514.           }
  9515.           end_system_call();
  9516.          #else # defined(EMUNIX_NEW_8e)
  9517.           { var struct stat statbuf;
  9518.             begin_system_call();
  9519.             if (stat(TheAsciz(namestring),&statbuf) < 0) { OS_error(); }
  9520.             end_system_call();
  9521.             if (!S_ISREG(statbuf.st_mode)) { fehler_file_not_exists(); } # Datei muß existieren
  9522.             file_datetime = statbuf.st_mtime;
  9523.           }
  9524.          #endif
  9525.         #endif
  9526.         #ifdef AMIGAOS
  9527.         if (!file_exists(_EMA_)) { fehler_file_not_exists(); } # Datei muß existieren
  9528.         file_datetime = filestatus->fib_Date;
  9529.         #endif
  9530.         #if defined(UNIX) || defined(RISCOS) || defined(WIN32_UNIX)
  9531.         if (!file_exists(_EMA_)) { fehler_file_not_exists(); } # Datei muß existieren
  9532.         file_datetime = filestatus->st_mtime;
  9533.         #endif
  9534.         skipSTACK(1);
  9535.       }}
  9536.     # Datum/Uhrzeit steht nun im Buffer file_datetime.
  9537.     # In Decoded-Time-Format umwandeln:
  9538.     { var decoded_time timepoint;
  9539.       #if defined(DJUNIX) || defined(WATCOM) || defined(EMUNIX_OLD_8d)
  9540.       convert_timedate(file_datetime.time,file_datetime.date,&timepoint);
  9541.       #endif
  9542.       #if defined(UNIX) || defined(EMUNIX_NEW_8e) || defined(AMIGAOS) || defined(RISCOS) || defined(WIN32_DOS) || defined(WIN32_UNIX)
  9543.       convert_time(&file_datetime,&timepoint);
  9544.       #endif
  9545.       pushSTACK(timepoint.Sekunden);
  9546.       pushSTACK(timepoint.Minuten);
  9547.       pushSTACK(timepoint.Stunden);
  9548.       pushSTACK(timepoint.Tag);
  9549.       pushSTACK(timepoint.Monat);
  9550.       pushSTACK(timepoint.Jahr);
  9551.       funcall(S(encode_universal_time),6);
  9552.       # (ENCODE-UNIVERSAL-TIME Sekunden Minuten Stunden Tag Monat Jahr)
  9553.       # als Ergebnis
  9554.   } }
  9555.  
  9556. LISPFUNN(file_author,1)
  9557. # (FILE-AUTHOR file), CLTL S. 424
  9558.   { var reg1 object pathname = popSTACK(); # pathname-Argument
  9559.     if (streamp(pathname))
  9560.       # Stream -> extra behandeln:
  9561.       { # muß File-Stream sein:
  9562.         pathname = as_file_stream(pathname);
  9563.         # Streamtyp File-Stream
  9564.         if (TheStream(pathname)->strmflags & strmflags_open_B)
  9565.           # offener File-Stream -> OK
  9566.           {}
  9567.           else
  9568.           # geschlossener File-Stream -> Truename als Pathname verwenden
  9569.           { pathname = TheStream(pathname)->strm_file_truename;
  9570.             goto is_pathname;
  9571.           }
  9572.       }
  9573.       else
  9574.       { pathname = coerce_pathname(pathname); # zu einem Pathname machen
  9575.         is_pathname: # pathname ist jetzt wirklich ein Pathname
  9576.         # pathname ist jetzt ein Pathname.
  9577.         check_no_wildcards(pathname); # mit Wildcards -> Fehler
  9578.         pathname = use_default_dir(pathname); # Default-Directory einfügen
  9579.         if (namenullp(pathname)) { fehler_noname(pathname); } # Kein Name angegeben -> Fehler
  9580.         # Name angegeben.
  9581.         pushSTACK(pathname);
  9582.        {# Directory muß existieren:
  9583.         var reg3 object namestring = assure_dir_exists(FALSE); # Filename als ASCIZ-String
  9584.         #ifdef MSDOS
  9585.          #if 1
  9586.           # Datei öffnen:
  9587.           begin_system_call();
  9588.           { var reg2 sintW ergebnis = # Datei zu öffnen versuchen
  9589.               open(TheAsciz(namestring),O_RDONLY);
  9590.             if (ergebnis < 0) { OS_error(); } # Error melden
  9591.             # Nun enthält ergebnis das Handle des geöffneten Files.
  9592.             if (CLOSE(ergebnis) < 0) { OS_error(); } # Datei gleich wieder schließen
  9593.           }
  9594.           end_system_call();
  9595.          #else
  9596.           { var struct stat statbuf;
  9597.             begin_system_call();
  9598.             if (stat(TheAsciz(namestring),&statbuf) < 0) { OS_error(); }
  9599.             end_system_call();
  9600.             if (!S_ISREG(statbuf.st_mode)) { fehler_file_not_exists(); } # Datei muß existieren
  9601.           }
  9602.          #endif
  9603.         #endif
  9604.         #if defined(UNIX) || defined(AMIGAOS) || defined(RISCOS) || defined(WIN32_UNIX)
  9605.         if (!file_exists(_EMA_)) { fehler_file_not_exists(); } # Datei muß existieren
  9606.         #endif
  9607.         skipSTACK(1);
  9608.       }}
  9609.     # Datei existiert -> NIL als Wert
  9610.     value1 = NIL; mv_count=1;
  9611.   }
  9612.  
  9613. #if defined(UNIX) || defined(MSDOS) || defined(RISCOS) || defined(WIN32_UNIX)
  9614.  
  9615. LISPFUN(execute,1,0,rest,nokey,0,NIL)
  9616. # (EXECUTE file arg1 arg2 ...) ruft ein File mit gegebenen Argumenten auf.
  9617.   {var reg6 object* args_pointer = rest_args_pointer STACKop 1;
  9618.    {var reg1 object* argptr = args_pointer; # Pointer über die Argumente
  9619.     # File überprüfen:
  9620.     { var reg2 object* file_ = &NEXT(argptr);
  9621.       var reg3 object pathname = *file_;
  9622.       pathname = coerce_pathname(pathname); # zu einem Pathname machen
  9623.       check_no_wildcards(pathname); # mit Wildcards -> Fehler
  9624.       pathname = use_default_dir(pathname); # Default-Directory einfügen
  9625.       if (namenullp(pathname)) { fehler_noname(pathname); } # Kein Name angegeben -> Fehler
  9626.       # Name angegeben.
  9627.       pushSTACK(pathname);
  9628.      {# Directory muß existieren:
  9629.       var reg4 object namestring = assure_dir_exists(FALSE); # Filename als ASCIZ-String
  9630.       # Überprüfe, ob die Datei existiert:
  9631.       if_file_exists(namestring, ; , { fehler_file_not_exists(); } );
  9632.       *file_ = namestring; # retten
  9633.       skipSTACK(1);
  9634.     }}
  9635.     # restliche Argumente überprüfen:
  9636.     { var reg3 uintC count;
  9637.       dotimesC(count,argcount,
  9638.         { var reg2 object* arg_ = &NEXT(argptr);
  9639.           pushSTACK(*arg_); funcall(L(string),1); # nächstes Argument in String umwandeln
  9640.           *arg_ = string_to_asciz(value1); # und ASCIZ-String umwandeln
  9641.         });
  9642.    }}
  9643.    #if defined(EMUNIX_PORTABEL) || (defined(WATCOM) && defined(WINDOWS)) || defined(WIN32_DOS)
  9644.    # (Unter OS/2 scheint system() sicherer zu sein als spawnv(). Warum?)
  9645.    # Alle Argumente (nun ASCIZ-Strings) zusammenhängen, mit Spaces dazwischen:
  9646.    {var reg1 object* argptr = args_pointer; # Pointer über die Argumente
  9647.     var reg3 uintC count;
  9648.     dotimesC(count,argcount, # alle Argumente außer dem letzten durchlaufen
  9649.       { var reg2 object string = NEXT(argptr); # nächster Argumentstring
  9650.         TheSstring(string)->data[TheSstring(string)->length - 1] = ' ';
  9651.       });
  9652.    }
  9653.    { var reg2 object command = string_concat(1+argcount);
  9654.      # Programm aufrufen:
  9655.      begin_system_call();
  9656.     {var reg1 int ergebnis = system(TheAsciz(command));
  9657.      end_system_call();
  9658.      # Rückgabewert verwerten: =0 (OK) -> T, >0 (nicht OK) -> NIL :
  9659.      value1 = (ergebnis==0 ? T : NIL); mv_count=1;
  9660.    }}
  9661.    #endif
  9662.    #if defined(DJUNIX) || (defined(EMUNIX) && !defined(EMUNIX_PORTABEL)) || (defined(WATCOM) && !defined(WINDOWS))
  9663.    {# argv-Array aufbauen:
  9664.     var DYNAMIC_ARRAY(reg5,argv,char*,1+(uintL)argcount+1);
  9665.     { var reg1 object* argptr = args_pointer;
  9666.       var reg2 char** argvptr = &argv[0];
  9667.       var reg4 uintC count;
  9668.       dotimespC(count,argcount+1,
  9669.         { var reg3 object arg = NEXT(argptr); # nächstes Argument, ASCIZ-String
  9670.           *argvptr++ = TheAsciz(arg); # in argv einfüllen
  9671.         });
  9672.       *argvptr = NULL; # und mit Nullpointer abschließen
  9673.     }
  9674.     # Programm aufrufen:
  9675.     begin_system_call();
  9676.     {var reg2 int flags =
  9677.        #ifdef EMUNIX_NEW_9a
  9678.          P_QUOTE  # Argumente korrekt quoten
  9679.        #else
  9680.          0
  9681.        #endif
  9682.        ;
  9683.      var reg1 int ergebnis = spawnv(P_WAIT|flags,argv[0],argv);
  9684.      end_system_call();
  9685.      if (ergebnis < 0) { OS_error(); } # Error melden
  9686.      # Fertig.
  9687.      set_args_end_pointer(args_pointer); # STACK aufräumen
  9688.      # Rückgabewert verwerten: =0 (OK) -> T, >0 (nicht OK) -> NIL :
  9689.      value1 = (ergebnis==0 ? T : NIL); mv_count=1;
  9690.     }
  9691.     FREE_DYNAMIC_ARRAY(argv);
  9692.    }
  9693.    #endif
  9694.    #if defined(UNIX) || defined(RISCOS) || defined(WIN32_UNIX)
  9695.    { # argv-Array im Stack aufbauen und Strings in den Stack kopieren:
  9696.      var reg9 uintL argvdata_length = 0;
  9697.      { var reg1 object* argptr = args_pointer;
  9698.        var reg3 uintC count;
  9699.        dotimespC(count,argcount+1,
  9700.          { var reg2 object arg = NEXT(argptr); # nächstes Argument, ASCIZ-String
  9701.            argvdata_length += TheSstring(arg)->length;
  9702.          });
  9703.      }
  9704.     {var DYNAMIC_ARRAY(reg9,argv,char*,1+(uintL)argcount+1);
  9705.      var DYNAMIC_ARRAY(reg9,argvdata,char,argvdata_length);
  9706.      { var reg8 object* argptr = args_pointer;
  9707.        var reg7 char** argvptr = &argv[0];
  9708.        var reg1 char* argvdataptr = &argvdata[0];
  9709.        var reg5 uintC count;
  9710.        dotimespC(count,argcount+1,
  9711.          { var reg4 object arg = NEXT(argptr); # nächstes Argument, ASCIZ-String
  9712.            var reg2 char* ptr = TheAsciz(arg);
  9713.            var reg3 uintL len = TheSstring(arg)->length;
  9714.            *argvptr++ = argvdataptr; # in argv einfüllen
  9715.            dotimespL(len,len, { *argvdataptr++ = *ptr++; } ); # und kopieren
  9716.          });
  9717.        *argvptr = NULL; # und mit Nullpointer abschließen
  9718.      }
  9719.      # einen neuen Prozeß starten:
  9720.      { var reg2 int child;
  9721.        begin_system_call();
  9722.        begin_want_sigcld();
  9723.        if ((child = vfork()) ==0)
  9724.          # Dieses Programmstück wird vom Child-Prozeß ausgeführt:
  9725.          { execv(argv[0],argv); # Programm aufrufen
  9726.            _exit(-1); # sollte dies mißlingen, Child-Prozeß beenden
  9727.          }
  9728.        # Dieses Programmstück wird wieder vom Aufrufer ausgeführt:
  9729.        if (child==-1)
  9730.          # Etwas ist mißlungen, entweder beim vfork oder beim execv.
  9731.          # In beiden Fällen wurde errno gesetzt.
  9732.          { end_want_sigcld(); OS_error(); }
  9733.        # Warten, bis der Child-Prozeß beendet wird:
  9734.       {var int status = wait2(child);
  9735.        # vgl. WAIT(2V) und #include <sys/wait.h> :
  9736.        #   WIFSTOPPED(status)  ==  ((status & 0xFF) == 0177)
  9737.        #   WEXITSTATUS(status)  == ((status & 0xFF00) >> 8)
  9738.        end_want_sigcld();
  9739.        end_system_call();
  9740.        # Fertig.
  9741.        set_args_end_pointer(args_pointer); # STACK aufräumen
  9742.        value1 = (((status & 0xFF) == 0000) # Prozeß normal beendet (ohne Signal, ohne Core-Dump) ?
  9743.                  ? # ja -> Exit-Status als Wert:
  9744.                    fixnum( (status & 0xFF00) >> 8)
  9745.                  : NIL # nein -> NIL als Wert
  9746.                 );
  9747.        mv_count=1;
  9748.      }}
  9749.      FREE_DYNAMIC_ARRAY(argvdata);
  9750.      FREE_DYNAMIC_ARRAY(argv);
  9751.    }}
  9752.    #endif
  9753.   }
  9754.  
  9755. #endif
  9756.  
  9757. #ifdef AMIGAOS
  9758.  
  9759. LISPFUN(execute,1,0,norest,nokey,0,NIL)
  9760. # (EXECUTE command-string) schickt einen String an das Betriebssystem.
  9761. # Das ist in diesem Fall mit (SHELL command-string) synonym.
  9762.   { C_shell(); } # SHELL aufrufen, selber Stackaufbau
  9763.  
  9764. #endif
  9765.  
  9766. #ifdef HAVE_SHELL
  9767.  
  9768. # (SHELL) ruft eine Shell auf.
  9769. # (SHELL command) ruft eine Shell auf und läßt sie ein Kommando ausführen.
  9770.  
  9771. #if defined(AMIGAOS)
  9772.  
  9773. LISPFUN(shell,0,1,norest,nokey,0,NIL)
  9774.   { var reg1 object command = popSTACK();
  9775.     if (eq(command,unbound))
  9776.       # Kommandointerpreter aufrufen:
  9777.       { run_time_stop();
  9778.         begin_system_call();
  9779.        {var reg2 BOOL ergebnis = FALSE;
  9780.         #if 0 # so einfach geht's wohl nicht
  9781.         ergebnis = Execute("",Input_handle,Output_handle);
  9782.         #else
  9783.         var reg3 Handle terminal = Open("*",MODE_READWRITE);
  9784.         if (!(terminal==Handle_NULL))
  9785.           { ergebnis = Execute("",terminal,Handle_NULL);
  9786.             Close(terminal);
  9787.             Write(Output_handle,CRLFstring,1);
  9788.           }
  9789.         #endif
  9790.         end_system_call();
  9791.         run_time_restart();
  9792.         # Rückgabewert verwerten: ausgeführt -> T, nicht gefunden -> NIL :
  9793.         value1 = (ergebnis ? T : NIL); mv_count=1;
  9794.       }}
  9795.       else
  9796.       # einzelnes Kommando ausführen:
  9797.       { if (!stringp(command))
  9798.           { pushSTACK(command); # Wert für Slot DATUM von TYPE-ERROR
  9799.             pushSTACK(S(string)); # Wert für Slot EXPECTED-TYPE von TYPE-ERROR
  9800.             pushSTACK(command);
  9801.             pushSTACK(TheSubr(subr_self)->name);
  9802.             //: DEUTSCH "~: Befehl muß ein String sein, nicht ~."
  9803.             //: ENGLISH "~: the command should be a string, not ~"
  9804.             //: FRANCAIS "~ : La commande doit être de type STRING et non ~."
  9805.             fehler(type_error, GETTEXT("~: the command should be a string, not ~"));
  9806.           }
  9807.         command = string_to_asciz(command); # in Simple-String umwandeln
  9808.         # Kommando ausführen:
  9809.         run_time_stop();
  9810.         begin_system_call();
  9811.        {var reg2 BOOL ergebnis = Execute(TheAsciz(command),Handle_NULL,Output_handle);
  9812.         end_system_call();
  9813.         run_time_restart();
  9814.         # Rückgabewert verwerten: ausgeführt -> T, nicht gefunden -> NIL :
  9815.         value1 = (ergebnis ? T : NIL); mv_count=1;
  9816.       }}
  9817.   }
  9818.  
  9819. #else # UNIX || MSDOS || ...
  9820.  
  9821. LISPFUN(shell,0,1,norest,nokey,0,NIL)
  9822.   { var reg1 object command = popSTACK();
  9823.     if (eq(command,unbound))
  9824.       { # (EXECUTE shell) ausführen:
  9825.         #if defined(UNIX) || defined(WIN32_UNIX)
  9826.         pushSTACK(O(user_shell)); # Shell-Name
  9827.         #else # MSDOS
  9828.         pushSTACK(O(command_shell)); # Shell-Name
  9829.         #endif
  9830.         funcall(L(execute),1);
  9831.       }
  9832.       else
  9833.       #if defined(MSDOS) || defined(RISCOS)
  9834.       # Dem DOS-Kommandointerpreter muß man das Kommando bereits entlang
  9835.       # der Leerstellen in einzelne Teile zerlegt übergeben. Die Funktion
  9836.       # system() erledigt uns das zum Glück.
  9837.       { command = string_to_asciz(command);
  9838.         begin_system_call();
  9839.         # Programm aufrufen:
  9840.        {var reg1 int ergebnis = system(TheAsciz(command));
  9841.         end_system_call();
  9842.         # Rückgabewert verwerten: =0 (OK) -> T, >0 (nicht OK) -> NIL :
  9843.         value1 = (ergebnis==0 ? T : NIL); mv_count=1;
  9844.       }}
  9845.       #else
  9846.       { # (EXECUTE shell "-c" command) ausführen:
  9847.         pushSTACK(O(command_shell)); # Shell-Name
  9848.         pushSTACK(O(command_shell_option)); # Shell-Option "-c"
  9849.         #if defined(MSDOS) && defined(EMUNIX)
  9850.         # Unter DOS 2.x, 3.x kann das Optionen-Zeichen ein anderes sein!
  9851.         if ((_osmode == DOS_MODE) && (_osmajor < 4))
  9852.           { var reg2 uintB swchar = _swchar();
  9853.             if (swchar) # evtl. "/C" durch etwas anderes ersetzen
  9854.               { TheSstring(STACK_0)->data[0] = swchar; } # (destruktiv)
  9855.           }
  9856.         #endif
  9857.         pushSTACK(command);
  9858.         funcall(L(execute),3);
  9859.       }
  9860.       #endif
  9861.   }
  9862.  
  9863. #endif
  9864.  
  9865. #endif
  9866.  
  9867. LISPFUNN(savemem,1)
  9868. # (SAVEMEM pathname) speichert ein Speicherabbild unter pathname ab.
  9869.   { # (OPEN pathname :direction :output) ausführen:
  9870.     # pathname als 1. Argument
  9871.     pushSTACK(S(Kdirection)); # :DIRECTION als 2. Argument
  9872.     pushSTACK(S(Koutput)); # :OUTPUT als 3. Argument
  9873.     #if !(defined(UNIX) || defined(WIN32_UNIX))
  9874.     funcall(L(open),3);
  9875.     #else
  9876.     # Unter Unix mit mmap() darf man existierende .mem-Files nicht einfach
  9877.     # überschreiben, weil laufende Lisp-Prozesse dadurch abstürzen würden.
  9878.     pushSTACK(S(Kif_exists)); # :IF-EXISTS als 4. Argument
  9879.     pushSTACK(S(Krename_and_delete)); # :RENAME-AND-DELETE als 5. Argument
  9880.     funcall(L(open),5);
  9881.     #endif
  9882.     # Speicherabbild in die Datei schreiben:
  9883.     # (Den Stream muß die Funktion savemem() schließen, auch im Fehlerfalle.)
  9884.     savemem(value1);
  9885.     value1 = T; mv_count=1; # 1 Wert T
  9886.   }
  9887.  
  9888. # ==============================================================================
  9889.  
  9890. #ifdef HAVE_DISASSEMBLER
  9891.  
  9892. # Finding the full path of the executable.
  9893. # Bruno Haible 20.12.1994
  9894.  
  9895. # This assumes that the executable is not removed or renamed while running.
  9896.  
  9897. # file name of the executable
  9898. local char* executable_name = NULL;
  9899. #define default_executable_name  "lisp.run"
  9900.  
  9901. # file descriptor of the executable
  9902. # (Only used to verify that we find the correct executable.)
  9903. local int executable_fd = -1;
  9904.  
  9905. # maybe_executable(pathname)
  9906. # checks whether a given pathname may belong to the executable.
  9907. local boolean maybe_executable (const char * filename);
  9908. local boolean maybe_executable(filename)
  9909.   var reg1 const char * filename;
  9910. { var struct stat statexe;
  9911.   var struct stat statfile;
  9912.   if (access(filename,R_OK|X_OK) < 0)
  9913.     return FALSE;
  9914.   if (executable_fd < 0)
  9915.     return TRUE;
  9916.   # If we already have an executable_fd, check that filename points to
  9917.   # the same inode.
  9918.   if (fstat(executable_fd,&statexe) < 0)
  9919.     return TRUE;
  9920.   if (stat(filename,&statfile) < 0)
  9921.     return FALSE;
  9922.   if (statfile.st_dev
  9923.       && statfile.st_dev == statexe.st_dev
  9924.       && statfile.st_ino == statexe.st_ino
  9925.      )
  9926.     return TRUE;
  9927.   return FALSE;
  9928. }
  9929.  
  9930. # find_executable(program_name)
  9931. # is to be called immediately after the program starts,
  9932. # with program_name = argv[0],
  9933. # before any chdir() operation and before any setenv("PATH",...).
  9934. # It determines the full program path and opens a file descriptor to
  9935. # the executable, for later use.
  9936. # Return value is 0 if successful, -1 and errno set if not.
  9937. global int find_executable (const char * program_name);
  9938. global int find_executable(program_name)
  9939.   var reg3 const char * program_name;
  9940. { # Don't need to execute this more than once.
  9941.   if (!(executable_name == NULL)) return 0;
  9942.   #ifdef UNIX_LINUX
  9943.   # The executable is accessible as /proc/<pid>/exe. We try this first
  9944.   # because it is safer: no race condition w.r.t. the file system. It may
  9945.   # fail, however, if the user has not compiled /proc support into his
  9946.   # kernel.
  9947.   { var char buf[6+10+5];
  9948.     var reg1 int fd;
  9949.     sprintf(buf,"/proc/%d/exe",getpid());
  9950.     fd = OPEN(buf,O_RDONLY,my_open_mask);
  9951.     if (fd >= 0)
  9952.       executable_fd = fd;
  9953.   }
  9954.   #endif
  9955.   # Now we guess the executable's full path. We assume the executable
  9956.   # has been called via execlp() or execvp() with properly set up argv[0].
  9957.   # The login(1) convention to add a '-' prefix to argv[0] is not supported.
  9958.  {var reg8 boolean has_slash = FALSE;
  9959.   {var reg1 const char * p;
  9960.    for (p = program_name; *p; p++) { if (*p == '/') { has_slash = TRUE; break; } }
  9961.   }
  9962.   if (!has_slash)
  9963.     { # exec searches paths without slashes in the directory list given
  9964.       # by $PATH.
  9965.       var reg7 const char * path = getenv("PATH");
  9966.       if (!(path==NULL))
  9967.         { var reg1 const char * p;
  9968.           var reg6 const char * p_next;
  9969.           for (p = path; *p; p = p_next)
  9970.             { var reg2 const char * q;
  9971.               var reg4 uintL p_len;
  9972.               for (q = p; *q; q++) { if (*q == ':') break; }
  9973.               p_len = q-p; p_next = (*q=='\0' ? q : q+1);
  9974.               # We have a path item at p, of length p_len.
  9975.               # Now concatenate the path item and program_name.
  9976.              {var reg5 char * concat_name = (char*) malloc(p_len + strlen(program_name) + 2);
  9977.               if (concat_name == NULL) { errno = ENOMEM; goto notfound; }
  9978.               if (p_len == 0)
  9979.                 # empty PATH element designates the current directory
  9980.                 { strcpy(concat_name,program_name); }
  9981.                 else
  9982.                 { memcpy(concat_name, p, p_len);
  9983.                   sprintf(concat_name+p_len, "/%s", program_name);
  9984.                 }
  9985.               if (maybe_executable(concat_name))
  9986.                 # Assume we have found the executable
  9987.                 { program_name = concat_name; goto resolve; }
  9988.               free(concat_name);
  9989.             }}
  9990.         }
  9991.       # Not found in the PATH, assume the current directory.
  9992.     }
  9993.   # exec treats paths containing slashes as relative to the current directory.
  9994.   if (maybe_executable(program_name))
  9995.     resolve:
  9996.     # resolve program_name:
  9997.     { executable_name = (char*) malloc(MAXPATHLEN);
  9998.       if (executable_name == NULL) { errno = ENOMEM; goto notfound; }
  9999.       if (realpath(program_name,executable_name) == NULL)
  10000.         { free(executable_name); goto notfound; }
  10001.       return 0;
  10002.     }
  10003.   errno = ENOENT;
  10004.  notfound:
  10005.   executable_name = default_executable_name; return -1;
  10006. }}
  10007.  
  10008. # (SYS::PROGRAM-NAME) returns the executable's name.
  10009. LISPFUNN(program_name,0)
  10010. { value1 = asciz_to_string(executable_name); mv_count=1; }
  10011.  
  10012. #endif
  10013.  
  10014. # ==============================================================================
  10015.  
  10016. #ifdef EMUNIX_PORTABEL
  10017.  
  10018. # Umgehen eines lästigen ENAMETOOLONG Errors bei Benutzung von langen
  10019. # Filenamen auf FAT-Drives unter OS/2:
  10020.  
  10021. #undef chdir
  10022. #undef access
  10023. #undef stat
  10024. #undef unlink
  10025. #undef rename
  10026. #undef __findfirst
  10027. #undef mkdir
  10028. #undef open
  10029. #undef creat
  10030. #undef spawnv
  10031.  
  10032. # path2 := verkürzte Kopie von path1
  10033. local void shorten_path (const char* path1, char* path2)
  10034.   { var reg1 const uintB* p1 = path1;
  10035.     var reg2 uintB* p2 = path2;
  10036.     var reg3 uintB c;
  10037.     var reg4 uintC wordlength = 0; # bisherige Länge in Name oder Typ
  10038.     var reg5 uintC maxwordlength = 8; # = 8 im Namen, = 3 im Typ
  10039.     loop
  10040.       { c = *p1++;
  10041.         if (c=='\0') { *p2++ = c; break; }
  10042.         if ((c=='\\') || (c=='/') || (c==':'))
  10043.           { *p2++ = c; wordlength = 0; maxwordlength = 8; }
  10044.         elif (c=='.')
  10045.           { *p2++ = c; wordlength = 0; maxwordlength = 3; }
  10046.         else
  10047.           { if (++wordlength <= maxwordlength) { *p2++ = c; } }
  10048.   }   }
  10049.  
  10050. global int my_chdir(path)
  10051.   var reg2 CONST char* path;
  10052.   { var reg1 int erg = chdir(path);
  10053.     if ((erg<0) && (errno==ENAMETOOLONG))
  10054.       { var reg3 char* shorter_path = alloca(asciz_length(path)+1);
  10055.         shorten_path(path,shorter_path);
  10056.         erg = chdir(shorter_path);
  10057.       }
  10058.     return erg;
  10059.   }
  10060.  
  10061. global int my_access(path,amode)
  10062.   var reg3 CONST char* path;
  10063.   var reg2 int amode;
  10064.   { var reg1 int erg = access(path,amode);
  10065.     if ((erg<0) && (errno==ENAMETOOLONG))
  10066.       { var reg4 char* shorter_path = alloca(asciz_length(path)+1);
  10067.         shorten_path(path,shorter_path);
  10068.         erg = access(shorter_path,amode);
  10069.       }
  10070.     return erg;
  10071.   }
  10072.  
  10073. global int my_stat(path,buf)
  10074.   var reg3 CONST char* path;
  10075.   var reg2 struct stat * buf;
  10076.   { var reg1 int erg = stat(path,buf);
  10077.     if ((erg<0) && (errno==ENAMETOOLONG))
  10078.       { var reg4 char* shorter_path = alloca(asciz_length(path)+1);
  10079.         shorten_path(path,shorter_path);
  10080.         erg = stat(shorter_path,buf);
  10081.       }
  10082.     return erg;
  10083.   }
  10084.  
  10085. global int my_unlink(path)
  10086.   var reg2 CONST char* path;
  10087.   { var reg1 int erg = unlink(path);
  10088.     if ((erg<0) && (errno==ENAMETOOLONG))
  10089.       { var reg3 char* shorter_path = alloca(asciz_length(path)+1);
  10090.         shorten_path(path,shorter_path);
  10091.         erg = unlink(shorter_path);
  10092.       }
  10093.     return erg;
  10094.   }
  10095.  
  10096. global int my_rename(oldpath,newpath)
  10097.   var reg3 CONST char* oldpath;
  10098.   var reg2 CONST char* newpath;
  10099.   { var reg1 int erg = rename(oldpath,newpath);
  10100.     if ((erg<0) && (errno==ENAMETOOLONG))
  10101.       { var reg4 char* shorter_oldpath = alloca(asciz_length(oldpath)+1);
  10102.         shorten_path(oldpath,shorter_oldpath);
  10103.         erg = rename(shorter_oldpath,newpath);
  10104.         if ((erg<0) && (errno==ENAMETOOLONG))
  10105.           { var reg5 char* shorter_newpath = alloca(asciz_length(newpath)+1);
  10106.             shorten_path(newpath,shorter_newpath);
  10107.             erg = rename(shorter_oldpath,shorter_newpath);
  10108.       }   }
  10109.     return erg;
  10110.   }
  10111.  
  10112. global int my___findfirst(path,attrib,ffblk)
  10113.   var reg4 const char* path;
  10114.   var reg2 int attrib;
  10115.   var reg3 struct ffblk * ffblk;
  10116.   { var reg1 int erg = __findfirst(path,attrib,ffblk);
  10117.     if ((erg<0) && (errno==ENAMETOOLONG))
  10118.       { var reg5 char* shorter_path = alloca(asciz_length(path)+1);
  10119.         shorten_path(path,shorter_path);
  10120.         erg = __findfirst(shorter_path,attrib,ffblk);
  10121.       }
  10122.     return erg;
  10123.   }
  10124.  
  10125. #ifdef EMUNIX_OLD_8e
  10126.   #define mkdir(path,attrib) (mkdir)(path)
  10127. #endif
  10128. global int my_mkdir(path,attrib)
  10129.   var reg2 CONST char* path;
  10130.   var reg3 long attrib;
  10131.   { var reg1 int erg = mkdir(path,attrib);
  10132.     if ((erg<0) && (errno==ENAMETOOLONG))
  10133.       { var reg4 char* shorter_path = alloca(asciz_length(path)+1);
  10134.         shorten_path(path,shorter_path);
  10135.         erg = mkdir(shorter_path,attrib);
  10136.       }
  10137.     return erg;
  10138.   }
  10139.  
  10140. global int my_open(path,flags)
  10141.   var reg3 CONST char* path;
  10142.   var reg2 int flags;
  10143.   { var reg1 int erg = open(path,flags);
  10144.     if ((erg<0) && (errno==ENAMETOOLONG))
  10145.       { var reg4 char* shorter_path = alloca(asciz_length(path)+1);
  10146.         shorten_path(path,shorter_path);
  10147.         erg = open(shorter_path,flags);
  10148.       }
  10149.     return erg;
  10150.   }
  10151.  
  10152. #define creat(path,mode)  open(path,O_RDWR|O_TRUNC|O_CREAT,mode)
  10153. global int my_creat(path,pmode)
  10154.   var reg3 CONST char* path;
  10155.   var reg2 int pmode;
  10156.   { var reg1 int erg = creat(path,pmode);
  10157.     if ((erg<0) && (errno==ENAMETOOLONG))
  10158.       { var reg4 char* shorter_path = alloca(asciz_length(path)+1);
  10159.         shorten_path(path,shorter_path);
  10160.         erg = creat(shorter_path,pmode);
  10161.       }
  10162.     return erg;
  10163.   }
  10164.  
  10165. global int my_spawnv(pmode,path,argv)
  10166.   var reg2 int pmode;
  10167.   var reg4 CONST char* path;
  10168.   var reg3 CONST char* CONST * argv;
  10169.   { var reg1 int erg = spawnv(pmode,path,argv);
  10170.     if ((erg<0) && (errno==ENAMETOOLONG))
  10171.       { var reg5 char* shorter_path = alloca(asciz_length(path)+1);
  10172.         shorten_path(path,shorter_path);
  10173.         erg = spawnv(pmode,shorter_path,argv);
  10174.       }
  10175.     return erg;
  10176.   }
  10177.  
  10178. #endif
  10179.  
  10180. #if DJGPP == 2
  10181. # hack until DJGPP2 gets fixed 
  10182. # (symptom: EACCES on read of just-recreated files)
  10183. global int djgpp2_creat (CONST char *path, unsigned long mode);
  10184. global int djgpp2_creat(path,mode)
  10185.   var reg3 CONST char* path;
  10186.   var reg2 unsigned long mode;
  10187.   {
  10188.     if (unlink(path) < 0) { OS_error(); }
  10189.     #undef creat
  10190.     return creat(path,mode);
  10191.   }
  10192. #endif
  10193.  
  10194. # ==============================================================================
  10195.  
  10196.