]> git.ipfire.org Git - thirdparty/freeradius-server.git/log
thirdparty/freeradius-server.git
14 months agoUpdate sample rlm_python config
Nick Porter [Mon, 21 Apr 2025 16:32:20 +0000 (17:32 +0100)] 
Update sample rlm_python config

14 months agoUpdate example.py
Nick Porter [Mon, 21 Apr 2025 15:07:56 +0000 (16:07 +0100)] 
Update example.py

14 months agoUpdate Python test function names
Nick Porter [Mon, 21 Apr 2025 09:24:39 +0000 (10:24 +0100)] 
Update Python test function names

14 months agoUse call_env to identify Python function names
Nick Porter [Mon, 21 Apr 2025 09:06:24 +0000 (10:06 +0100)] 
Use call_env to identify Python function names

Firstly a function `<name1>_<name2>` is looked for.  If that is not
found (or there is no `name2`, then `<name1>` is looked for.

The Python module can be set with config option `mod_name1_name2` or
`mod_name1`.

The function can be overridden with config option `func_name1_name2` or
`func_name1`.

`name1` and `name2` are converted to lower case and any
non-alphanumercic characters are converted to underscores.

e.g. a module call in `recv Access-Request` will look for
`recv_accsss_request` and then `recv`.

14 months agoAdd tests of rlm_python attribute setting
Nick Porter [Fri, 18 Apr 2025 13:12:38 +0000 (14:12 +0100)] 
Add tests of rlm_python attribute setting

14 months agoOnly accept an rcode in the Python function reply
Nick Porter [Fri, 18 Apr 2025 08:14:19 +0000 (09:14 +0100)] 
Only accept an rcode in the Python function reply

Python functions can now directly amend attribute lists

14 months agoPass py_freeradius_request_t to python functions
Nick Porter [Fri, 18 Apr 2025 07:45:26 +0000 (08:45 +0100)] 
Pass py_freeradius_request_t to python functions

In place of the tuple representation of the request list

14 months agoUpdate Python module init
Nick Porter [Fri, 18 Apr 2025 07:17:39 +0000 (08:17 +0100)] 
Update Python module init

To add type definitions and add new objects

14 months agoCreate py_freeradius_state_t when instantiating thread instances
Nick Porter [Wed, 16 Apr 2025 17:37:10 +0000 (18:37 +0100)] 
Create py_freeradius_state_t when instantiating thread instances

14 months agoRemove Python 2.x comment
Nick Porter [Wed, 16 Apr 2025 16:09:20 +0000 (17:09 +0100)] 
Remove Python 2.x comment

14 months agoAdd py_freeradius_pair_str
Nick Porter [Wed, 16 Apr 2025 16:07:30 +0000 (17:07 +0100)] 
Add py_freeradius_pair_str

Get the string representation of a pair

14 months agoAdd py_freeradius_pair_setvalue
Nick Porter [Wed, 16 Apr 2025 16:04:49 +0000 (17:04 +0100)] 
Add py_freeradius_pair_setvalue

The setter called when a value is assigned to a pair's value attribute.

14 months agoAdd py_freeradius_pair_getvalue
Nick Porter [Wed, 16 Apr 2025 15:57:53 +0000 (16:57 +0100)] 
Add py_freeradius_pair_getvalue

The getter callback when the .value attribute of a pair is requested

14 months agoAdd py_freeradius_pair_map_set
Nick Porter [Wed, 16 Apr 2025 15:56:19 +0000 (16:56 +0100)] 
Add py_freeradius_pair_map_set

Which handles assigning string values to pair objects accessed in dict
syntax and deleting pair objects.

14 months agoAdd py_freeradius_attribute_instance
Nick Porter [Wed, 16 Apr 2025 11:47:57 +0000 (12:47 +0100)] 
Add py_freeradius_attribute_instance

Used to access a specific instance of an attribute

14 months agoAdd py_freeradius_pair_map_subscript()
Nick Porter [Wed, 16 Apr 2025 11:46:50 +0000 (12:46 +0100)] 
Add py_freeradius_pair_map_subscript()

Called when pair objects are accessed as dictionaries.

14 months agoAdd definitions of Python objects for pair access
Nick Porter [Wed, 16 Apr 2025 11:42:14 +0000 (12:42 +0100)] 
Add definitions of Python objects for pair access

And associated function prototypes

14 months agoAdd functions to set and get current request from Python thread state
Nick Porter [Wed, 16 Apr 2025 10:35:53 +0000 (11:35 +0100)] 
Add functions to set and get current request from Python thread state

14 months agoUpdate Python freeradius.log() to use optional args
Nick Porter [Wed, 16 Apr 2025 10:24:53 +0000 (11:24 +0100)] 
Update Python freeradius.log() to use optional args

And move module definition to global static

14 months agoAdd functions to fetch module instance from python state
Nick Porter [Wed, 16 Apr 2025 10:23:02 +0000 (11:23 +0100)] 
Add functions to fetch module instance from python state

The dict `__State` entry will be added later

14 months agoNo need to talk about Python 2
Nick Porter [Wed, 16 Apr 2025 09:58:01 +0000 (10:58 +0100)] 
No need to talk about Python 2

14 months agoAdd C structure for Python state object
Nick Porter [Wed, 16 Apr 2025 09:56:31 +0000 (10:56 +0100)] 
Add C structure for Python state object

14 months agoDefine C structures for Python request and pair objects
Nick Porter [Wed, 16 Apr 2025 09:47:34 +0000 (10:47 +0100)] 
Define C structures for Python request and pair objects

14 months agoDon't call python_error_log twice
Nick Porter [Wed, 16 Apr 2025 09:44:23 +0000 (10:44 +0100)] 
Don't call python_error_log twice

It is called after `finish`

14 months agoChange LOG_PREFIX to use inst->name
Nick Porter [Wed, 16 Apr 2025 08:34:38 +0000 (09:34 +0100)] 
Change LOG_PREFIX to use inst->name

This is simpler to work with than mctx, when logging from python
callback functions.

In addition using `current_inst` in place of `current_mctx` avoids
setting a global to a stack allocated variable.

14 months agoSet module instance name in instance data
Nick Porter [Wed, 16 Apr 2025 08:19:34 +0000 (09:19 +0100)] 
Set module instance name in instance data

14 months agoThe module name is freeradius
Nick Porter [Tue, 15 Apr 2025 16:54:59 +0000 (17:54 +0100)] 
The module name is freeradius

14 months agoSwitch GitHub runners to Ubuntu 24.04 to match self-hosted
Nick Porter [Fri, 18 Apr 2025 14:50:15 +0000 (15:50 +0100)] 
Switch GitHub runners to Ubuntu 24.04 to match self-hosted

14 months agoUse sh in place of bash
Nick Porter [Mon, 21 Apr 2025 10:58:14 +0000 (11:58 +0100)] 
Use sh in place of bash

For cross platform tests

14 months agomake timeout limits warnings, and limit to max_request_time
Alan T. DeKok [Sat, 19 Apr 2025 13:46:23 +0000 (09:46 -0400)] 
make timeout limits warnings, and limit to max_request_time

14 months agoadd headings for accountng, and add docs for timeout section
Alan T. DeKok [Sat, 19 Apr 2025 13:43:32 +0000 (09:43 -0400)] 
add headings for accountng, and add docs for timeout section

14 months agofix nav bar indentation
Alan T. DeKok [Sat, 19 Apr 2025 13:43:18 +0000 (09:43 -0400)] 
fix nav bar indentation

14 months agoadd test for top-level "timeout" functionality
Alan T. DeKok [Sat, 19 Apr 2025 13:20:27 +0000 (09:20 -0400)] 
add test for top-level "timeout" functionality

14 months agoallow time_delta as .3, in addition to 0.3
Alan T. DeKok [Sat, 19 Apr 2025 13:15:37 +0000 (09:15 -0400)] 
allow time_delta as .3, in addition to 0.3

14 months agoFix typos in CI #5542
Alan T. DeKok [Sat, 19 Apr 2025 13:05:37 +0000 (09:05 -0400)] 
Fix typos in CI #5542

14 months agoUpdate dictionary. fixes ##5545
Alan T. DeKok [Sat, 19 Apr 2025 13:01:24 +0000 (09:01 -0400)] 
Update dictionary.  fixes ##5545

14 months agoremove duplicate includes
Alan T. DeKok [Sat, 19 Apr 2025 12:52:45 +0000 (08:52 -0400)] 
remove duplicate includes

By running scripts.build/min-includes.pl, and double-checking
the output.

The script should be updated to ignore things inside of #ifdef's,
as otherwise it will remove too many includes.

14 months agolimit timers
Alan T. DeKok [Fri, 18 Apr 2025 20:12:03 +0000 (16:12 -0400)] 
limit timers

14 months agoFix issue in extendable talloced sbuffs, that would cause the sbuff to slowly shrink
Arran Cudbard-Bell [Fri, 18 Apr 2025 20:43:33 +0000 (15:43 -0500)] 
Fix issue in extendable talloced sbuffs, that would cause the sbuff to slowly shrink

14 months agoExtend end on talloc_extend
Arran Cudbard-Bell [Fri, 18 Apr 2025 20:21:08 +0000 (15:21 -0500)] 
Extend end on talloc_extend

14 months agomake global "timeout" section work
Alan T. DeKok [Fri, 18 Apr 2025 18:51:22 +0000 (14:51 -0400)] 
make global "timeout" section work

with some tweaks to unlang_timeout_section_push() to make it work
and indent the request correctly.

14 months agolook for and compile a per-server "timeout" section
Alan T. DeKok [Fri, 18 Apr 2025 17:51:55 +0000 (13:51 -0400)] 
look for and compile a per-server "timeout" section

14 months agodon't insert static clients into the dynamic client list
Alan T. DeKok [Fri, 18 Apr 2025 18:37:20 +0000 (14:37 -0400)] 
don't insert static clients into the dynamic client list

14 months agodistinguish frame_signal and interpret_signal
Alan T. DeKok [Fri, 18 Apr 2025 17:23:51 +0000 (13:23 -0400)] 
distinguish frame_signal and interpret_signal

when we hit a timeout, we signal the frames to cancel.  If pushing
the timer section onto the stack fails, we signal the entire
request to cancel.

14 months agoPrint errors when we fail copy log messages to the aggregation buffer
Arran Cudbard-Bell [Fri, 18 Apr 2025 17:02:03 +0000 (12:02 -0500)] 
Print errors when we fail copy log messages to the aggregation buffer

14 months agoadd unlang_timeout_section_push()
Alan T. DeKok [Fri, 18 Apr 2025 15:41:55 +0000 (11:41 -0400)] 
add unlang_timeout_section_push()

not overly tested, but the actual timeout handler only has minor
changes from the existing one.

14 months agohoist tmpls
Alan T. DeKok [Fri, 18 Apr 2025 12:40:11 +0000 (08:40 -0400)] 
hoist tmpls

if a tmpl is asked to parse %{...}. then the result is TMPL_TYPE_XLAT.

However, if the contents of that xlat are identically a tmpl, e.g.
User-Name, then there is no reason to keep the xlat.  Instead, we
hoist the tmpl we really want, and discard the intermediate xlat

this simplifies the internal data structures, and allows for
additional optimizations.  It also means that we do less work
at run-time in order to expand the tmpl.

14 months agoprint out that it's a tmpl
Alan T. DeKok [Fri, 18 Apr 2025 12:39:32 +0000 (08:39 -0400)] 
print out that it's a tmpl

14 months agoremove backticks from tests
Alan T. DeKok [Thu, 17 Apr 2025 19:53:41 +0000 (15:53 -0400)] 
remove backticks from tests

And clean up corresponding use of exec module.  We hoist it to
unit_test_module.conf, and then replace any module-specific
configuration with a module-specific instant of exec

14 months agoUse a function actually in libmruby to check for the library
Nick Porter [Fri, 18 Apr 2025 09:10:41 +0000 (10:10 +0100)] 
Use a function actually in libmruby to check for the library

14 months agoMore fixes for OpenSSL's questionable design decisions
Arran Cudbard-Bell [Fri, 18 Apr 2025 00:57:34 +0000 (19:57 -0500)] 
More fixes for OpenSSL's questionable design decisions

Always drain the thread local error stack before calling SSL_read() and SSL_write() to prevent stale errors from masking SSL_ERROR_WANT_* return codes from SSL_get_error().

14 months agoAdd "ping" xlat for winbind, so we can run periodic checks against AD
Arran Cudbard-Bell [Wed, 16 Apr 2025 19:51:35 +0000 (14:51 -0500)] 
Add "ping" xlat for winbind, so we can run periodic checks against AD

14 months agoCache more things, and only unprotect the protected page
Arran Cudbard-Bell [Wed, 16 Apr 2025 18:23:41 +0000 (13:23 -0500)] 
Cache more things, and only unprotect the protected page

14 months agoFix timer use in rlm_sql_oracle
Arran Cudbard-Bell [Wed, 16 Apr 2025 17:03:09 +0000 (12:03 -0500)] 
Fix timer use in rlm_sql_oracle

14 months agoSetup custom fibre allocators for OpenSSL with a guard page
Arran Cudbard-Bell [Wed, 16 Apr 2025 17:00:09 +0000 (12:00 -0500)] 
Setup custom fibre allocators for OpenSSL with a guard page

14 months agoadd "exceed_mtu" configuration flag for UDP sockets.
Alan T. DeKok [Wed, 16 Apr 2025 15:58:19 +0000 (11:58 -0400)] 
add "exceed_mtu" configuration flag for UDP sockets.

which defaults to "yes".

When set to "yes", clears the "don't fragment" bit in the IP
packet header.

When set to "no", it uses the OS definition for the DF bit.
Note that we do NOT set the DF bit.  OSX supports IP_DONTFRAG
only for raw sockets.  Linux always sets the DF flag for UDP
sockets.

No matter what this flag is set to, there are some situations
where UDP packets will silently disappear in the network.
When DF is set, "too large" packets might get an ICMP error
to the OS, which the server will currently ignore.
When DF is clear, "too large" packets might be silently discarded
by some other network element.

We can later add code to do actual PMTU discovery

14 months agomove files to a better location
Alan T. DeKok [Wed, 16 Apr 2025 15:37:11 +0000 (11:37 -0400)] 
move files to a better location

14 months agodocs: Add v4 proxy information back into Antora from freeradius.org wiki
nolade [Tue, 15 Apr 2025 15:42:10 +0000 (11:42 -0400)] 
docs: Add v4 proxy information back into Antora from freeradius.org wiki

14 months agoupdate handling of PMTU and EMSGSIZE
Alan T. DeKok [Wed, 16 Apr 2025 15:27:29 +0000 (11:27 -0400)] 
update handling of PMTU and EMSGSIZE

We disable PMTU discovery for the client code too, and not just
for the server code.

Add notes explaining what else needs to be done in order to fully
support PTMUD

update the write code to handle EMSGSIZE.  This should arguably
be done only for connected UDP sockets.

14 months agouse correct RFC
Alan T. DeKok [Wed, 16 Apr 2025 11:31:12 +0000 (07:31 -0400)] 
use correct RFC

14 months agorearrange code to be simpler
Alan T. DeKok [Wed, 16 Apr 2025 11:12:34 +0000 (07:12 -0400)] 
rearrange code to be simpler

14 months agoremove variable for xlat_func_bare_words
Alan T. DeKok [Wed, 16 Apr 2025 10:37:55 +0000 (06:37 -0400)] 
remove variable for xlat_func_bare_words

14 months agoremove transitional settings for xlat_func_bare_words
Alan T. DeKok [Wed, 16 Apr 2025 10:32:48 +0000 (06:32 -0400)] 
remove transitional settings for xlat_func_bare_words

14 months agominor cleanups to remove xlat_func_bare_words
Alan T. DeKok [Wed, 16 Apr 2025 10:31:23 +0000 (06:31 -0400)] 
minor cleanups to remove xlat_func_bare_words

and add name to node which previously didn't have it.  That
changed the test output (for the better), so we update it, too

14 months agoremove -S xlat_func_bare_words=no from tests
Alan T. DeKok [Wed, 16 Apr 2025 01:28:28 +0000 (21:28 -0400)] 
remove -S xlat_func_bare_words=no from tests

14 months agoScheduled fuzzing: Update src/tests/fuzzer-corpus/der.tar
github-actions[bot] [Wed, 16 Apr 2025 09:34:55 +0000 (09:34 +0000)] 
Scheduled fuzzing: Update src/tests/fuzzer-corpus/der.tar

14 months agoScheduled fuzzing: Update src/tests/fuzzer-corpus/cbor.tar
github-actions[bot] [Wed, 16 Apr 2025 09:34:52 +0000 (09:34 +0000)] 
Scheduled fuzzing: Update src/tests/fuzzer-corpus/cbor.tar

14 months agouse the new xlat argument parser
Alan T. DeKok [Tue, 15 Apr 2025 13:30:23 +0000 (09:30 -0400)] 
use the new xlat argument parser

14 months agohandle non-string arguments to Perl xlat function
Alan T. DeKok [Tue, 15 Apr 2025 19:33:42 +0000 (15:33 -0400)] 
handle non-string arguments to Perl xlat function

by the simple expedient of mangling them to a string

14 months agoquote strings in module tests
Alan T. DeKok [Tue, 15 Apr 2025 15:36:52 +0000 (11:36 -0400)] 
quote strings in module tests

14 months agoset is_argv if the function will do it's own escaping
Alan T. DeKok [Tue, 15 Apr 2025 13:10:00 +0000 (09:10 -0400)] 
set is_argv if the function will do it's own escaping

and update rlm_rest with the will_escape flag

14 months agoadd "will_escape" for xlat args, and have LDAP use it
Alan T. DeKok [Tue, 15 Apr 2025 10:33:19 +0000 (06:33 -0400)] 
add "will_escape" for xlat args, and have LDAP use it

rlm_ldap does it's own escaping, which is unusual

14 months agoquote bare words in redis ippool tool tests
Alan T. DeKok [Mon, 14 Apr 2025 15:27:38 +0000 (11:27 -0400)] 
quote bare words in redis ippool tool tests

14 months agodoc: import customer doc EAP Certs - updated links to relevant sections (HIVE 3404)
nolade [Fri, 11 Apr 2025 19:56:29 +0000 (15:56 -0400)] 
doc: import customer doc EAP Certs - updated links to relevant sections (HIVE 3404)

14 months agoalloc call.args
Alan T. DeKok [Mon, 14 Apr 2025 13:21:28 +0000 (09:21 -0400)] 
alloc call.args

14 months agoadd nonnull
Alan T. DeKok [Mon, 14 Apr 2025 12:55:08 +0000 (08:55 -0400)] 
add nonnull

to hopefully get more information about UBSAN issues in CI OSX

14 months agoallow dict to be NULL when setting the function
Alan T. DeKok [Mon, 14 Apr 2025 11:23:30 +0000 (07:23 -0400)] 
allow dict to be NULL when setting the function

if so, also set needs_resolving.

Because an xlat can be parsed from a module configuration, where
the dictionary is unknown.  Until such time as all of the modules
are converted to use call_env, this is likely the best approach.

14 months agomark safe_for recursively
Alan T. DeKok [Mon, 14 Apr 2025 10:57:51 +0000 (06:57 -0400)] 
mark safe_for recursively

now that we have strings as groups

14 months agomove xalt func code into common routines
Alan T. DeKok [Mon, 14 Apr 2025 01:01:26 +0000 (21:01 -0400)] 
move xalt func code into common routines

for less code duplication, along with other cleanups

14 months agoturn checks into explict comparisons
Alan T. DeKok [Sun, 13 Apr 2025 21:52:45 +0000 (17:52 -0400)] 
turn checks into explict comparisons

otherwise in some uses, the checks won't match.

14 months agoremove TMPL_TYPE_NULL
Alan T. DeKok [Sun, 13 Apr 2025 15:27:24 +0000 (11:27 -0400)] 
remove TMPL_TYPE_NULL

it was only used for =* and !*. Those have been updated to use
ANY on the RHS

14 months agoclean up attr_filter and map_to_vp()
Alan T. DeKok [Sun, 13 Apr 2025 15:17:03 +0000 (11:17 -0400)] 
clean up attr_filter and map_to_vp()

only attr_filter passes comparison operators to map_to_vp()
we should arguably forbid comparison operators, as allowing them
could break other modules.

Also disallow comparison operators in map_to_request, as nothing
uses them.

14 months agomore cleanups for map_exec_to_vp
Alan T. DeKok [Sun, 13 Apr 2025 14:58:17 +0000 (10:58 -0400)] 
more cleanups for map_exec_to_vp

remove unused variable.  Note that it does NOT parse RHS strings
as pairs, for assignment to a LHS list.

14 months agono need to switch over LHS type when we know what it is
Alan T. DeKok [Sun, 13 Apr 2025 13:53:39 +0000 (09:53 -0400)] 
no need to switch over LHS type when we know what it is

14 months agoallow for suppression of structural attributes, too
Alan T. DeKok [Sun, 13 Apr 2025 11:16:24 +0000 (07:16 -0400)] 
allow for suppression of structural attributes, too

and remove "compat" flag.  If the admin wants to suppress printing
of the Packet-Type attribute, they can list it in the suppress
block.

14 months agoremove attr_protocol and attr_user_password
Alan T. DeKok [Sun, 13 Apr 2025 11:08:57 +0000 (07:08 -0400)] 
remove attr_protocol and attr_user_password

attr_protocol is now unused, and attr_user_password will be
replaced with better choices

14 months agomove request->dict to proto_dict and local_dict
Alan T. DeKok [Sat, 12 Apr 2025 18:44:00 +0000 (14:44 -0400)] 
move request->dict to proto_dict and local_dict

in order to split uses of base protocol dictionary, and
dictionary with local variables.

The protocol dictionary is used for encoding / decoding,
including xlats, and attributes read from external modules such as SQL or LDAP.

The local dictionary is used for local variables

it is saved, updated, and restored every time the interpreter
defines a local attribute

update request_init() to set dict to internal if it isn't passed
in.

update nearly all references to run-time parsing from request->dict
to request->local_dict.  Only the protocol encoders are left
unchanged.

this means that maps, %debug() etc. can now reference local
attributes, which they couldn't before.

update %eval() to use the local dict, too.

update fr_listen_t to have a dict, so the worker thread can use it.

and don't set request->dict in the listen decode any more.

14 months agoremove references to request->dict
Alan T. DeKok [Sun, 13 Apr 2025 09:58:37 +0000 (05:58 -0400)] 
remove references to request->dict

we can't change protocols when reading a request off of disk

14 months agoremove unused assignment
Alan T. DeKok [Sat, 12 Apr 2025 20:39:34 +0000 (16:39 -0400)] 
remove unused assignment

14 months agothere's no need for INPUT_ARGS
Alan T. DeKok [Sat, 12 Apr 2025 17:30:31 +0000 (13:30 -0400)] 
there's no need for INPUT_ARGS

there's now only one type of function, as opposed to the
transitional MONO and INPUT_ARGS.  So we remove the redundant
field and update the associated checks

14 months agoEnsure the GIL is locked before calling Py_XDECREF
Nick Porter [Sat, 12 Apr 2025 18:26:03 +0000 (19:26 +0100)] 
Ensure the GIL is locked before calling Py_XDECREF

14 months agoRevert "We never manually incref on the module"
Nick Porter [Sat, 12 Apr 2025 18:10:51 +0000 (19:10 +0100)] 
Revert "We never manually incref on the module"

This reverts commit 798de18ec34c1ea49f2d5d6041f779c4760d41bd.

Actually we do need to do this

14 months agoquiet static analyis
Alan T. DeKok [Sat, 12 Apr 2025 15:32:26 +0000 (11:32 -0400)] 
quiet static analyis

14 months agosimplify setting of xlat flags
Alan T. DeKok [Sat, 12 Apr 2025 13:25:45 +0000 (09:25 -0400)] 
simplify setting of xlat flags

move more to common functions to avoid ad-hoc setting

14 months agomove common code to tokenize word
Alan T. DeKok [Sat, 12 Apr 2025 12:56:35 +0000 (08:56 -0400)] 
move common code to tokenize word

14 months agoprint out more flags
Alan T. DeKok [Sat, 12 Apr 2025 12:39:15 +0000 (08:39 -0400)] 
print out more flags

14 months agomove tokenize regex
Alan T. DeKok [Sat, 12 Apr 2025 12:09:57 +0000 (08:09 -0400)] 
move tokenize regex

from expr to tokenize_word, in preparation for moving more over

14 months agodocs: added eap certificates to Introduction >> Best Practices >> EAP Certificates...
nolade [Wed, 9 Apr 2025 16:10:05 +0000 (12:10 -0400)] 
docs: added eap certificates to Introduction >> Best Practices >> EAP Certificates (customer doc import from salt)

14 months agocorrect error offsets for test.xlat
Alan T. DeKok [Fri, 11 Apr 2025 18:00:56 +0000 (14:00 -0400)] 
correct error offsets for test.xlat

14 months agobetter errors
Alan T. DeKok [Fri, 11 Apr 2025 17:50:27 +0000 (13:50 -0400)] 
better errors

print which brace is missing ')' or '}'

double-check and fix error locations