C scripting (continued)

J

jacob navia

The main complaints about the version of a C script I presented in this
forum were:

(1) Limited buffers could overflow
(2) Security issues concerning specially crafted file names

Those issues were not at all any problem in the environment where this
script was used but from a more general perspective they could be an
issue.

Here is a version that uses ggets() from Chuck Falconer to avoid problem
number (1) and solves also problem number two. I use snprintf instead of
sprintf to avoid any buffer overflow problems
-------------------------------------------------------cut here
#include <stdio.h>
#include <stdlib.h>
#define INITSIZE 112 /* power of 2 minus 16, helps malloc */
#define DELTASIZE (INITSIZE + 16)
enum {
OK = 0, NOMEM};
int fggets(char* *ln, FILE *f) {
int cursize, ch, ix=0;
char *buffer, *temp;

*ln = NULL; /* default */
if (NULL == (buffer = malloc(INITSIZE))) return NOMEM;
cursize = INITSIZE;
while ((EOF != (ch = getc(f))) && ('\n' != ch)) {
if (ix >= (cursize - 1)) { /* extend buffer */
cursize += DELTASIZE;
if (NULL == (temp = realloc(buffer, (size_t)cursize))) {
buffer[ix] = '\0';
*ln = buffer;
return NOMEM;
}
buffer = temp;
}
buffer[ix++] = ch;
}
if ((EOF == ch) && (0 == ix)) {
free(buffer);
return EOF;
}

buffer[ix] = '\0';
if (NULL == (temp = realloc(buffer, (size_t)ix + 1))) {
*ln = buffer; /* without reducing it */
}
else *ln = temp;
return OK;
}

#define MAXTESTS 2000
#include <stdio.h>
#include <string.h>
void ShowFailures(char *,char *tab[],int);
int main(int argc,char *argv[]){
FILE *f;
char *buf,*p,char cmd[1024];
int r,tests=0, CompilationFailures=0, LinkFailures=0, RunFailures=0;
char *CFailures[MAXTESTS];
char *LFailures[MAXTESTS];
char *RFailures[MAXTESTS];

f = popen("ls *.c","r");
if (f == NULL) { fprintf(stderr,"Impossible to list files.\n");
return -1;
}
while (0==fggets(&buf,f)) {
if (++tests >= MAXTESTS) {
printf("More than %d files. No more space"
"available\n",MAXTESTS);
break;
}
snprintf(cmd,sizeof(cmd),"../lcc -g2 -nw ./%s",buf);
p = strchr(buf,'.');
if (p) *p=0;
r = system(cmd);
if (r) {
printf("Compilation of %s.c failed\n",buf);
CFailures[CompilationFailures++] = strdup(buf);
}
else {
p = strchr(buf,'.');
if (p) *p = 0;
snprintf(cmd,sizeof(cmd),"gcc ./%s.o lcclibc_asm.s -lm");
r = system(cmd);
if (r) {
printf("Link of %s.o failed\n",buf);
LFailures[LinkFailures++] = strdup(buf);
}
else {
r = system("./a.out");
if (r) {
printf("Execution of %s failed\n",buf);
RFailures[RunFailures++] = strdup(buf);
}
}
}
}
pclose(f);
printf("\n********************************\n%d"
"tests\n********************************\n",tests);
ShowFailures("Compilation",CFailures,CompilationFailures);
ShowFailures("Link",LFailures,LinkFailures);
ShowFailures("Run",RFailures,RunFailures);
}
void ShowFailures(char *name,char *tab[],int n){
int i;
if (n <= 0) return;
printf("%s failures (%d)\n",name,n);
for (i=0; i<n; i++) {
printf("%s ",tab);
}
printf("\n");
}
---------------------------------------------------------------------------
Note that this is only slightly more complicated than the original and
it has no obvious security problems. Yes, it doesn't handle file names
with new lines in them, and if you create a file name with just the
backspace character in it, it will probably not display it correctly.

This script (I repeat) is not going to be run in an hostile environment.
But now it is more robust. This just confirms that C can be used without
any trouble as a language to create small scripts that organize the work
of other software.
 
J

jacob navia

Note that I do not free the buffer returned by fggets since they
will be freed when the script ends.

Obviously in memory constrained environments you should add a single line

free(buf)

at the end of the while loop.
 
B

Bartc

Here is a version that uses ggets() from Chuck Falconer to avoid problem
number (1) and solves also problem number two. I use snprintf instead of
sprintf to avoid any buffer overflow problems
-------------------------------------------------------cut here
#include <stdio.h> ....
f = popen("ls *.c","r"); ....
while (0==fggets(&buf,f)) { ....
snprintf(cmd,sizeof(cmd),"../lcc -g2 -nw ./%s",buf);
p = strchr(buf,'.');
if (p) *p=0;
r = system(cmd); ....
snprintf(cmd,sizeof(cmd),"gcc ./%s.o lcclibc_asm.s -lm"); ....
r = system("./a.out");

I assume it works but you can't really pretend anymore that using C is as
sweet and succinct as using the right tool.

The style of the code just doesn't seem right for writing scripts. Anyone
writing scripts for compiling, linking and running programs would likely
have ready-made functions such as:

compile(filename,[switches]) => status
link(filename,[switches]) => status
link(filename-list, [switches]) => status
run(filename,[options]) => status
changeext(filename,newextension) => newfilename (one of mine)

Then the script writer doesn't need to care about the exact details of
running the compiler, or which compiler, or even which language (if
compile() picks up the file extension). These are possible in C too but
having easy string and list manipulation is a big help.

The method for obtaining a list of files also seems crude, and highly
system-specific. (I use a function like:

dirlist("*.c") => filename-list, still a little specific but I don't need to
know any system commands.)

I couldn't quite understand the premise of this script: it looks for
individual C files, and tries to compile and link as though each file
generates a complete executable? It's not possible that sometimes a bunch of
C files need to be linked together into one executable, or that some are
libraries not intended to be executed?
 
J

jacob navia

Bartc said:
I assume it works but you can't really pretend anymore that using C is
as sweet and succinct as using the right tool.

This program is very small. The total size of the executable is 5072
bytes, and the size of the source is just 1671 bytes. This is just
as succinct as it gets... and it doesn't need 800K+ of ruby interpreter
The style of the code just doesn't seem right for writing scripts.
Anyone writing scripts for compiling, linking and running programs would
likely have ready-made functions such as:

compile(filename,[switches]) => status
link(filename,[switches]) => status
link(filename-list, [switches]) => status
run(filename,[options]) => status
changeext(filename,newextension) => newfilename (one of mine)

I could write those in C too but I do not see any need for it. They
are "inlined" say.

The method for obtaining a list of files also seems crude, and highly
system-specific. (I use a function like:

dirlist("*.c") => filename-list, still a little specific but I don't
need to know any system commands.)

Sure, there are functions in C that list directories and return lists,
but why bother? Those work.

The problem withlists in C is that every program has one and the
Ccommittee refuses to standardize an interface so that library functions
can be written like

stdlist *dir(char *pattern);

This would make C much better in this respect.
I couldn't quite understand the premise of this script: it looks for
individual C files, and tries to compile and link as though each file
generates a complete executable? It's not possible that sometimes a
bunch of C files need to be linked together into one executable, or that
some are libraries not intended to be executed?

Yes, each test case is written so that it either calls abort()
and exits with an error, or it returns zero to the environment
 
K

Keith Thompson

jacob navia said:
The main complaints about the version of a C script I presented in
this forum were:

(1) Limited buffers could overflow
(2) Security issues concerning specially crafted file names

(3) Blatant unacknowledged non-portability.
Those issues were not at all any problem in the environment where this
script was used but from a more general perspective they could be an
issue. [...]
char *buf,*p,char cmd[1024];

This is a syntax error.

[...]
snprintf(cmd,sizeof(cmd),"gcc ./%s.o lcclibc_asm.s -lm");

You're missing an argument for the "%s" specifier; I think you had a
similar bug in your first version.

Your own lcc-win compiler (at least the Windows version) diagnoses
both these problems. Why are you posting code that you haven't even
compiled?
 
J

jacob navia

Keith said:
(3) Blatant unacknowledged non-portability.

The code will compile and run under windows and unix... Just change
ls by dir. And who told you that I am interested in more portability
than that? I do not want to run in a coffee machine pelase.
Those issues were not at all any problem in the environment where this
script was used but from a more general perspective they could be an
issue. [...]
char *buf,*p,char cmd[1024];

This is a syntax error.

Yes. I reformated for posting to avoid line breaks.
[...]
snprintf(cmd,sizeof(cmd),"gcc ./%s.o lcclibc_asm.s -lm");

You're missing an argument for the "%s" specifier; I think you had a
similar bug in your first version.

Again the same problem
Your own lcc-win compiler (at least the Windows version) diagnoses
both these problems. Why are you posting code that you haven't even
compiled?

No, I did not compile the message I compiled the code that I later
reformatted to allow for better reading.

And that is all you have to say?

Vacuous, like all your answers. Never go into the subject, just
go on picking some small point and tripping there.
 
K

Kenny McCormack

jacob navia said:
Vacuous, like all your answers. Never go into the subject, just
go on picking some small point and tripping there.

That's our Keith. Surely, you had no reason to expect anything else?
 
K

Keith Thompson

jacob navia said:
Keith said:
(3) Blatant unacknowledged non-portability.

The code will compile and run under windows and unix... Just change
ls by dir. And who told you that I am interested in more portability
than that? I do not want to run in a coffee machine pelase.
Those issues were not at all any problem in the environment where this
script was used but from a more general perspective they could be an
issue. [...]
char *buf,*p,char cmd[1024];

This is a syntax error.

Yes. I reformated for posting to avoid line breaks.

What's wrong with line breaks? Did your reformatting change a
semicolon to a comma?
[...]
snprintf(cmd,sizeof(cmd),"gcc ./%s.o lcclibc_asm.s -lm");

You're missing an argument for the "%s" specifier; I think you had a
similar bug in your first version.

Again the same problem

So you deleted a required argument to snprintf to make the line fit?
Why didn't you just split the call across two lines?

snprintf(cmd, sizeof(cmd),
"gcc ./%s.o lcclibc_asm.s -lm",
buf);
No, I did not compile the message I compiled the code that I later
reformatted to allow for better reading.

And your reformatting broke the code.
And that is all you have to say?
Yes.

Vacuous, like all your answers. Never go into the subject, just
go on picking some small point and tripping there.

Here's my advice. If you're planning to post code to Usenet, write it
so it's *both* correct and suitable for posting. Copy-and-paste the
exact code that you fed to the compiler into your newsreader. For a
snippet of only a few lines, this probably isn't necessary, but for a
complete program, in my opinion, it's vital.

Since I have no idea what other errors you introduced when you
reformatted your code for posting, I'm not going to waste my time on
it. If you don't care about your own code, why should I?

And just in case you feel like I'm picking on you personally, I'm not.
I would have pointed out the compilation errors to anyone. I probably
wouldn't have posted this followup, because most posters would have
accepted the corrections with some semblance of good grace.
 
K

Kenny McCormack

Keith Thompson said:
And just in case you feel like I'm picking on you personally, I'm not.
I would have pointed out the compilation errors to anyone. I probably
wouldn't have posted this followup, because most posters would have
accepted the corrections with some semblance of good grace.

Gosh, but you are a piece of work.

Have you any idea how insane you sound?

I mean, seriously. Step back. Pretend you are an adult reading the
above text. What sort of conclusions would you draw about the poster
known as "Keith Thompson"?
 
C

CBFalconer

jacob said:
.... snip ...

Here is a version that uses ggets() from Chuck Falconer to avoid
problem number (1) and solves also problem number two. I use
snprintf instead of sprintf to avoid any buffer overflow problems

You omitted the header file, which defines the macro ggets(**ln);
I don't see that definition copied anywhere.
 
C

CBFalconer

jacob said:
Keith Thompson wrote:
.... snip valid criticism ...

No, I did not compile the message I compiled the code that I
later reformatted to allow for better reading.

And that is all you have to say?

Vacuous, like all your answers. Never go into the subject,
just go on picking some small point and tripping there.

Very good. Offend someone who offers accurate criticism and
suggestions. However he doesn't seem to bow down before your
godlike image.

FYI C source generally needs to have the 'small points' corrected.
 
J

jacob navia

CBFalconer said:
jacob navia wrote:
... snip ...

You omitted the header file, which defines the macro ggets(**ln);
I don't see that definition copied anywhere.

Yes, since I used fggets it wasn't really necessary, and I did not want
to add many more lines for no reason.
 
J

Jens Thoms Toerring

jacob navia said:
The main complaints about the version of a C script I presented in this
forum were:
(1) Limited buffers could overflow
(2) Security issues concerning specially crafted file names
Those issues were not at all any problem in the environment where this
script was used but from a more general perspective they could be an
issue.

You post a revised version of a program that takes about
100 lines of C code to a) compilee, b) link and c) run the
C programs in a certain directory. And, as has been pointed
out, there are still some issues with the program. So I am
wondering is what you actually want to prove with this?

I could come up with less then 10 lines of a Makefile or
similar number of limes of a Perl or bash script to do
more or less the same, and I am rather sure many of the
other "regulars" could do the same (or they rather might
know an even more concise way to do it). So it's no a
very convincing example of why one should write scripts
in C.

If you want to write everything in C (and I don't blame you,
I like C and also write a lot of stuff in C, but not every-
thing) that's fine with me. What I don't understand is why
you seem to see C as the one and only "true way" of writing
programs. C is, at least to me, a nice. small and often ele-
gant language, but, alas, at times bogged down with histo-
rical decisions that can't be easily undone and simply have
to be accepted. It's very useful for a lot of things I do
(especially if speed is an issue or you have to write e.g.
libraries or device drivers). But there are things that
are too time-consuming and complicated to write in C and then,
if possible, I tend to write that in a different language.
E.g. recenty I had to read in a file with CO2 concentration
data from antarctic ice core samples for the last 2000 years
and calculate 100-year averages I do that in Perl since it
takes me perhaps 2-3 minutes (including debugging) to do,
while in C it would have me taken at least ten times as
long (and I write C regularly since nearly 20 years and
learned Perl perhaps 8 years ago - before I would have
used awk or something similar to at least do the parsing
instead of C).

But, if I understand what you write at other times, you
don't seem to like C for what it is: you want to extend
it in all kinds of directions, e.g. making it more like
C++, with operator and function overloading etc., just to
name a few examples. I get the feeling that you actually
want to develop your own language and the restrictions of
C are keeping you back all of the time, making you unhappy
more than necessary. Why don't you simple cast off all the
baggage and start your own language? Other people have done
so before and have been very successful, with C++ as the
most obvious example?

C has a very important role nowadays as a lingua franca.
Many kernels, device drivers etc. are written in C, as
well as all kinds of other languages and libraries. But
that's also what makes C so hard to change fundamentally.
How much you rave and swear, it won't get changed easily.
And if you don't like it come up with something that's
really better and gets accepted! You obviously have tried
to with lcc, but just having tried, unfortunately, isn't
good enough. There are lots of people that have written
excellent programs or tools that never got the recognition
they deserved. So why not make your lcc a compiler for a
language you really like and feel to be superior to C,
dropping all pretensions of it being "just another C
compiler"? It obviously isn't meant to be one. You don't
seem to want to write just a C compiler that's faster than
all others or just produces faster executables - from all
you write I take that you want to write a compiler for what
you see as a better language than (that's just similar to
C). So just do that and don't look back - don't get bogged
down by people pointing out that it may have some issues as
a standard conforming C compiler.

Regards, Jens
 
B

Bartc

But, if I understand what you write at other times, you
don't seem to like C for what it is: you want to extend
it in all kinds of directions, e.g. making it more like
C++, with operator and function overloading etc., just to
name a few examples. I get the feeling that you actually
want to develop your own language and the restrictions of
C are keeping you back all of the time, making you unhappy
more than necessary. Why don't you simple cast off all the
baggage and start your own language? Other people have done
so before and have been very successful, with C++ as the
most obvious example?

That's a good idea. lcc-win32 compiles a language with features that seem
more than just compiler extensions.

Perhaps a formal spec can be created for this language, that is upwards
compatible with standard C, and this might encourage other compilers for it.

Otherwise I think Jacob will have a long wait for a committee to come up
with the language spec he wants. Some of the stuff in lcc-win seems much
more interesting than C99 and it's successor.
 
K

Keith Thompson

CBFalconer said:
You omitted the header file, which defines the macro ggets(**ln);
I don't see that definition copied anywhere.

That's ok, he didn't actually use ggets(), just fggets().
 
B

Ben Bacarisse

jacob navia said:
The main complaints about the version of a C script I presented in
this forum were:

(1) Limited buffers could overflow

Was the concern that buffers could overflow or that input sizes were
limited? If it was the first, then of course you need to fix it but I
thought the complaint (such as it was) was about limiting the size of
file name that can be handled. Using ggets does not fix that issue.
(2) Security issues concerning specially crafted file names

Those issues were not at all any problem in the environment where this
script was used but from a more general perspective they could be an
issue.

Here is a version that uses ggets() from Chuck Falconer to avoid
problem number (1) and solves also problem number two. I use snprintf
instead of sprintf to avoid any buffer overflow problems

I don't see how you solved (2). I can still make the script delete my
files. By saying that you have fixed (2) when you haven't you make
the script, if anything, more dangerous (because some people might not
check).

Note that this is only slightly more complicated than the original and
it has no obvious security problems. Yes, it doesn't handle file names
with new lines in them, and if you create a file name with just the
backspace character in it, it will probably not display it correctly.

This script (I repeat) is not going to be run in an hostile environment.
But now it is more robust. This just confirms that C can be used without
any trouble as a language to create small scripts that organize the work
of other software.

If the environment is not hostile, why did you bother to address (2)
at all? You should have just said that such things don't matter to
you in this case, which is fair enough. Saying a script is safe when
it is not is worth than just saying: "beware or dangerous file names".
 
K

Keith Thompson

jacob navia said:
The main complaints about the version of a C script I presented in
this forum were:

(1) Limited buffers could overflow
(2) Security issues concerning specially crafted file names

Those issues were not at all any problem in the environment where this
script was used but from a more general perspective they could be an
issue.

Here is a version that uses ggets() from Chuck Falconer to avoid
problem number (1) and solves also problem number two. I use snprintf
instead of sprintf to avoid any buffer overflow problems
[...]

I note that your use of snprintf could result in very long commands
being silently truncated. I also note that you haven't entirely
solved problem number 2; since you're passing file names to system()
without checking for metacharacters, I could probably construct a file
name that would cause arbitary commands to be executed. But ok, such
a file name isn't likely to be generated accidentally, and you're
assuming your environment is safe from malicious attacks.
-------------------------------------------------------cut here
[102 lines of C deleted; see upthread]
---------------------------------------------------------------------------
Note that this is only slightly more complicated than the original and
it has no obvious security problems. Yes, it doesn't handle file names
with new lines in them, and if you create a file name with just the
backspace character in it, it will probably not display it correctly.

This script (I repeat) is not going to be run in an hostile environment.
But now it is more robust. This just confirms that C can be used without
any trouble as a language to create small scripts that organize the work
of other software.
---------------------------------------------------------------------------

Here's my version, written in Perl, based on your C program.

------------------------------ CUT HERE ------------------------------
#!/usr/bin/perl

use strict;
use warnings;

opendir my $CWD, '.' or die ".: $!\n";
my @c_files = sort grep { /\.c$/ and -f $_ and -r $_ } readdir $CWD;
closedir $CWD;

die "No readable *.c files\n" if not @c_files;

my @compilation_failures = ();
my @link_failures = ();
my @run_failures = ();

my @compile_command = qw(../lcc -g2 -nw);
my @link_command1 = qw(gcc);
my @link_command2 = qw(lcclibc_asm.s -lm);

TEST:
foreach my $c_file (@c_files) {
my $base_name;
($base_name = $c_file) =~ s/\.c$//;
my $obj_file = "$base_name.o";

my $compile_result = system @compile_command, $c_file;
if ($compile_result != 0) {
push @compilation_failures, "Compilation of $c_file failed\n";
next TEST;
}

my $link_result = system @link_command1, $obj_file, @link_command2;
if ($link_result != 0) {
push @link_failures, "Link of $obj_file failed\n";
next TEST;
}

my $run_result = system './a.out';
if ($run_result != 0) {
push @run_failures, "Execution of $base_name failed\n";
next TEST;
}
}

print '=' x 72, "\n";

if (@compilation_failures) {
print scalar @compilation_failures, " compilation failure(s):\n";
print @compilation_failures;
}

if (@link_failures) {
print scalar @link_failures, " link failure(s):\n";
print @link_failures;
}
if (@run_failures) {
print scalar @run_failures, " run failure(s):\n";
print @run_failures;
}
------------------------------ CUT HERE ------------------------------

It's roughly half the size of your C version, it performs more
checking, and because of the way it invokes system() it's not
vulnerable to attacks involving file names with metacharacters. It
took me a few minutes to write it.

I know a lot of people dislike Perl for various reasons, and I'm not
going to advocate for the language here. Very similar things could be
done in Python, Ruby, or any other decent scripting language -- and,
IMHO, much more easily and reliably than in C.

And I'm certainly not arguing against C either (after all, Larry Wall
liked it enough that he wrote Perl in C). I'm merely arguing, and
trying to demonstrate, that scripting languages can be better for some
tasks, just as C is better for other tasks. And Perl, Python, Ruby,
et al are readily available on just about any of the systems where
your C program would work.
 
I

Ian Collins

jacob said:
f = popen("ls *.c","r");
if (f == NULL) { fprintf(stderr,"Impossible to list files.\n");
return -1;
}
while (0==fggets(&buf,f)) {

You still missed the obvious (and probably more portable) simplification
(see my reply to your original post) of letting the shell pass the file
list in in argv.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads

C as a scripting language 88
C pipe 1
Fibonacci 0
Function is not worked in C 2
Adding adressing of IPv6 to program 1
HELP:function at c returning (null) 4
Lexical Analysis on C++ 1
Can't solve problems! please Help 0

Members online

No members online now.

Forum statistics

Threads
473,982
Messages
2,570,190
Members
46,740
Latest member
AdolphBig6

Latest Threads

Top