[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
some snippets from make381, 2/2
From: |
Markus Mauhart |
Subject: |
some snippets from make381, 2/2 |
Date: |
Thu, 10 Mar 2005 03:05:05 +0100 |
"Markus Mauhart" wrote ...
>
> during some long ago experiments with make381beta1 sources I've
> gathered some questionable code snippets, some maybe bugs, but
> didnt find the time to discuss them, now I thought better now
> than even later.
> If necessary I can file something to savannah.
> A few others are still to be extracted from my archive.
And here comes the rest. Not included are VMS-specifics, but IIRC
the vms*-files contain great opportunities for innovation ;-)
Regards,
Markus.
*****************************
* bool, filedef.h, implicit.c, dep.h:
"struct file" contains the following bitfield, which is intended as bool:
unsigned int tried_implicit:1;
Such pseudo-bool is very dangerous, cause even " |= 0xfffffe" wouldnt change
the bit. What I mean is line 819 in implicit.h:
dep->file->tried_implicit |= dep->changed;
//Fail-save code:
//if (dep->changed) dep->file->tried_implicit = true;
Who knows whithout expensive checks whether this does the right thing ?
I did expensive checks and still dont know.
'dep->changed' referres to "struct dep"s member "unsigned int changed : 8;"
Here my old comment says:
/* FIXME: sometimes CHANGED is used as RM_xxx-flag set, sometimes as a single
bool "flag". */
/* Problem is that both sorts of "sometimes" are not documented, which makes
reliable */
/* and verifyable usage impossible. So either add this docu, or replace
CHANGED with */
/* two separate items, e.g. [union] {byte rmflags ;bool changed ;} */
If nobody can verify the above "|=", IMHO one should replace it with save code.
Btw, see below w.r.t. remake.c, it really uses the 8-bit dep->changed
as a kind of counting bool.
"struct dep" also contains member "unsigned int ignore_mtime : 1;"
And "struct idep" contains member "unsigned char ignore_mtime;"
AFAICS they are used correctly, nevertheless I'd replace both with type 'bool',
which would make human checks redundant and wont cost a single byte at
runtime (cause of alignment induced padding).
*****************************
* file.c, filedef.h, lookup_file(xptr)
extern struct file *lookup_file PARAMS ((char *name));
My copy contains:
extern struct file *lookup_file PARAMS ((char const *name));
/* often called with string litteral as name, e.g. ".PHONY" */
lookup_file's implementation actually DOES treat its argument as-if "const",
nevertheless to please the compiler's const warning one has to change a few
trivial things:
=> my completely equivalent version of lookup_file(xptr), but with
xptr correctly declared as "const":
struct file *
lookup_file (char const *name)
{
struct file *f;
struct file key;
assert (*name != '\0');
/* This is also done in parse_file_seq, so this is redundant
for names read from makefiles. It is here for names passed
on the command line. */
#ifdef VMS
# ifndef WANT_CASE_SENSITIVE_TARGETS
{
char const* src = name;
char* dst = (char*) alloca (strlen(src) + 1);
name = dst;
for ( ; *src != '\0'; ++src, ++dst)
*dst = isupper ((unsigned char)*src) ? tolower ((unsigned char)*src)
: *src;
*dst = '\0';
}
# endif
while (name[0] == '[' && name[1] == ']' && name[2] != '\0')
name += 2;
#endif
while (name[0] == '.' && name[1] == '/' && name[2] != '\0')
{
name += 2;
while (*name == '/')
/* Skip following slashes: ".//foo" is "foo", not "/foo". */
++name;
}
if (*name == '\0')
/* It was all slashes after a dot. */
#ifdef VMS
name = "[]";
#else
#ifdef _AMIGA
name = "";
#else
name = "./";
#endif /* AMIGA */
#endif /* VMS */
key.hname = (char*) name;
f = (struct file *) hash_find_item (&files, &key);
return f;
}
*******************************
* file.c, enter_file(xptr):
my comment said:
/* Was a bug ... 381beta1 contained such obfuscated VMS handling so that
btw it removed "free(name)" even for the non-VMS case.
*/
AFAICS the original VMS-code seems to have missed that the function-argument
is completely under control of the function -> may be lowercase'd inline.
Wait, now I see that the reason for enter_file's unnecessary VMS-malloc is that
enter_file has been copied&pasted from lookup_file - but lookup_file(xptr)
has semantic so that xptr can be (and often is) a literal (".PHONY"),
while enter_file's xptr has opposite semenatic.
My replacement for the whole function was ...
struct file *
enter_file (char *name)
{
struct file **slot;
assert (*name != '\0');
#if defined(VMS) && !defined(WANT_CASE_SENSITIVE_TARGETS)
{
char *a;
for (a = name; *a != '\0'; ++a)
if (isupper ((unsigned char)*a))
*a = tolower ((unsigned char)*a);
}
#endif
/* 1) Find SLOT of existing or still-to-create FILES entry: */
{
struct file key;
key.hname = name;
slot = (struct file **) hash_find_slot (&files, &key);
if (! HASH_VACANT (*slot) && !(*slot)->double_colon)
{
free (name);
return *slot;
}
}
/* 2) Put new entry into SLOT (or into *SLOT's list): */
{
struct file *a;
a = (struct file *) xmalloc (sizeof (*a));
bzero ((char *) a, sizeof (*a));
a->name = a->hname = name;
a->update_status = -1;
if (HASH_VACANT (*slot))
hash_insert_at (&files, a, slot);
else
{
/* There is already a double-colon entry for this file. */
struct file *f = *slot;
a->double_colon = f;
while (f->prev != 0)
f = f->prev;
f->prev = a;
}
return a;
}
}
**********************************
* file.c
char *
build_target_list (char *value)
{
static unsigned long last_targ_count = 0;
if (files.ht_fill != last_targ_count)
My version says ...
if (files.ht_fill != last_targ_count) /* FIXME ... is this really the
correct indicator ? */
... maybe only a dumb question, but who knows.
**********************************
* file.c:
line 67 defines a currently unused macro ...
#ifndef FILE_BUCKETS
#define FILE_BUCKETS 1007
#endif
... which looks like a perfect replacement for "1000" in line 925:
void
init_hash_files (void)
{
hash_init (&files, 1000, file_hash_1, file_hash_2, file_hash_cmp);
}
*******************
* bool, job.*:
"struct child" has member "unsigned int remote:1;", again an optimized bool.
remote-stub.c contains:
/* Return nonzero if the next job should be done remotely. */
int
start_remote_job_p (int first_p UNUSED)
{
return 0;
}
job.c, line 822, combines both:
c->remote = start_remote_job_p (0);
So IMHO either make the variable and the function-return 'bool', or say ...
c->remote = (start_remote_job_p (0) != 0);
*******************
* bool, job.*, commands.h
commands.h(37): #define COMMANDS_NOERROR 4
job.h: "struct child" has member "unsigned int noerror:1;"
job.c:
flags = (child->file->command_flags
| child->file->cmds->lines_flags[child->command_line - 1]);
p = child->command_ptr;
child->noerror = flags & COMMANDS_NOERROR;
IMHO the last assignment is even optimized away by GCC.
Ok, this now looks so unbelievable wrong for me that probably I'm
missing something and I soon will make a break ;-)
job.c, line 1230:
child->remote = is_remote;
In a bool-less world better say ...
child->remote = (is_remote != 0);
line 2860: "if", "if"
**********************
* main.c
line 602: switch (tolower (p[0]))
Not knowing p's details I would write ...
switch (tolower ((unsigned char) p[0]))
line 1581: extern int unixy_shell;
... this is also declared in make.h.
************************
* bool, make.h, configure.in:
IMHO both humans and today's compilers greatly profite from dealing with
_Bool (C99) or bool (c++) instead of unsigned (int|char) [:n].
So to ease future development, according to autoconf.html only a little
bit must be copied&pasted into make.h and configure.in:
configure.in needs AC_HEADER_STDBOOL, make.h needs ...
#if HAVE_STDBOOL_H
# include <stdbool.h>
#else
# if ! HAVE__BOOL
# ifdef __cplusplus
typedef bool _Bool;
# else
typedef unsigned char _Bool;
# endif
# endif
# define bool _Bool
# define false 0
# define true 1
# define __bool_true_false_are_defined 1
#endif
**************************************
* read.c
(this is only w.r.t. readability)
line 128 contains ...
static int eval PARAMS ((struct ebuffer *buffer, int flags));
line 450 is a bit more concrete w.r.t. "flags":
static int eval (struct ebuffer *ebuf, int set_default)
I've changed it to ...
static void eval PARAMS ((struct ebuffer *buffer, bool set_default));
... because eval() does return 1 unconditionally, and its return value is
undocumented and not really used.
Also from eval_buffer(..) I've removed its return, cause it was
unconditionally 1, and not used.
**************************************
* read.c, "char* tilde_expand (char* name)"
At its end it does ...
#if !defined(_AMIGA) && !defined(WINDOWS32)
else
{
struct passwd *pwent;
char *userend = strchr (name + 1, '/');
if (userend != 0)
*userend = '\0';
pwent = getpwnam (name + 1);
if (pwent != 0)
{
if (userend == 0)
return xstrdup (pwent->pw_dir);
else
return concat (pwent->pw_dir, "/", userend + 1);
}
else if (userend != 0)
*userend = '/';
}
#endif /* !AMIGA && !WINDOWS32 */
#endif /* !VMS */
return 0;
}
I thought that it should restore its argument "name" in any case, hence I'd
put "if (userend != 0) *userend = '/';" immediately after
"pwent = getpwnam (name + 1);", before returning.
But maybe this effect is desired.
**********************************
* bool, remake.c
line 162:
g->changed += commands_started - ocommands_started;
(g is struct dep's "unsigned int changed : 8;")
IMHO the only save method is ...
if (commands_started != ocommands_started)
g->changed = true;
**********************************
* w32/pathstuff.c
w32ify() misses to handle 2 errors, one only occurring with artificial
long filenames (e.g. produced by makefile-bugs), the other one IMHO looks
rather likely. Additionally w32ify() flushes the CPU cache considerably.
So I've replaced it completely:
/*
* Return pointer to internal buffer.
* Convert to forward slashes. Resolve to full pathname optionally.
*/
char *
w32ify(char const *filename, bool resolve)
{
static char w32_path [FILENAME_MAX];
char *p;
if (resolve)
{
if (!_fullpath(w32_path, filename, sizeof (w32_path)))
/* invalid filename syntax, or drive doesnt exist, or buffer too
small.
FIXME: another diagnostic ? */
goto FAILED;
}
else
{
/* ! Any "strncpy" writes FILENAME_MAX (e.g.1024) bytes
unconditionally !
And can create non-zero-terminated strings, and it has no error
indicator. */
size_t const a = 1 + strlen(filename);
if (a > sizeof(w32_path))
/* buffer too small. FIXME: another diagnostic ? */
goto FAILED;
memcpy (w32_path, filename, a);
}
for (p = w32_path; *p; p++)
if (*p == '\\')
*p = '/';
return w32_path;
FAILED:
return (char*) memset (w32_path ,'\0' ,8);
/* Why not "w32_path[0] = 0" ?
Cause some functions might assume that "w32ify(*,true)" couldnt
fail
and resulted unconditionally in things like "d:/rest". */
}