When the struct stat was used for both calling the actual stat and for
returning the result, no copying was needed. This also had the side
effect that for the first call of cached_stat, the returned struct stat
included all the fields properly filled in, and on later calls, these
fields were all zeroed out.
These two variables are separate now, thus the fields need to be copied
explicitly. There are no existing unit tests for this, but ./build.sh
failed reliably.
Only st_mtime and st_mode are actually filled, the remaining fields had
been set to zero. To prevent these from ever being accessed, a custom
struct make_stat replaces the previously used struct stat.
The fields in struct make_stat are intentionally named different from
the fields in struct stat because NetBSD and some other operating
systems define st_mtime as a macro, and that would not work in a field
declaration.
Back in the 1980s it made sense to have the type information encoded in
the variable names. At the time when make was imported into the NetBSD
tree (1993-03-21), the functions did indeed not have prototypes, they
only had return types. The void type was already invented at that time.
Since the compiler could not verify the types of function parameters, it
made perfect sense to have each variable tell whether it was a pointer
or not.
Since ISO C90 this is no longer necessary since the compiler checks
this. The variable names can now focus on the application level and
their high-level meaning, expressing the relationship to other
variables instead of encoding redundant type information.
Lst_Find is called with a "comparison" function that returns the integer
0 if the desired node is found. This leads to confusion since there are
so many different return value conventions for int, such as 0/1 for
mimicking false/true, -1/0 as in close(2), and the sign as in strcmp(3).
This API is much easier to understand if the "comparison" function is
not called a comparison function (since that is too close to strcmp),
but a "match" function that just returns a boolean.
In Lst_FindFromB, the node argument may be null. This deviates from the
other Lst functions, which require Lst and LstNode to generally be
non-null. In this case it is useful though to make the calling code
simpler.
In arch.c, this makes a lot of the previous documentation redundant.
In cond.c, the documentation is reduced a little bit since it had
already been cleaned up before. It also removes the strange negation
from CondFindStrMatch.
In dir.c, the documentation collapses as well.
In main.c, separating the ReadMakefile function from the callbacks for
Lst_FindB allows the former to get back its natural function signature,
with proper types and no unused parameters.
To catch any accidental mistakes during the migration from Lst_Find to
Lst_FindB, the code can be compiled with -DUSE_DOUBLE_BOOLEAN, which
will complain about incompatible function pointer types.
Extract the null check for path to the top level. This has the
side-effect of only incrementing dotLast.refCount if that entry is
actually used.
Reduce the indentation of the code by returning early from the simple
branches.
The migration from null-passing Lst functions to argument-checking Lst
functions is completed.
There were 2 surprises: The targets list may be NULL, and in Dir_AddDir,
the path may be NULL. The latter case is especially surprising since
that function turns into an almost-nop in that case. This is another
case where probably 2 independent functions have been squeezed into a
single function. This may be improved in a follow-up commit.
All other lists were fine. They were always defined and thus didn't
need much work.
In most cases the Lst functions are only called when the arguments are
indeed valid. It's not guaranteed though, therefore each function call
needs to be analyzed and converted individually.
While here, remove a few statements that were only useful when the Lst
functions handled circular lists.
Lst_Duplicate would have passed through any null pointer, which was not
needed for make. It was the last function that used Lst_AtEnd, which in
turn was the last function that used LstInsertAfter. As a result, these
two functions have been removed.
Since the lists don't contain null pointers, it doesn't make sense to
search for a null pointer. All calls but one already had obviously
non-null arguments. The one remaining call using targ->suff has been
guarded for now.
The code for Lst_Member became much simpler than before. Partly because
the old code had an extra condition for circular lists, which are not
used by make.
Except for once instance in parse.c, the usage pattern for Lst_Dequeue
was to first test whether the list is empty. This pattern allowed the
implementation of Lst_Dequeue to become simpler since the null check is
not needed anymore.
The calls to Lst_Enqueue never pass an invalid list or a null pointer,
therefore making them strict was trivial.
This change ensures that there is actually something added to the list.
Lst_AtEnd had silently skipped the addition if the list was invalid
(null pointer), which was not intended in these cases. The "(void)" is
assumed to mean "I know that this cannot fail", while it could also mean
"I don't care whether something actually happened".
Running "./build.sh -j6 tools" still succeeds after this change,
therefore chances are very low that this change breaks anything. If
there is any change, it's an obvious assertion failure. There is no
silent change in behavior though.
The previous variant of using a special case of Lst_InsertAfter was
unnecessarily complicated. Linked lists are a very basic data
structure, and there is no need to overcomplicate things by introducing
unnecessary conditions and branches.
In several places, it just doesn't make sense to have a null pointer
when a list is expected.
In the existing unit tests, the list passed to Lst_Open is always valid,
but that's not a guarantee for real-world usage. Therefore, Lst_Open
has been left for now, and Lst_OpenS is only the preferred alternative
to it.
Up to now, the list library didn't distinguish between programming
mistakes (violations of invariants, illegal parameter values) and
actually interesting situations like "element not found in list".
The current code contains many branches for conditions that are neither
exercised by the unit tests nor by real-world usage. There is no point
in keeping this unnecessary code.
The list functions will be migrated from their lenient variants to the
stricter variants in small parts, each function getting the S suffix
when it is made strict, to avoid any confusion about how strict a
particular function is. When all functions have been migrated, they
will be renamed back to their original names.
While here, the comments of the functions are cleaned up since they
mention irrelevant implementation details in the API comments, as well
as "side effects" that are really main effects.
The list library had probably been imported from a general-purpose
library that also supported circular lists. These are not used by make
though.
After replacing Lst_Init(FALSE) with Lst_Init(), only a single call to
Lst_Init remained with a non-constant argument, and that was in
Lst_Concat, which was to be expected.
This avoids unnecessary string allocations, especially for long lists.
There is no unit test to cover this code. Since this is an obscure,
undocumented part of make, I wasn't able to come up with a unit test.
Therefore I resorted to write a separate testing program to verify the
expected results.
$ cat <<'EOF' >dir_test.c
#include <stdio.h>
#include "make.h"
#include "dir.h"
int main(int argc, char **argv)
{
int i;
Lst lst;
char *flags;
lst = Lst_Init(FALSE);
for (i = 1; i < argc; i++)
Dir_AddDir(lst, argv[i]);
flags = Dir_MakeFlags("-I", lst);
printf("%s\n", flags);
free(flags);
return 0;
}
EOF
# Needs some trivial patches to Makefile and main.c
$ make PROG=dir_test EXTRA_SRCS=dir_test.c EXTRA_CPPFLAGS=-DNO_MAIN \
COPTS.main.c=-Wno-unused-function NOMAN=1
$ ./dir_test a b c
$ mkdir a b c
$ ./dir_test a b c
-Ia -Ib -Ic
$ rmdir a b c
The new functions have a simpler interface, and str_concat3 is even more
general-purpose, since the middle string is no longer required to be
exactly of length 1.