Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Errors versus warnings

71 views
Skip to first unread message

Bart

unread,
Aug 24, 2018, 7:34:46 AM8/24/18
to
I had this file lying around:

https://pastebin.com/raw/rbfmwDPj

Called lisp.c, I expect it's some sort of Lisp interpreter.

Three of my 7 C Windows compilers manage to build it, including gcc
(5.1.0, -std=c11 -Wall -Wextra -Wpedantic), MSVC and Tiny C, but all
with warnings. Four of them fail it with errors.

On godbolt, gcc generally fails it (with one error in a function
argument for write()). MSVC also fails it, but because there is no
windows.h.

On my old Linux machine with gcc 4.4.3, it passes (I wish it would tell
you at the end if it succeeded or failed; instead I have to capture the
output and look for 'error:').

My question is whether this program should compile or not. Or whether
that depends purely on what options you give to a compiler.

If C, the language, does deem it OK to build as a working program, then
it does seem astonishingly lax.

If someone was given this program as a 'black box' (not looking at or
modifying the contents at all) and told to build it, how would they
instruct a compiler to do so?


(Needless to say my compiler doesn't pass it. Furthermore, if I
concentrate on this detail (the original has no prototype for write):

extern void write(void);

int main(void){
write();
}

My product and Pelles C fail to link to write(). Most others do. It
doesn't exist in msvcrt.dll except as _write(), but nothing inside
lccwin's headers (where it works) does the usual mapping of "name" to
"_name".

(I think the prototype for write is usually inside io.h. But that is not
a standard header, and is anyway not included inside lisp.c. More mystery.)

One further question, if my compiler doesn't build this program (while
gcc etc manage it), where does the problem lie? Do I need to downgrade
my error checking a couple more levels?)

--
bart

Philipp Klaus Krause

unread,
Aug 24, 2018, 8:50:21 AM8/24/18
to
Am 24.08.2018 um 13:34 schrieb Bart:

> My question is whether this program should compile or not. Or whether
> that depends purely on what options you give to a compiler.

Without looking at the specific program:
The C standard mandates "diagnostic meesages[s]" for some programs.
Which ones are errors vs. warnings is left to the implementation.

Philipp

Tim Rentsch

unread,
Aug 24, 2018, 9:23:55 AM8/24/18
to
Bart <b...@freeuk.com> writes:

> I had this file lying around:
>
> https://pastebin.com/raw/rbfmwDPj
>
> Called lisp.c, I expect it's some sort of Lisp interpreter. [...]
>
> If someone was given this program as a 'black box' (not looking at or
> modifying the contents at all) and told to build it, how would they
> instruct a compiler to do so?

What I did was this:

1. Got a copy of the file in lisp-interpreter.c

2. Try compiling (without linking) -
gcc -std=c11 -pedantic-errors -c -o <place> lisp-interpreter.c

3. Saw that there were some warnings about conversions between
pointers and integers

4. Added options to gcc to turn off those warnings
-Wno-int-to-pointer-cast
-Wno-pointer-to-int-cast

5. Compile again

6. Observe that there are error messages:
lisp-interpreter.c:64:46: error: comparison between pointer and integer
lisp-interpreter.c:64:59: error: comparison between pointer and integer
lisp-interpreter.c:283:56: error: implicit declaration of function 'write' [-Wimplicit-function-declaration]
/usr/include/unistd.h:369:16: error: conflicting types for 'write'
lisp-interpreter.c:326:5: error: ISO C forbids assignment between function pointer and 'void *' [-Wpedantic]
lisp-interpreter.c:328:16: error: implicit declaration of function 'strf' [-Wimplicit-function-declaration]
lisp-interpreter.c:399:44: error: pointer targets in initialization differ in signedness [-Wpointer-sign]
lisp-interpreter.c:447:63: error: implicit declaration of function 'waitpid' [-Wimplicit-function-declaration]

7. Try recompiling with -std=c90 instead of -std=c11

8. Observe error messages:
lisp-interpreter.c:64:46: error: comparison between pointer and integer
lisp-interpreter.c:64:59: error: comparison between pointer and integer
/usr/include/unistd.h:369:16: error: conflicting types for 'write'
lisp-interpreter.c:326:5: error: ISO C forbids assignment between function pointer and 'void *' [-Wpedantic]
lisp-interpreter.c:399:44: error: pointer targets in initialization differ in signedness [-Wpointer-sign]

9. Conclude that the source needs work, at least for the environment
where I would normally be interested in running it.

Noob

unread,
Aug 24, 2018, 11:25:23 AM8/24/18
to
On 24/08/2018 13:34, Bart wrote:

> On my old Linux machine with gcc 4.4.3, it passes (I wish it would
> tell you at the end if it succeeded or failed; instead I have to
> capture the output and look for 'error:').

If only there were some simple way to capture the return code
of a process... These silly Unix dudes should have thought
of that... Curse them!

Have you ever wondered what the EXIT_SUCCESS and EXIT_FAILURE
macros are used for?

I get the strong vibe that you think you're smart, and you
are in a way, because it does take dedication to implement
one's own programming language. But you seem to think that
you already know it all, and never leave your comfort zone.

EOT

Bart

unread,
Aug 24, 2018, 12:14:23 PM8/24/18
to
On 24/08/2018 16:25, Noob wrote:
> On 24/08/2018 13:34, Bart wrote:
>
>> On my old Linux machine with gcc 4.4.3, it passes (I wish it would
>> tell you at the end if it succeeded or failed; instead I have to
>> capture the output and look for 'error:').
>
> If only there were some simple way to capture the return code
> of a process... These silly Unix dudes should have thought
> of that... Curse them!
>
> Have you ever wondered what the EXIT_SUCCESS and EXIT_FAILURE
> macros are used for?

I know about return codes. I use them when invoking such programs from
an IDE. And I generate them myself from my own programs.

But sometimes you have to run them from a bare command line. Maybe there
is a simple way in Linux of running a program in a way that will display
the exit code, but that's not the point.

There's no reason why the program itself can't do that. And of course
you will want to know what the errors are as they are the ones that most
urgently need fixing.

> I get the strong vibe that you think you're smart, and you
> are in a way, because it does take dedication to implement
> one's own programming language. But you seem to think that
> you already know it all, and never leave your comfort zone.

I know when a user interface is being a pain and when it isn't.

--
bart

Wouter Verhelst

unread,
Aug 24, 2018, 12:25:53 PM8/24/18
to
On 8/24/18 6:14 PM, Bart wrote:
> But sometimes you have to run them from a bare command line. Maybe there
> is a simple way in Linux of running a program in a way that will display
> the exit code

Yes, there is:

command; echo $?

There you go.

If you don't want to be bothered to always type "echo $?", you can (with
a bit of hackery) also make it part of your prompt.
https://stackoverflow.com/questions/16715103/bash-prompt-with-last-exit-code
shows how to do that.

Alternatively, you can also just use "make" to build your software,
rather than running command lines directly. It keeps track of
dependencies, and will also issue a nice error message when things went
south. And for simple cases (i.e., a single source file) you don't even
need a Makefile:

wouter@gangtai:~/foo$ ls -a
. ..
wouter@gangtai:~/foo$ echo "int main(void) {}" > foo.c; make foo
cc foo.c -o foo
wouter@gangtai:~/foo$ echo "bar" > foo.c; make foo
cc foo.c -o foo
foo.c:1:1: error: expected '=', ',', ';', 'asm' or '__attribute__' at
end of input
bar
^~~
make: *** [<builtin>: foo] Error 1
wouter@gangtai:~/foo$

You know, these things have actually been thought about a bit. You're
*really* not the first person ever to run a command and not see the
output...

Keith Thompson

unread,
Aug 24, 2018, 12:33:11 PM8/24/18
to
Bart <b...@freeuk.com> writes:
> I had this file lying around:
>
> https://pastebin.com/raw/rbfmwDPj
>
> Called lisp.c, I expect it's some sort of Lisp interpreter.

It's also badly obfuscated code. I suspect it was in part automatically
generated. What is its origin?

> Three of my 7 C Windows compilers manage to build it, including gcc
> (5.1.0, -std=c11 -Wall -Wextra -Wpedantic), MSVC and Tiny C, but all
> with warnings. Four of them fail it with errors.

As usual, you pretend not to remember that the C standard requires
diagnostics for syntax errors and constraint violations, but does not
require any diagnostics to be fatal errors (other than for a #error
directive). The code appears to contain multiple constraint violations.
I don't believe there are any syntax errors.

I'll note that it includes two sizeable blocks of code, one of which is
compiled if _WIN32 is defined and on that's compiled if it isn't.
Anyone compiling this on a UNIX/Linux-like system is, to some extent,
compiling different code than you are.

> On godbolt, gcc generally fails it (with one error in a function
> argument for write()). MSVC also fails it, but because there is no
> windows.h.
>
> On my old Linux machine with gcc 4.4.3, it passes (I wish it would tell
> you at the end if it succeeded or failed; instead I have to capture the
> output and look for 'error:').

Or you can just capture the exit status. In a Unix shell:

gcc ... || echo FAILED

> My question is whether this program should compile or not. Or whether
> that depends purely on what options you give to a compiler.
>
> If C, the language, does deem it OK to build as a working program, then
> it does seem astonishingly lax.

As usual, you pretend not to remember that the only case where the C
standard requires a compiler *not* to build a working program is in the
presence of a #error directive. For programs containing any other kind
of error, it must issue a diagnostic but can generate an object file /
executable. The behavior of the resulting executable is undefined.
Yes, it's lax.

> If someone was given this program as a 'black box' (not looking at or
> modifying the contents at all) and told to build it, how would they
> instruct a compiler to do so?

With difficulty, and hoping to find an alternative that does not require
compiling incorrect code.

> (Needless to say my compiler doesn't pass it. Furthermore, if I
> concentrate on this detail (the original has no prototype for write):
>
> extern void write(void);
>
> int main(void){
> write();
> }
>
> My product and Pelles C fail to link to write(). Most others do. It
> doesn't exist in msvcrt.dll except as _write(), but nothing inside
> lccwin's headers (where it works) does the usual mapping of "name" to
> "_name".
>
> (I think the prototype for write is usually inside io.h. But that is not
> a standard header, and is anyway not included inside lisp.c. More mystery.)

write is not a standard C function. It is a standard POSIX function,
declared in <unistd.h>. (I don't know what "io.h" is.) As for linking,
on typical UNIX-like systems the write function is implemented in a
library that's linked for C programs by default. On your Windows
system, whether it's linked by default or not probably depends on the
compiler and perhaps its settings. It's also possible that some library
you're using might define a function named "write" that doesn't match
the POSIX function (though that would be odd).

> One further question, if my compiler doesn't build this program (while
> gcc etc manage it), where does the problem lie? Do I need to downgrade
> my error checking a couple more levels?)

A conforming C compiler must diagnose errors in that program, and
may or may not generate an executable for it. If you actually have
a requirement to build this thing, you have my sympathy (but probably
not my help).

--
Keith Thompson (The_Other_Keith) ks...@mib.org <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

Bart

unread,
Aug 24, 2018, 1:35:42 PM8/24/18
to
On 24/08/2018 17:25, Wouter Verhelst wrote:
> On 8/24/18 6:14 PM, Bart wrote:
>> But sometimes you have to run them from a bare command line. Maybe there
>> is a simple way in Linux of running a program in a way that will display
>> the exit code
>
> Yes, there is:
>
> command; echo $?
>
> There you go.

So you have to do something else outside of the program.

Considering that the program is generating output to stderr, presumably
primarily intended for a human user to peruse, you would think a summary
at the end would not go amiss given that the amount of output can be
arbitrarily large.

And that solution is poor. First, it doesn't work under Windows (where
you have to do "echo %errorlevel%", as a separate command after the one
you're interested in). So something inside the program, OS-independent,
is better.

Secondly, that simply writes a lonely "0" or "1" at the end of what can
be a lot of output.

Actually, I get a better idea of whether a compilation succeeded by the
extra delay there is between the end of the messages, and the
reappearance of the prompt. Well before that "0" appears.

> You know, these things have actually been thought about a bit.

And the above is the best they could come up with? I spent less than 5
minutes to write this program:

#include <stdio.h>
#include <stdlib.h>

int main (int n, char** a) {
int exitcode;

if (n>=2) {
printf("Running: \"%s\"\n",a[1]);
exitcode=system(a[1]);
printf("Exit code is %d (%s)\n",exitcode,
exitcode ? "Failed" : "Successful");
}
}

Compilation is hard; telling you whether it worked or not should be the
easy bit!

Where a compilation is successful, I can see the reason for a silent
finish because you might be invoking it dozens of times for a project,
and you don't want the output swamped. But when it isn't, that needs to
be 100% obvious.

--
bart

Bart

unread,
Aug 24, 2018, 1:49:19 PM8/24/18
to
OK, so you agree with my conclusion that the program really needs fixing
to be able to compile. And those tools [combination of tool and options]
that passed it, such as the author must have used, were being too lax.

(I hadn't thought of using c90, but this invocation:

gcc -std=c90 -Wall -Wpedantic -Wextra lisp.c

still produces an executable. Given that, I'd imagine the author would
not be interested in dealing with any complaints about the code. An easy
fix for them would be to stipulate this set of build options.)

--
bart

Keith Thompson

unread,
Aug 24, 2018, 2:13:05 PM8/24/18
to
Bart <b...@freeuk.com> writes:
[...]
> (I hadn't thought of using c90, but this invocation:
>
> gcc -std=c90 -Wall -Wpedantic -Wextra lisp.c
>
> still produces an executable. Given that, I'd imagine the author would
> not be interested in dealing with any complaints about the code. An easy
> fix for them would be to stipulate this set of build options.)

When I compile with the same options on Linux, it fails. As I mentioned
elsewhere, the `#ifdef _WIN32` directives mean that I'm compiling
different code than you are.

Do you really *need* to compile this code? Why? Where did it come
from? Is finding a different Lisp interpreter not a solution to your
unstated problem?

Bart

unread,
Aug 24, 2018, 2:32:04 PM8/24/18
to
On 24/08/2018 19:12, Keith Thompson wrote:
> Bart <b...@freeuk.com> writes:
> [...]
>> (I hadn't thought of using c90, but this invocation:
>>
>> gcc -std=c90 -Wall -Wpedantic -Wextra lisp.c
>>
>> still produces an executable. Given that, I'd imagine the author would
>> not be interested in dealing with any complaints about the code. An easy
>> fix for them would be to stipulate this set of build options.)
>
> When I compile with the same options on Linux, it fails. As I mentioned
> elsewhere, the `#ifdef _WIN32` directives mean that I'm compiling
> different code than you are.
>
> Do you really *need* to compile this code? Why? Where did it come
> from? Is finding a different Lisp interpreter not a solution to your
> unstated problem?

I don't need to compile it. But if you are testing compiler X on an
application A which passes with compiler Y but not X, you might want to
know whether it's a shortcoming in X, Y, or A.

(That this program includes an option for Windows suggests it has worked
at some point on both Windows and Linux.)

I don't know where it came from. I just found (on my machine) it while
looking meaty programs to compile that were preferably self-contained.

--
bart

james...@alumni.caltech.edu

unread,
Aug 24, 2018, 2:55:47 PM8/24/18
to
On Friday, August 24, 2018 at 7:34:46 AM UTC-4, Bart wrote:
> I had this file lying around:
>
> https://pastebin.com/raw/rbfmwDPj
>
> Called lisp.c, I expect it's some sort of Lisp interpreter.
>
> Three of my 7 C Windows compilers manage to build it, including gcc
> (5.1.0, -std=c11 -Wall -Wextra -Wpedantic), MSVC and Tiny C, but all
> with warnings. Four of them fail it with errors.
>
> On godbolt, gcc generally fails it (with one error in a function
> argument for write()). MSVC also fails it, but because there is no
> windows.h.
>
> On my old Linux machine with gcc 4.4.3, it passes (I wish it would tell
> you at the end if it succeeded or failed; instead I have to capture the
> output and look for 'error:').
>
> My question is whether this program should compile or not. Or whether
> that depends purely on what options you give to a compiler.
12345678012345678012345678012345678012345678012345678012345678012
There's only one feature a program can contain for which the
standard specifies that it should not compile: a #error directive
that survives conditional compilation (6.10.5p1).
If it's important to you to ensure that any fully conforming
implementation of C must fail to compile your program if it
contains errors, then you must include an #error directive that
is unconditionally compiled. The simplest way to do this is to
make it the first line of the file. Note that this only applies
to C99 or later; before C99, the only portable way to guarantee
that your code wouldn't compile was to avoid starting up the
compiler in the first place.
Of course, the problem is that it will also cause the program to
fail to compile even if it doesn't contain any errors at all.
This is typical of the kinds of problems you run into when you
try to use a tool incorrectly. It's just like what happens when
you try to use a hammer to cut paper.

You're not supposed to use the fact that a program compiled
successfully to determine whether it has any errors. Your first
line of defense is to look at the diagnostics that are produced.
And here is the hard part - you need to read, understand, and
think about the diagnostics to figure out whether they're valid.
Some diagnostics are mandatory, some are not. All of the
mandatory diagnostics indicate portability problems, but in many
cases there can be perfectly legitimate reasons for writing code
that triggers them. You need to know whether you have such
reasons (as a rule of thumb, it's unlikely that you have a good
reason unless you're a C expert, in which case there still a good
chance that you don't have a good reason). Non-mandatory
diagnostics can warn you about anything - and some of those
warnings point to a more serious problem than is revealed by many
of the mandatory diagnostics.

Some diagnostics cause your program to be rejected; these are
generally referred to as "error messages", and this should never
happen if your code is strictly conforming - which isn't much
help, because most useful code isn't strictly conforming. Other
messages allow compilation to complete; these are usually
referred to as "warnings". Just because a diagnostic is "only a
warning" does not justify ignoring it - many warnings point to
problems in code that might (or might not) be quite serious.

> If C, the language, does deem it OK to build as a working program, then
> it does seem astonishingly lax.

If it doesn't contain a #error directive, it's OK for a fully
conforming implementation to produce a working program, no matter
how many other problems it contains. Therefore, by your
standards, the language is indeed astonishingly lax. However, if
it produces warnings, and you chose to ignore those warning and
execute the resulting program anyway, then by my standards, it's
you who are being astonishingly lax.

> If someone was given this program as a 'black box' (not looking at or
> modifying the contents at all) and told to build it, how would they
> instruct a compiler to do so?

As a "black box", you can't tell. Nowadays, most things of any
complexity come with some system for autoconfiguration, or at
least build instructions. For code that has neither of those,
there is no substitute for reading the code and trying to figure
out which version of which language it was targeted at. This is
really bad code, contain many dodgy practices, but the fact that
it calls "write()" without #including any header which might
contain a declaration for that identifier, implies that it was
targeted at C90, where the implicit int rule allowed such calls
to be compiled, and if you were lucky, they might even link and
execute correctly, if the entire program included at least one
library which contained a definition for write() that was
compatible with those calls.

The implicit int rule was a bad idea, which is why that rule was dropped in C99.

> (Needless to say my compiler doesn't pass it. Furthermore, if I
> concentrate on this detail (the original has no prototype for write):
>
> extern void write(void);
>
> int main(void){
> write();
> }
>
> My product and Pelles C fail to link to write(). Most others do. It
> doesn't exist in msvcrt.dll except as _write(), but nothing inside
> lccwin's headers (where it works) does the usual mapping of "name" to
> "_name".
>
> (I think the prototype for write is usually inside io.h. But that is not
> a standard header, and is anyway not included inside lisp.c. More mystery.)
>
> One further question, if my compiler doesn't build this program (while
> gcc etc manage it), where does the problem lie? Do I need to downgrade
> my error checking a couple more levels?)

It depends upon which version of the C standard you're targeting,
if any. If you're targeting C99 or later, a diagnostic is
mandatory, and completing compilation is optional. If you're
targeting C90 or earlier, the diagnostic is optional, and if
there are no other syntax errors or constraint violations,
completing compilation is mandatory.

Keith Thompson

unread,
Aug 24, 2018, 3:16:00 PM8/24/18
to
OK, fair enough.

To summarize: It's bad code.

When compiled without _WIN32 defined, it contains multiple constraint
violations. I don't know whether it has constraint violations when
compiled with _WIN32 defined, but I would guess that it does. Some C
compilers are lax about accepting bad code. (gcc, for example, often
issues non-fatal warnings for constraint violations unless you enable
certain options that you already know about.)

A conforming compiler *must* at least warn about some constructs in the
code. In my opinion, a good compiler *should* reject the code, but the
standard does not require it to do so. I personally would consider
figuring out how to get a compiler to accept that code to be a waste of
time unless I had a specific requirement to do so that did not allow me
to modify the code. (If I had such a requirement, I'd try to figure out
where the code came from.)

Tim Rentsch

unread,
Aug 24, 2018, 6:08:31 PM8/24/18
to
I'm not expressing any opinion about your conclusion, just
answering the question quoted above.

> (I hadn't thought of using c90, but this invocation:
>
> gcc -std=c90 -Wall -Wpedantic -Wextra lisp.c
>
> still produces an executable. Given that, I'd imagine the author
> would not be interested in dealing with any complaints about the
> code. [...]

I don't know anything about the author, so I have no idea what he
(or she) might or might not be interested in. My reaction was
this (the $@'s and $<'s are used in place of the file names):

1. I compiled with this set of options:

gcc -std=gnu90 -c -o $@ $<

2. The compile produced one error:

/usr/include/unistd.h:369:16: error: conflicting types for 'write'

3. There were also lots of warnings, mostly about converting
between pointers and integers. One keystroke brings me to
the first flagged line, which is this:

lval *o2c(lval o) { return (lval*)(o - 1); }

The line above shows 'lval' is a typedef for 'int'. What
is happening apparently is a 64-bit pointer is being
converted to or from a 32-bit int.

4. That suggests the code has a bad architectural assumption.
I compile using a different set of options, forcing a
32-bit mode for the compile:

gcc -m32 -std=gnu90 -c -o $@ $<

5. That set of compile options gives no errors and just two
warnings (note: both are on the same source line):

lisp-interpreter.c:64:46: warning: comparison between pointer and integer
lisp-interpreter.c:64:59: warning: comparison between pointer and integer

6. For completeness I try compiling using c90 rather than gnu90:

gcc -m32 -std=c90 -c -o $@ $<

7. That set of options produces the same set of warnings as the
last set (ie, -std=gnu90).

8. Again for completeness I try adding -pedantic

gcc -m32 -std=c90 -pedantic -c -o $@ $<

9. That compile gives a few more warnings:

lisp-interpreter.c:64:46: warning: comparison between pointer and integer
lisp-interpreter.c:64:59: warning: comparison between pointer and integer
lisp-interpreter.c:326:5: warning: ISO C forbids assignment between function pointer and 'void *' [-Wpedantic]
lisp-interpreter.c:399:44: warning: pointer targets in initialization differ in signedness [-Wpointer-sign]

Here I think I have learned enough about the code to stop
investigating.

Geoff

unread,
Aug 25, 2018, 9:56:41 PM8/25/18
to
On Fri, 24 Aug 2018 12:34:43 +0100, Bart <b...@freeuk.com> wrote:

>I had this file lying around:
>
> https://pastebin.com/raw/rbfmwDPj
>
>Called lisp.c, I expect it's some sort of Lisp interpreter.
>

It is, in fact, a Lisp interpreter.

>Three of my 7 C Windows compilers manage to build it, including gcc
>(5.1.0, -std=c11 -Wall -Wextra -Wpedantic), MSVC and Tiny C, but all
>with warnings. Four of them fail it with errors.
>
>On godbolt, gcc generally fails it (with one error in a function
>argument for write()). MSVC also fails it, but because there is no
>windows.h.
>

I don't know what version of MSVC you're running but on Visual Studio
2017 it won't build due to 4 different errors when built against the
Visual Studio 2017 (v141) platform toolset but it will build without
errors but with 16 warnings as a 32-bit application on the Visual
Studio 2010 (v100) toolset. It won't build as x86_64 at all due to
implicit assumptions in the source about the target architecture.

FWIW, VS2017 errors (v141):
Error C4996 'GetVersionExA': was declared deprecated
Error C4996 'sprintf': This function or variable may be unsafe.
Error C4996 'fopen': This function or variable may be unsafe.
Error C4996 'fscanf': This function or variable may be unsafe.

C4996 is an error in VS when (v141) but only a warning in (v100).

Visual Studio doesn't complain about missing windows.h at all.

It won't build on Xcode as a 64-bit app.

>On my old Linux machine with gcc 4.4.3, it passes (I wish it would tell
>you at the end if it succeeded or failed; instead I have to capture the
>output and look for 'error:').
>

Looking at the main function, it always returns 0. But the code is
obfuscated and it's easy to see why you couldn't see that... or didn't
bother to look.

The program has no graceful exit, ctrl-c seems to be the only way out.
On Windows 10 in the VS2017 debugger it returns 0 when closed with
ctrl-c and garbage when the console window is closed with the mouse.

Passing it a file containing (print "Hello World") works very nicely
with the exception that the program doesn't terminate in the
traditional sense but keeps the interactive window open. I suppose the
intent was to allow the user to see the output.

>My question is whether this program should compile or not. Or whether
>that depends purely on what options you give to a compiler.
>

It depends on the compiler and the options you give it, obviously.
This is one characteristic of old code on newer compilers.

>If C, the language, does deem it OK to build as a working program, then
>it does seem astonishingly lax.
>

This code appears to be WIN32 code originally and then modified for
OSX. (__MACH__)

If by lax, you mean a C compiler can be told to accept this program,
then yes, it's lax, by design. This has been true since ANSI-C days.

>If someone was given this program as a 'black box' (not looking at or
>modifying the contents at all) and told to build it, how would they
>instruct a compiler to do so?
>

Personally, I wouldn't accept it as-is. It's a mess.

I created an empty project targeting X86 and copy-pasted the code into
a new lisp.c file created in the IDE. First compilation failed and I
experimented with the settings, knowing about C4996 and turning it off
to see if it would build. It did.

>
>(Needless to say my compiler doesn't pass it. Furthermore, if I
>concentrate on this detail (the original has no prototype for write):
>
> extern void write(void);
>
> int main(void){
> write();
> }
>

That's because the style of this code is so obfuscated even the author
couldn't see this bug or he chose to ignore it.

Warning C4013 'write' undefined; assuming extern returning int

Only the fact that he defines typdef int lval saves him.

>My product and Pelles C fail to link to write(). Most others do. It
>doesn't exist in msvcrt.dll except as _write(), but nothing inside
>lccwin's headers (where it works) does the usual mapping of "name" to
>"_name".
>
>(I think the prototype for write is usually inside io.h. But that is not
>a standard header, and is anyway not included inside lisp.c. More mystery.)
>
>One further question, if my compiler doesn't build this program (while
>gcc etc manage it), where does the problem lie? Do I need to downgrade
>my error checking a couple more levels?)

No, this program is admittedly very interesting but formatted in
exceedingly bad style such that one would be inclined to run it
through indent a few times or a C prettifier before attempting to
rewrite it for current C implementations. It appears the author may
have written this as-is rather than compressing it after the fact
because some of the style is inconsistent with an automatic
obfuscator.

Other warnings from VS2017:

Warning C4022 'ReadFile' : pointer mismatch for actual parameter 1
Warning C4005 'TRUE' : macro redefinition
Warning C4047 '<' : 'lval' differs in levels of indirection from
'lval *'
Warning C4047 '>' : 'lval' differs in levels of indirection from
'lval *'
Warning C4018 '>=' : signed/unsigned mismatch
Warning C4013 'write' undefined; assuming extern returning int
Warning C4022 'CloseHandle' : pointer mismatch for actual parameter
1
Warning C4022 'WaitForSingleObject' : pointer mismatch for actual
parameter 1
Warning C4022 'WriteFile' : pointer mismatch for actual parameter 1
Warning C4022 'FlushFileBuffers' : pointer mismatch for actual
parameter 1
Warning C4018 '<' : signed/unsigned mismatch
Warning C4716 'fasr' : must return a value

Geoff

unread,
Aug 25, 2018, 10:06:53 PM8/25/18
to
On Fri, 24 Aug 2018 12:34:43 +0100, Bart <b...@freeuk.com> wrote:

>On godbolt, gcc generally fails it (with one error in a function
>argument for write()). MSVC also fails it, but because there is no
>windows.h.

Actually, looking at it again, there is inclusion of windows.h:

#ifdef __MACH__
#define setjmp(e) sigsetjmp(e, 0)
#define longjmp siglongjmp
#endif
#ifdef _WIN32
#include <windows.h>
#define X __declspec(dllexport)
#else
#define X
#endif

Why does your MSVC not see it? Could it be the _WIN32? MS used both
_WIN32 and WIN32 macros depending on compiler vintage, IIRC.

Geoff

unread,
Aug 25, 2018, 10:14:19 PM8/25/18
to
On Sat, 25 Aug 2018 18:56:30 -0700, Geoff <ge...@invalid.invalid>
wrote:

>The program has no graceful exit, ctrl-c seems to be the only way out.
>On Windows 10 in the VS2017 debugger it returns 0 when closed with
>ctrl-c and garbage when the console window is closed with the mouse.
>

The graceful exit is ctrl-z (EOF) that magic happens in lread().

Kenny McCormack

unread,
Aug 25, 2018, 11:34:08 PM8/25/18
to
In article <4634odl6t27arfrnk...@4ax.com>,
It's funny, that. Many people coming to Unix from Windows think that ^Z
causes a program to exit.

Which it does. Sort of...

--
Mike Huckabee has yet to consciously uncouple from Josh Duggar.

Geoff

unread,
Aug 25, 2018, 11:41:22 PM8/25/18
to
On Sun, 26 Aug 2018 03:33:58 +0000 (UTC), gaz...@shell.xmission.com
(Kenny McCormack) wrote:

>In article <4634odl6t27arfrnk...@4ax.com>,
>Geoff <ge...@invalid.invalid> wrote:
>>On Sat, 25 Aug 2018 18:56:30 -0700, Geoff <ge...@invalid.invalid>
>>wrote:
>>
>>>The program has no graceful exit, ctrl-c seems to be the only way out.
>>>On Windows 10 in the VS2017 debugger it returns 0 when closed with
>>>ctrl-c and garbage when the console window is closed with the mouse.
>>>
>>
>>The graceful exit is ctrl-z (EOF) that magic happens in lread().
>
>It's funny, that. Many people coming to Unix from Windows think that ^Z
>causes a program to exit.
>
>Which it does. Sort of...

Actually, the program explicitly does it:

lval lread(lval *g)
{
int c = getnws();
if (c == EOF)
return 8;
....


and main:

...

for (i = 1; i < argc; i++)
load(g, argv[i]);
setjmp(top_jmp);
do
printf("? ");
while (ep(g, lread(g)));
return 0;
}

Function ep scans input and returns 0 if lread returns 8.
Goofiness all around.

Scott

unread,
Aug 26, 2018, 3:22:41 AM8/26/18
to
On Sat, 25 Aug 2018 20:41:15 -0700, Geoff <ge...@invalid.invalid>
wrote:

>On Sun, 26 Aug 2018 03:33:58 +0000 (UTC), gaz...@shell.xmission.com
>(Kenny McCormack) wrote:
>
>>>The graceful exit is ctrl-z (EOF) that magic happens in lread().
>>
>>It's funny, that. Many people coming to Unix from Windows think that ^Z
>>causes a program to exit.
>>
>>Which it does. Sort of...
>
>Actually, the program explicitly does it:
>
>lval lread(lval *g)
>{
> int c = getnws();
> if (c == EOF)
> return 8;
>....

Oh, my.... <stifling a giggle now>

Philipp Klaus Krause

unread,
Aug 26, 2018, 4:39:11 AM8/26/18
to
Am 24.08.2018 um 18:32 schrieb Keith Thompson:
> Bart <b...@freeuk.com> writes:
>> I had this file lying around:
>>
>> https://pastebin.com/raw/rbfmwDPj
>>
>> Called lisp.c, I expect it's some sort of Lisp interpreter.
>
> It's also badly obfuscated code. I suspect it was in part automatically
> generated. What is its origin?

I'm just guessing here, but wouldn't be surprised, if it was some Lisp
passed through some Lisp-to-C compiler.

A few years ago, I looked at a few of those Lisp/Scheme-to-C compilers
(I was mostly interested in compilation of Scheme to C, sicne both are
relatively nice languages with the standard being short enough to
actually read and somewhat understand). There are lots of them, and
writing them seems a somewhat popular exercise among Lisp/Scheme beginners.
But even the established ones (i.e. the ones that made it into GNU/Linux
distributions), tend to not output standard C. It seems the authors are
satisfied with the "C" as soon as it compiles without errors with their
favorite compiler. They tend to rely a lot on implementation-defined
behaviour, even when not necessary, rely on whatever happened in their
test cases for undefine dbehaviour, make unnecessary use of vendor
extensions, etc.

Philipp

Bart

unread,
Aug 26, 2018, 6:34:43 AM8/26/18
to
On 26/08/2018 03:06, Geoff wrote:
> On Fri, 24 Aug 2018 12:34:43 +0100, Bart <b...@freeuk.com> wrote:
>
>> On godbolt, gcc generally fails it (with one error in a function
>> argument for write()). MSVC also fails it, but because there is no
>> windows.h.
>
> Actually, looking at it again, there is inclusion of windows.h:

> Why does your MSVC not see it?

This is MSVC running via godbolt.org. On my own PC it can see windows.h.

Presumably godbolt.org runs on some sort of Linux system, so I'm not
sure how an MSVC without access to windows.h is going to work. (It only
has to cross-compile, not build or run.) Maybe it's only for programs
that don't use windows.h.

BC:
>> On my old Linux machine with gcc 4.4.3, it passes (I wish it would tell
>> you at the end if it succeeded or failed; instead I have to capture the
>> output and look for 'error:').
>>
>
> Looking at the main function, it always returns 0. But the code is
> obfuscated and it's easy to see why you couldn't see that... or didn't
> bother to look.


The return code I'm talking about is from gcc, not the program that is
being compiled. (I've done nothing with that - when it worked - other
than enter a few random things at the prompt while desperately trying to
remember some fragments of Lisp.)

--
bart

james...@alumni.caltech.edu

unread,
Aug 26, 2018, 7:55:00 AM8/26/18
to
On Sunday, August 26, 2018 at 6:34:43 AM UTC-4, Bart wrote:
...
> The return code I'm talking about is from gcc, not the program that is
> being compiled. (I've done nothing with that - when it worked - other
> than enter a few random things at the prompt while desperately trying to
> remember some fragments of Lisp.)

-pass-exit-codes
Normally the gcc program exits with the code of 1 if any phase of
the compiler returns a non-success return code. If you specify
-pass-exit-codes, the gcc program instead returns with the
numerically highest error produced by any phase returning an error
indication. The C, C++, and Fortran front ends return 4 if an
internal compiler error is encountered.

-fsanitize-recover[=opts]
-fsanitize-recover= controls error recovery mode for sanitizers
mentioned in comma-separated list of opts. Enabling this option
for a sanitizer component causes it to attempt to continue running
the program as if no error happened. This means multiple runtime
errors can be reported in a single program run, and the exit code
of the program may indicate success even when errors have been
reported. The -fno-sanitize-recover= option can be used to alter
this behavior: only the first detected error is reported and
program then exits with a non-zero exit code.

There is, of course, always the danger that some situation that you want to be treated as an error does not cause any phase of the compiler to return a non-success return code.
0 new messages