summaryrefslogtreecommitdiffstats
path: root/stdlib
Commit message (Collapse)AuthorAgeFilesLines
* Version 284.txr-284Kaz Kylheku2022-12-301-1/+1
| | | | | | | | | | | | | | * RELNOTES: Updated. * configure (txr_ver): Bumped version. * stdlib/ver.tl (lib-version): Bumped. * txr.1: Bumped version and date. * txr.vim, tl.vim: Regenerated. * protsym.c: Regenerated.
* awk: new feature, res variable.Kaz Kylheku2022-12-302-2/+5
| | | | | | | | | | | | | | | | The res variable captures the specific value of the condition expression, making it available to the action. * autoload.c (awk_set_entries): Intern the res symbol * stdlib/awk.tl (awk): Instead of generating the condition-action into a simple when, we use whenlet to also bind the res variable. * tests/015/awk-res.tl: New file. * txr.1: Documented. * stdlib/doc-syms.tl: Updated.
* doc: fix nonexistent macro name.Kaz Kylheku2022-12-301-1/+1
| | | | | | | * txr.1: *define-struct-prelude* should of course be define-struct-prelude. * stdlib/doc-syms.tl: Updated.
* quips: new one.Kaz Kylheku2022-12-101-0/+1
| | | | * stdlib/quips.tl (%quips%): New one about personality.
* compiler: runaway recursion in constant folding call.Kaz Kylheku2022-11-261-1/+4
| | | | | | | | | | | | | | | | | When an invalid call expression is constant folded, such as (call 'abs 1 2), runaway recursion occurs. This is because due to the wrong number of arguments being passed to abs, the safe-const-reduce function returns the expression unmodified. The comp-apply-call method then passes it to compile, wrongly assuming a reduction had taken place, and so everything repeats. * stdlib/compiler.tl (comp-apply-call): Detect when safe-const-reduce has hit a fixed point by returning the input form. In that case, we don't call the compiler top-level entry point, but the comp-fun-form method directly; the wrong function call will be compiled without constant folding and throw an error at run-time.
* compiler: bug: some functions mustn't be constant-foldedKaz Kylheku2022-11-251-5/+5
| | | | | | | | * stdlib/constfun.tl (%const-foldable-syms%): Removing the following functions, which cannot be constant folded because maybe are relied upon to produce fresh objects: cons, sub-list, conses, ldiff, uniq, tostring, tostringp, join, join-with.
* read-once: support globals properly.Kaz Kylheku2022-11-101-2/+3
| | | | | | | | | | | | | | | | | | | | | | | | When a global variable v is wrapped with (read-once v), multiple accesses to the place still generate multiple accesses of the global through getv or getlx instructions. The reason is that the alet and slet macros optimize away a temporary bound to the value of a variable regardless of whether the variable is lexical. Let's fix that. * stdlib/place.tl (slet, alet): Replace the bindable test with lexical-var-p, in the given environment. A binding to a variable is only alias-like if the variable is lexical, otherwise we need a real temporary. * tests/012/struct.tl (get-current-menv): New macro. (menv): New global variable. Fix a number of tests which use expand, whose expansion has changed because the expressions refer to free variables. We introduce an environment parameter which binds all the variables, so that the optimized expansion is produced, as before. * txr.1: Updated documentation. slet gets examples.
* New feature: struct preludes.Kaz Kylheku2022-11-032-2/+21
| | | | | | | | | | | | | | | | | | | | | | | | | | | A struct prelude definition associates one or more future defstruct (by struct name) with clauses which are implicitly inserted into the defstruct. It is purely a macro-time construct, customizing the expansion behavior of defstruct. * stdlib/struct.tl (*struct-prelude, *struct-prelude-alists*): New special variables holding hash tables. (defstruct): Before processing slot-specs, augment it with the contents of the prelude definitions associated with this struct name. (define-struct-prelude): New macro. * autoload.c (struct_set_entries): define-struct-prelude is interned and triggers autoload of struct module. * tests/012/oop-prelude.tl: New file. * tests/012/oop-prelude.expected: Likewise. * txr.1: Documented. * stdlib/doc-syms.tl: Updated.
* compiler: optimizations in catch.Kaz Kylheku2022-10-271-46/+63
| | | | | | | | | | | | | | | * stdlib/compiler.tl (comp-catch): Under an optimization level of at least 1, if no symbols are being caught, or if the try expression is a safe constant expression, then just compile the try expression. Furthermore, if there is only one exception symbol being caught, and a catch clause is for a subtype of that symbol, we eliminate the run-time exception-subtype-p test. This will always be true if the catch macros are being used, because the list of symbols is derived from the clauses. Lastly, if there is only one exception symbol being caught, any clause which doesn't match that symbol is now eliminated as dead code. That shouldn't happen unless the sys:catch operator is used directly.
* defstruct: new :inherit clause.Kaz Kylheku2022-10-171-5/+14
| | | | | | | | | | | | | | | The :inherit clause allows custom struct clauses to inject inherited bases. * stdlib/struct.tl (defstruct): Recognize :inherit clause, adding symbol arguments to extra list of supers that get appended to the list coming from defstruct's seconda rgument. (define-struct-clause): Disallow :inherit clause name. * tests/012/oop-dsc.tl: New tests. * txr.1: Documented.
* Version 283.txr-283Kaz Kylheku2022-10-161-1/+1
| | | | | | | | | | | | | | | | * RELNOTES: Updated. * configure (txr_ver): Bumped version. * stdlib/ver.tl (lib-version): Bumped. * txr.1: Bumped version and date. Also mention that separator commas in integer tokens are new in 283 and have a different interpretation in older versions. * txr.vim, tl.vim: Regenerated. * protsym.c: Regenerated.
* New function: macroexpand-struct-clause.Kaz Kylheku2022-10-132-0/+6
| | | | | | | | | | | * stdlib/struct.tl (macroexpand-struct-clause): New function. * autoload.c (struct_set_entries): Autoload struct module on macroexpand-struct-clause. * txr.1: Documented. * stdlib/doc-syms.tl: Updated.
* New function: macroexpand-match.Kaz Kylheku2022-10-132-0/+6
| | | | | | | | | | | * stdlib/match.tl (macroexpand-match): New function. * autoload.c (match_set_entries): Autoload match module on macroexpand-match. * txr.1: Documented. * stdlib/doc-syms.tl: Updated.
* New function: macroexpand-place.Kaz Kylheku2022-10-132-6/+15
| | | | | | | | | | | | | | | * stdlib/place.tl (sys:pl-expand): Function renamed to macroexpand-place; env parameter becomes optional. (macroexpand-1-place): New function. (place-form-p, call-update-expander, call-clobber-expander, call-delete-expander): Follow rename. * autoload.c (place_set_entries): Register symbols macroexpand-place and macroexpand-1-place for autoload. * txr.1: Documented. * stdlib/doc-syms.tl: Updated.
* New function: macroexpand-params.Kaz Kylheku2022-10-132-0/+8
| | | | | | | | | | | | | | | * stdlib/pmac.tl (macroexpand-params): New function, implemented using newly exposed sys:expand-param-macro. * autoload.c (pmac_set_entries): Trigger pmac.tl autload on macroexpand-params symbol. * eval.c (eval_init): Register existing expand_param_macro function as sys:expand-param-macro. * txr.1: Documented. * stdlib/doc-syms.tl: Updated.
* structs: optional init-exprs now useful in :delegateKaz Kylheku2022-10-111-3/+13
| | | | | | | | | | | | * stdlib/struct.tl (:delegate): Handle the two-element form of the optional parameter, which specifies the usual initializing expression for the default value. This is just passed through as-is to the generated method. Diagnose if the three-element form occurs. * tests/012/oop.tl: Some new tests. * txr.1: Documented.
* json: support standard-style formatting.Kaz Kylheku2022-10-111-0/+1
| | | | | | | | | | | | | | | | | | | | * stream.c (standard_k, print_json_format_s): New symbol variables. (stream_init): New variables initialized. * stream.h (enum json_fmt): New enum. (standard_k, print_json_format_s): Declared. * lib.c (out_json_rec): Take enum json_fmt param, and pass it recursively. Printing for vector and dictionaries reacts to argument value. (out_json, put_json): Examine value of special var *print-json-format* and calculate enum json_fmt value from this. Pass to out_json_rec. * txr.1: Documented. * stdlib/doc-syms.tl: Updated.
* streams: new function inc-indent-abs.Kaz Kylheku2022-10-111-3/+4
| | | | | | | | | | | * stream.c (inc_indent_abs): New function. (stream_init): inc-init-abs intrinsic registered. * stream.h (inc_indent_abs): Declared. * txr.1: Documented. * stdlib/doc-syms.tl: Updated.
* New quip: future-proof.Kaz Kylheku2022-10-101-0/+1
| | | | * stdlib/quips.tl (%quips%): New entry.
* defstruct: consolidate finalizers into one lambda.Kaz Kylheku2022-10-051-6/+12
| | | | | | | | * stdlib/struct.tl (defstruct): Don't generate a separate finalizer registration for each :fini or :postfini; roll them into a single lambda in the correct order. Their object argument turns into a let block around each piece of code to bind that argument, like had been done for :init and :postinit.
* defstruct: refactor elimination of empty :init/:fini.Kaz Kylheku2022-10-051-18/+18
| | | | | | | | * stdlib/struct.tl (defstruct): When an :init, :fini, :postinit or :postfini has an empty body, do not push it onto its corresponding list. Then later we don't have to check for empty items when generating the code; we know only non-empty items are on the lists.
* define-struct-clause: reject :postfiniKaz Kylheku2022-10-041-1/+1
| | | | | | | * stdlib/struct.tl (define-struct-clause): Disallow the :postfini keyword as clause name. * txr.1: Documented.
* oop: allow multiple :init, :fini, etc.Kaz Kylheku2022-10-041-43/+29
| | | | | | | | | | | | | | | | | | | | | | The motivation is that struct clause macros defined using define-struct-clause may want to introduce their own initializers and finalizers for the specific stuff they add to the struct. The uniqueness restrictions on these initializing and finalizing clauses makes it impossible to use two clause macros which both want to inject a definition of the same initializer or finalizer type. * stdlib/struct.tl (defstruct): Don't enforce that there be at most one clause in the category of :init, :postinit, :fini or :postini. Multiple are allowed. They all execute left-to-right except for :fini. * tests/012/fini.tl: New tests. * tests/012/fini.expected: Updated. * txr.1: Documented.
* New: %fun% mechanism for current function name.Kaz Kylheku2022-10-032-15/+24
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | * eval.c (pct_fun_s): New symbol variable, holding the usr:%fun% symbol. (fun_macro_env): New static function. (do_expand): For defun and defmacro, use fun_macro_env to establish an environment binding the %fun% symbol macro, and expand everything in that environment. (eval_init): Intern the %fun% symbol, initializing pct_fun_s, and also register a global symbol macro in that name so that we can freely use %fun% everywhere without worrying that the code will blow up. E.g. a logging macro can use it to get the function name, but still be useful in a top-level form outside of a named function. * stdlib/struct.tl (sys:meth-lambda): New macro. (defstruct, defmeth): Use sys:meth-lambda as a replacement for lambda to set up the %fun% symbol macro. In the :init case which doesn't use a lambda, an open-coded symacrolet does the job. * tests/019/pct-fun.tl: New file. * tests/019/pct-fun.expected: Likewise. * txr.1: Documented. * stdlib/doc-syms.tl: Updated.
* New method: str-addr.Kaz Kylheku2022-10-032-0/+29
| | | | | | | | | | | | | | | | | | | | | * socket.c (sock_set_entries): Intern str-addr symbol. There is no autoload on this because the struct types of which this is a method don't exist if the socket module has not been loaded. * stdlib/socket.tl ((sockaddr-in str-addr), (sockaddr-in6 str-addr), (sockaddr-un str-addr)): New methods. * tests/014/str-addr.tl: New file. This provides coverage not just for the str-addr method, but the hitherto untested address to text functions. This is why the bug was found, that was addressed in the previous commit. The test case which produces "8000::1" was actually producing "800:1". * txr.1: Documented. * stdlib/doc-syms.tl: Updated.
* str-in6addr: bugfix: trailing zero in hex problem.Kaz Kylheku2022-10-031-2/+4
| | | | | | | | | | | * stdlib/socket.c (sys:in6addr-condensed-text): The regular expression used in calculating zr is incorrect; the zero in it can match the trailing zero of a nonzero quad, when the intent is only to match zero quads. Hack: we represent zero quads by the character Z and use that for the matching and removal of the longest range of zero quads. Then we filter the Z back to 0.
* New sockaddr-str function.Kaz Kylheku2022-10-022-0/+10
| | | | | | | | | | | | | | | | This function "intelligently" constructs an address object of the right type from a string. * socket.c (sock_set_entries): Autoload socket.tl on sockaddr-str function being accessed. * stdlib/socket.tl (sockaddr-str): New function. * tests/014/sockaddr-str.tl: New file. * txr.1: Documented. * stdlib.doc-syms.tl: Updated.
* New :postfini feature in defstruct.Kaz Kylheku2022-09-271-2/+16
| | | | | | | | | | | | | | | | | | | | | | | | The :postfini clause registers a finalizer that runs in the ordinary order: after previously registered ones. This has the effect of allowing a derived structure to run clean-up actions after those of inherited structures. Either order can be useful because the dependencies between base and derived can go in either direction. It's a huge mistake in C++ that it supports only derived-first destructor invocation order. * stdlib/struct.tl (defstruct): Recognize and translate :postfini clause. It's exactly like :fini but omits the t parameter in the finalize call, registering in the natural order. * tests/012/fini.tl (derived): Add :postfini handler. * tests/012/fini.expected: Updated to reflect the messages coming from the postfini handler, which are happening in the correct order. * txr.1: Documented.
* Version 282.txr-282Kaz Kylheku2022-09-161-1/+1
| | | | | | | | | | | | * RELNOTES: Updated. * configure (txr_ver): Bumped version. * stdlib/ver.tl (lib-version): Bumped. * txr.1: Bumped version and date. * txr.vim, tl.vim: Regenerated.
* compiler: bug: bad basic-block merge across end insn.Kaz Kylheku2022-09-153-6/+12
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The bad situation reproduced as a miscompilation of some prof forms at *opt-level* 5 or above. The basic idea is that there is a situation like this prof t2 ... profiled code here producing value in t8 mov t2 t8 end t2 end t2 The code block produces a value in t8, which is copied into t2, and executes the end instruction. This instruction does not fall through to the next one but passes control back to the prof instruction. The prof instruction then stores the result value, which came from t2, back into the t2 register and resumes the program at the end t2. The first bad thing that happens is that the end instructions get merged together into one basic block. The optimizer then treats them without regard for the prof instruction, as if they were a linear sequence. It looks like the register move mov t2 t8 is wasteful and so it eliminates it, rewriting the end instruction to: end t8 end t8 Of course, the second instruction is now wrong because prof is still producing the result in t2. To fix this without changing the instruction set, I'm introducing another pseudo-op that represents end, called xend. This is similar to jend, except that jend is regarded as an unconditional branch whereas xend isn't. The special thing about xend is that a basic block in which it occcurs is marked as non-joinable. It will not be joined with the following basic block. * stdlib/asm.tl (xend): New alias opcode for end. * stdlib/compiler.tl (comp-prof): Use xend to end prof fragment, rather than plain end. * stdlib/optimize.tl (basic-block): New slot, nojoin. If true, block cannot be joined with next one. (basic-blocks jump-ops): Add xend to list of jump ops, so that a basic block will terminate on xend. (basic-blocks link-graph): Set the nojoin flag on a basic block which contains (and thus ends with) xend. (basic-blocks local-liveness): Add xend to the case in def-ref that handles end. (basic-blocks (peephole, join-blocks)): Refuse to join blocks marked nojoin. * tests/019/comp-bugs.tl: New file with miscompiled test case that was returning 42 instead of (42 0 0 0) as a result of the wrong register's value being returned.
* compiler: bug: scoping of lambda optionals.Kaz Kylheku2022-09-151-17/+15
| | | | | | | | | | | | | | | | | | | | | | The scoping is not behind handled correctly for optional variables. The init-forms are being evaluated in a scope in which all the variables are already visible, instead of sequentially. Thus, for instance, variable rebinding doesn't work, as in (lambda (: (x x)) ...). When the argument is missing, x ends up with the value : because the expression refers to the new x, rather than the outer x. * stdlib/compiler.tl (compiler comp-lambda-impl): Perform the compilation of the init-forms earlier. Use the same new trick that is used for let*: the target for the code fragment is a locaton obtained from get-loc, which is then attached to a variable afterward. The spec-sub helper is extended with a loc parameter to help with this case. * tests/012/lambda.tl: New test case that fails without this fix.
* compiler: eliminate rename-var hack.Kaz Kylheku2022-09-151-13/+6
| | | | | | | | | | | | | | | * stdlib/compiler.tl (env rename-var): Method removed. (compiler comp-let): Instead of initially creating a let* variable as a gensym, and then renaming it after compiling the init expression, we now just obtain the location not bound to a variable, use the location when compiling the init form, and bind the location to a variable right after. This is cleaner since the only thing we are mutating now is the environment, and we are not wastefully allocating a gensym. The real motivation is that this is building up to a bugfix in compiling optional variables in lambda: stay tuned!
* compiler: unbundle v-reg allocation from env extensionKaz Kylheku2022-09-151-6/+10
| | | | | | | | | | | | * stdlib/compiler.tl (env get-loc): New method for allocating v-reg, split out of extend-var and extend-var*. Now there is a check for the v-cntr overflow. (env (extend-var, extend-var*)): Taken an optional loc parameter, so the caller can optionally allocate a v-reg location using get-loc, and then specify that location when creating a variable. If the argument is omitted, use get-loc.
* compiler: eliminate uses of cdar.Kaz Kylheku2022-09-141-7/+9
| | | | | | | | | * stdlib/compiler.tl (env (extend-var, extend-var*)): Return the variable binding rather than the alist containing it. (compiler (comp-catch, comp-let, comp-tree-case)): Drop use of cdar on return value of extend-var to ferret out the binding from the alist.
* compiler: bugfixes in dead code eliminationKaz Kylheku2022-09-131-2/+2
| | | | | | | | | | | | | | | | | | | | | | * stdlib/optimize (basic-blocks ling-graph): I'm reverting an old design decision here. The decision is this: the basic block of a close instruction points to the first basic block of the closure as its next block, but that next block does not point back: it doesn't list the close instruction's basic block among the rlinks. The idea was that the close instruction doesn't jump to that block, and so it shouldn't be linked to it. However, the next link was set purely so that the graph is connected. Unfortunately, the inconsistency in the graph structure which this causes is a problem in the elim-dead-code method. A situation arises when that first basic block after the close is removed. Because pit has an empty rlinks list, the block remains listed as the next block of the close block, even though it is removed from the master list of blocks. (basic-blocks check-bypass-empty): Fix one forgotten detail in this function: the block being deleted must be removed from the rlinks list of the next block.
* Version 281.txr-281Kaz Kylheku2022-09-031-1/+1
| | | | | | | | | | | | | | * RELNOTES: Updated. * configure (txr_ver): Bumped version. * stdlib/ver.tl (lib-version): Bumped. * txr.1: Bumped version and date. * txr.vim, tl.vim: Regenerated. * protsym.c: Regenerated.
* New macro: close-lazy-streams.Kaz Kylheku2022-08-282-0/+7
| | | | | | | | | | | | | | | | | | | | | | | | * lib.c (lazy_stream_s): New symbol variable. (lazy_streams_binding): New static variable. (lazy_stream_register): New static function (lazy_stream_cons): If the stream is associated with a lazy cons, register it with lazy_stream_register. (obj_init): gc-protect lazy_streams_binding variable. Intern the sys:*lazy-streams* symbol. * lib.h (lazy_streams_s): Declared. * eval.c (eval_init): Register sys:*lazy-streams* special variable. * stdlib/getput.tl (close-lazy-streams): New macro. * autoload.c (getput_set_entries): Trigger autload on close-lazy-streams symbol. * txr.1: Documented. * stdlib/doc-syms.tl: Updated.
* New function: search-allKaz Kylheku2022-08-171-0/+1
| | | | | | | | | | | | | | | | | | | | | | | * eval.c (eval_init): search-all intrinsic registered. * lib.c (search_common): New Boolean argument all, indicating whether all positions are to be returned. We must handle this in the two places where empty key and sequence are handled, and also in the main loop. A trick is used: the found variable is now bound by list_collect_decl, but not used for collecting unless all is true. (search, rsearch, contains): Pass 0 for all argument of search_common. (search_all): New function. * lib.h (search_all): Declared. * tests/012/seq.tl: New tests. * txr.1: Documented. * stdlib/doc-syms.tl: Regenerated.
* Version 280.txr-280Kaz Kylheku2022-08-091-1/+1
| | | | | | | | | | * RELNOTES: Updated. * configure (txr_ver): Bumped version. * stdlib/ver.tl (lib-version): Bumped. * txr.1: Bumped version and date.
* Version 279.txr-279Kaz Kylheku2022-08-081-1/+1
| | | | | | | | | | | | * RELNOTES: Updated. * configure (txr_ver): Bumped version. * stdlib/ver.tl (lib-version): Bumped. * txr.1: Bumped version and date. * txr.vim, tl.vim: Regenerated.
* path-components-safe: proc tests only on Linux.Kaz Kylheku2022-07-301-18/+20
| | | | | | | * stdlib/path-test.tl (safe-abs-path): If (uname) doesn't report Linux, then define this function in a way that it always returns true. We do this by making the name an alias for the tf function.
* path-components-safe: check symlink link count.Kaz Kylheku2022-07-301-2/+3
| | | | | | * stdlib/path-test.tl (path-components-safe): Reject symlinks that have a link count not equal to one. This looks suspiciously like a hard link attack.
* path-components-safe: tighten /proc checkKaz Kylheku2022-07-301-15/+14
| | | | | | | | | | | | | | Attacks are possible via /proc/<pid>/fd/<n> involving a deleted file, whereby the link target changes from "/path/to/file" to "/path/to/file (deleted)", which can be perpetrated by a different user, not related to process <pid>, who has access to perform unlink("/path/to/file"). * stdlib/path-test.tl (safe-abs-path): Perform the pattern check regardless of effective user ID. * tests/018/path-safe.tl: Test cases adjusted.
* path-components-safe: handle consecutive slashes.Kaz Kylheku2022-07-301-4/+2
| | | | | * stdlib/path-test (path-components-safe): Remove empty components from split path.
* path-components-safe: repel /proc symlink attacksKaz Kylheku2022-07-291-17/+42
| | | | | | | | | | | | | | | | | | | | | | In a Linux system, it's possible for an unprivileged user to create a root symlink pointing to any directory, simply by changing to that directory and running a setuid executable like "su". That executable will get a process whose /proc/<pid> directory is root owned, and contains a symlink named cwd pointing to the current directory. Other symlinks under /proc look exploitable in this way. * stdlib/path-test.tl (safe-abs-path): New function. Here is where we are going to check for unsafe paths. We use some pattern matching to recognize various unsafe symlinks under /proc. (path-components-safe): Simplify code around recognition of absolute paths. When an absolute path is read from a symlink, remove the first empty component. Pass every absolute path through safe-abs-path to check for known unsafe paths. * tests/018/path-safe.tl: New tests.
* rel-path, path-equal: relocate.Kaz Kylheku2022-07-292-105/+94
| | | | | | | | * stdlib/copy-file.tl (path-simplify, path-split, path-volume, rel-path, path-equal): Remove from here. * stdlib/path-test.tl: (path-simplify, path-split, path-volume, rel-path, path-equal): Move to here.
* in6addr-str: remove useless regsub.Kaz Kylheku2022-07-271-1/+0
| | | | | | | | * stdlib/socket.tl (in6addr-str): Remove one of the two repetitions of a string substitution intended to be done once. The substitution is idempotent and therefore a second application of it is redundant regardless of intent.
* Take advantage of substring support in regsub.Kaz Kylheku2022-07-273-4/+4
| | | | | | | | | | | | | | | | * txr.c (sysroot_init): Use regsub to look for "\\" substring instead of regex. * stdlib/getopts.tl (opt-parsed convert-type): regsub for "0x" substring rather than #/0x/ regex. * stdlib/pic.tl (pic-join-oipt): regsub for "~" substring rather than #/\~/ regex. * stdlib/socket.tl (in6addr-str): regsub for "::" substring instead of #/::/ regex in two places. The double regsub there looks like a mistake; will address in another commit.
* path-components-safe: refactoring.Kaz Kylheku2022-07-251-55/+37
| | | | | | | | * stdlib/path-test.tl (path-components-safe): Simplify code; forget trying to do anything on Windows: just return true. * txr.1: Document that path-components-safe is useless on Windows.
* New function: path-components-safe.Kaz Kylheku2022-07-252-0/+73
| | | | | | | | | | | | | | | | * autoload.c (path_test_set_entries): Autoload on path-components-safe symbol. * stdlib/path-test.tl (if-windows, if-native-windows): New system macros. (path-safe-sticky-dir): New system function. (path-components-safe): New function. * tests/018/path-safe.tl: New file.' * txr.1: Documented. * stdlib/doc-syms.tl: Updated.