Home > Archive > Unix Programming > June 2007 > parsing a command from a config file
You are viewing an archived Text-only version of the thread.
To view this thread in it's original format and/or if you want to reply to
this thread please [click here]
| Author |
parsing a command from a config file
|
|
| David Lee 2007-05-15, 10:04 pm |
|
Given: The config file for an application (written in C) includes a
directive for a command to be invoked via one of the "exec()" calls
(following a "fork()").
Problem: What is the cleanest way to parse that command:
cmd arg1 arg2 arg3
so that such an "exec()" can then be done?
It would also be nice if it could understand:
cmd "arg1a arg1b" arg2 arg3
(that is, allowing an argument to include whitespace).
Is there already a portable subroutine for this sort of quasi-shell
parsing? In my years of programming, I don't recall seeing any such
subroutine. Or do I need to write my own using something like "strtok()"?
(Yes, I'm alert to the potentially lethal possibilities of interference
leading to something like "/bin/rm -rf /" ...!)
--
David Lee
| |
| Måns Rullgård 2007-05-15, 10:04 pm |
| David Lee <t.d.lee@durham.ac.uk> writes:
> Given: The config file for an application (written in C) includes a
> directive for a command to be invoked via one of the "exec()" calls
> (following a "fork()").
>
> Problem: What is the cleanest way to parse that command:
> cmd arg1 arg2 arg3
>
> so that such an "exec()" can then be done?
>
> It would also be nice if it could understand:
> cmd "arg1a arg1b" arg2 arg3
>
> (that is, allowing an argument to include whitespace).
>
> Is there already a portable subroutine for this sort of quasi-shell
> parsing? In my years of programming, I don't recall seeing any such
> subroutine. Or do I need to write my own using something like "strtok()"?
Maybe wordexp() is what you've been looking for all these years.
--
Måns Rullgård
mans@mansr.com
| |
| Logan Shaw 2007-05-15, 10:04 pm |
| David Lee wrote:
> Given: The config file for an application (written in C) includes a
> directive for a command to be invoked via one of the "exec()" calls
> (following a "fork()").
>
> Problem: What is the cleanest way to parse that command:
> cmd arg1 arg2 arg3
>
> so that such an "exec()" can then be done?
>
> It would also be nice if it could understand:
> cmd "arg1a arg1b" arg2 arg3
>
> (that is, allowing an argument to include whitespace).
>
> Is there already a portable subroutine for this sort of quasi-shell
> parsing? In my years of programming, I don't recall seeing any such
> subroutine.
Perl, I believe, handles this issue internally with the following
rules:
(1) if there are no metacharacters (backslash, quotes, etc.),
split on whitespace and pass the resulting list of tokens
to exec().
(2) if there are metacharacters, pass the list
( "sh", "-c", string_with_metacharacters ) to exec() instead.
In this way, you get all the efficiency of doing it directly in a
lot of cases, and in the other cases, you get the full flexibility
of the shell. It also keeps the complexity of parsing out of
your code.
The di vantage is that the logic is different for different cases.
This makes it slightly harder to test and slightly harder to understand.
It also makes the process tree shaped differently depending on the
string, with a child in one case where there is a grandchild in another.
I suppose it probably has implications for error handling as well
(although "sh -c" will propagate the exit code, I think).
- Logan
| |
| David Lee 2007-05-15, 10:04 pm |
| On Mon, 14 May 2007, Logan Shaw wrote:
> Perl, I believe, handles this issue internally with the following
> rules:
>
> (1) if there are no metacharacters (backslash, quotes, etc.),
> split on whitespace and pass the resulting list of tokens
> to exec().
>
> (2) if there are metacharacters, pass the list
> ( "sh", "-c", string_with_metacharacters ) to exec() instead.
>
> In this way, you get all the efficiency of doing it directly in a
> lot of cases, and in the other cases, you get the full flexibility
> of the shell. It also keeps the complexity of parsing out of
> your code.
>
> The di vantage is that the logic is different for different cases.
> This makes it slightly harder to test and slightly harder to understand.
> It also makes the process tree shaped differently depending on the
> string, with a child in one case where there is a grandchild in another.
> I suppose it probably has implications for error handling as well
> (although "sh -c" will propagate the exit code, I think).
Many thanks. Actually it was this "sh -c" process-treee behaviour that
lay in the background to my question. The application already does:
fork();
if (child) {
execl("/bin/sh", "sh", "-c", command, NULL);
}
else { /* parent*/
/* subsequently exercise control over child "command" */
}
But this seems to be causing portability issues. On Linux, that "/bin/sh"
(bash) appears to replace itself with "command" (that is, "command"
appears still to be the child itself). But on OSes (Solaris) where
"/bin/sh" is Bourne, that shell seems to do a further fork before doing
the command (that is, "command" seems to end up the grandchild).
(I'm reporting the above second hand, so feel free to confirm/refute!)
Hence our wanting to go directly to:
exec...(command, ...)
so that we will know exactly what we're getting.
--
: David Lee I.T. Service :
: Senior Systems Programmer Computer Centre :
: UNIX Team Leader Durham University :
: South Road :
: http://www.dur.ac.uk/t.d.lee/ Durham DH1 3LE :
: Phone: +44 191 334 2752 U.K. :
| |
| David Lee 2007-05-15, 10:04 pm |
| On Mon, 14 May 2007, [iso-8859-1] M=E5ns Rullg=E5rd wrote:
> David Lee <t.d.lee@durham.ac.uk> writes:
>
)"?[color=darkred]
>
> Maybe wordexp() is what you've been looking for all these years.
Great! At a quick glance that looks promising. And it seems to be
present on three sample systems I've just checked (Linux, Solaris,
FreeBSD). Thanks.
(I wonder how I never noticed it before?
--=20
: David Lee I.T. Service :
: Senior Systems Programmer Computer Centre :
: UNIX Team Leader Durham University :
: South Road :
: http://www.dur.ac.uk/t.d.lee/ Durham DH1 3LE :
: Phone: +44 191 334 2752 U.K. :
| |
| Geoff Clare 2007-05-15, 10:04 pm |
| David Lee wrote:
> The application already does:
>
> fork();
> if (child) {
> execl("/bin/sh", "sh", "-c", command, NULL);
> }
> else { /* parent*/
> /* subsequently exercise control over child "command" */
> }
>
> But this seems to be causing portability issues. On Linux, that "/bin/sh"
> (bash) appears to replace itself with "command" (that is, "command"
> appears still to be the child itself). But on OSes (Solaris) where
> "/bin/sh" is Bourne, that shell seems to do a further fork before doing
> the command (that is, "command" seems to end up the grandchild).
Maybe what you need is:
execl("/bin/sh", "sh", "-c", "exec command ...", (char *)NULL);
Of course this will only work if "command" is something that can be
exec-ed by the shell.
(Note that the (char *) cast I added is required for portability.)
--
Geoff Clare <netnews@gclare.org.uk>
| |
| Rainer Weikusat 2007-05-15, 10:04 pm |
| Geoff Clare <geoff@clare.See-My-Signature.invalid> writes:
> David Lee wrote:
>
>
> Maybe what you need is:
>
> execl("/bin/sh", "sh", "-c", "exec command ...", (char *)NULL);
>
> Of course this will only work if "command" is something that can be
> exec-ed by the shell.
>
> (Note that the (char *) cast I added is required for portability.)
That hasn't become more true since the last time. The last argument of
the execl-routine needs to be 'a null pointer'. NULL is an
implementation defined 'null pointer constant'. A 'null pointer
constant' is converted to 'a null pointer' whenever it is used in some
place where an expression of some pointer type is required. While the
type of the expansion of null may be a pointer type, it is not
specified that it must be a pointer type (eg it could be a plain 0 of
type int). That makes NULL unsuitable for use in places where
automatic conversions do not take place (like in arguments to variadic
routines) and casting it (or another null pointer constant, eg int 0
again) to a suitable pointer type is required for correctness. What
exactly a suitable pointer type is supposed to be is not specified by
SUS.
| |
| Bjorn Reese 2007-05-15, 10:04 pm |
| Rainer Weikusat wrote:
> That hasn't become more true since the last time. The last argument of
> the execl-routine needs to be 'a null pointer'. NULL is an
> implementation defined 'null pointer constant'. A 'null pointer
> constant' is converted to 'a null pointer' whenever it is used in some
> place where an expression of some pointer type is required. While the
> type of the expansion of null may be a pointer type, it is not
> specified that it must be a pointer type (eg it could be a plain 0 of
> type int). That makes NULL unsuitable for use in places where
> automatic conversions do not take place (like in arguments to variadic
> routines) and casting it (or another null pointer constant, eg int 0
> again) to a suitable pointer type is required for correctness. What
> exactly a suitable pointer type is supposed to be is not specified by
> SUS.
Isn't it obvious that the suitable pointer is "char *" (with or without
the const qualifier), given that arg1 to argN-1 have to be "const char
*"? Is there any way to implement execl() so that arg1 to argN-1 are
"char *" and argN is an incompatible pointer?
--
mail1dotstofanetdotdk
| |
| Rainer Weikusat 2007-05-16, 4:15 am |
| Bjorn Reese <breese@see.signature> writes:
> Rainer Weikusat wrote:
>
> Isn't it obvious that the suitable pointer is "char *" (with or without
> the const qualifier), given that arg1 to argN-1 have to be "const char
> *"?
It is a possible interpretation of the standard text. But this implies
that its content is not part of the standard text. This appears to be
an accidental omission.
| |
| Bjorn Reese 2007-05-16, 7:06 pm |
| Rainer Weikusat wrote:
> It is a possible interpretation of the standard text. But this implies
> that its content is not part of the standard text. This appears to be
> an accidental omission.
You are asking for a completely unnecessary addition. Show me an example
of execl() that reads arg1 to argN-1 as "char *" and argN as an
unsuitable pointer.
--
mail1dotstofanetdotdk
| |
| Rainer Weikusat 2007-05-17, 8:04 am |
| Bjorn Reese <breese@see.signature> writes:
> Rainer Weikusat wrote:
>
>
> You are asking for a completely unnecessary addition.
That you really believe your diviniation based on what is contained in
the text to be correct still doesn't make it a part of the
text. This text says 'a null pointer'. And nothing else. 'A null
pointer' is a pointer of unspecified type that resulted from
converting a null pointer constant to this unspecified type.
> Show me an example of execl() that reads arg1 to argN-1 as "char *"
> and argN as an unsuitable pointer.
If have no idea what you are talking about.
| |
| Bjorn Reese 2007-05-17, 8:04 am |
| Rainer Weikusat wrote:
> Bjorn Reese <breese@see.signature> writes:
>
> If have no idea what you are talking about.
Given that you question the advice to the user to use a "char *" cast
for argN (the terminating null pointer) when using execl(), you are
alluding that there may be execl() implementations that expect argN to
be of an incompatible type. For this allusion to carry any weigth you
must demonstrate that it is feasible to write such an implementation.
First, SUS states that "[t]he arguments represented by arg0,... are
pointers to null-terminated character strings." This is sufficient to
conclude that argN must be of the same (or a compatible) pointer type as
arg0, which is specified to be "const char *".
However, for the sake of argument, I shall indulge your interpretation
and assume argN may indeed be of an incompatible type, say, "float *".
The question is now, how does the execl() implementor implement this?
execl() is essentially a wrapper for execve(), so the problem can be
reduced to converting a variadic list into "char *argv[]". Let us
further simplify the problem by putting an upper limit on the number
of arguments.
#include <stdarg.h>
#include <unistd.h>
int my_execl(const char *path, const char *arg, ...)
{
va_list ap;
char *argv[1024];
char *env[1] = {0}; /* Not implemented */
int i;
/* Build argv */
va_start(ap, arg);
argv[0] = (char *)arg;
for (i = 1; i < sizeof(argv) / sizeof(argv[0]); ++i) {
argv[i] = va_arg(ap, char *);
if (argv[i] == 0)
break;
}
va_end(ap);
return execve(path, argv, env);
}
Here is my challenge to you: rewrite the above such that arg1 to argN-1
are read as "char *" and argN is read as "float *".
The basic problem you are facing is that you cannot determine if the
next argument is the terminating null pointer until after you have read
it with va_arg().
--
mail1dotstofanetdotdk
| |
| Casper H.S. Dik 2007-05-17, 8:04 am |
| Rainer Weikusat <rweikusat@mssgmbh.com> writes:
>That hasn't become more true since the last time. The last argument of
>the execl-routine needs to be 'a null pointer'. NULL is an
>implementation defined 'null pointer constant'. A 'null pointer
>constant' is converted to 'a null pointer' whenever it is used in some
>place where an expression of some pointer type is required. While the
>type of the expansion of null may be a pointer type, it is not
>specified that it must be a pointer type (eg it could be a plain 0 of
>type int). That makes NULL unsuitable for use in places where
>automatic conversions do not take place (like in arguments to variadic
>routines) and casting it (or another null pointer constant, eg int 0
>again) to a suitable pointer type is required for correctness. What
>exactly a suitable pointer type is supposed to be is not specified by
>SUS.
NULL is a "null pointer constant"; it is NOT a *pointer*.
You should argue this over in comp.std.c where they'll side on
"yes, the cast is required".
SUS is completely irrelevant; what is relevant is the fact that the
default type promotions are in effect, that "0" is a valid "null
pointer constant" but that the default type conversion rules will
see it as an "int" and that attempting to read it as a "char *" may
fail.
(And can be demonstrated to fail on some 64 bit architectures)
Casper
--
Expressed in this posting are my opinions. They are in no way related
to opinions held by my employer, Sun Microsystems.
Statements on Sun products included here are not gospel and may
be fiction rather than truth.
| |
| Casper H.S. Dik 2007-05-17, 8:04 am |
| Bjorn Reese <breese@see.signature> writes:
>Isn't it obvious that the suitable pointer is "char *" (with or without
>the const qualifier), given that arg1 to argN-1 have to be "const char
>*"? Is there any way to implement execl() so that arg1 to argN-1 are
>"char *" and argN is an incompatible pointer?
That's not the point; the point is that the compiled has no idea
what to do with the "null pointer constant" when passing it as
an argument. Since NULL can be defined as just a single zero (0),
not using the cast may cause the compiler to pass in a 32 bit 0 int value
instead of a 64 bit NULL pointer value.
Do you think the cast is needed here:
execl(......, (char *)0)
(using 0 as a NULL pointer constant)
Do you think the cast is needed here:
execl(......, (char *)NULL)
If the answers to both questions differ, then why?
(Both are null pointer constants according to the C standard)
If you think the answer two both questions is "no", then why?
Casper
--
Expressed in this posting are my opinions. They are in no way related
to opinions held by my employer, Sun Microsystems.
Statements on Sun products included here are not gospel and may
be fiction rather than truth.
| |
| Casper H.S. Dik 2007-05-17, 8:04 am |
| Bjorn Reese <breese@see.signature> writes:
>Rainer Weikusat wrote:
[color=darkred]
>You are asking for a completely unnecessary addition. Show me an example
>of execl() that reads arg1 to argN-1 as "char *" and argN as an
>unsuitable pointer.
Not relevant.
Casper
--
Expressed in this posting are my opinions. They are in no way related
to opinions held by my employer, Sun Microsystems.
Statements on Sun products included here are not gospel and may
be fiction rather than truth.
| |
| Casper H.S. Dik 2007-05-17, 8:04 am |
| Bjorn Reese <breese@see.signature> writes:
>Here is my challenge to you: rewrite the above such that arg1 to argN-1
>are read as "char *" and argN is read as "float *".
Here's my challenge to you:
write the code such that it works when the last argument passed
in is a 0. (A null pointer constant).
Portably, on all platforms.
Casper
--
Expressed in this posting are my opinions. They are in no way related
to opinions held by my employer, Sun Microsystems.
Statements on Sun products included here are not gospel and may
be fiction rather than truth.
| |
| Rainer Weikusat 2007-05-17, 8:04 am |
| Casper H.S. Dik <Casper.Dik@Sun.COM> writes:
> Rainer Weikusat <rweikusat@mssgmbh.com> writes:
>
> NULL is a "null pointer constant"; it is NOT a *pointer*.
Quoting myself:
NULL is an implementation defined 'null pointer constant'.
[...]
While the type of the expansion of null may be a pointer type
(void *)0 is a null pointer constant as well and one that does have a
pointer type.
> SUS is completely irrelevant; what is relevant is the fact that the
> default type promotions are in effect, that "0" is a valid "null
> pointer constant" but that the default type conversion rules will
> see it as an "int" and that attempting to read it as a "char *" may
> fail.
Again quoting myself:
A 'null pointer constant' is converted to 'a null pointer'
whenever it is used in someplace where an expression of some
pointer type is required. While the type of the expansion of
null may be a pointer type, it is not specified that it must
be a pointer type (eg it could be a plain 0 of type int). That
makes NULL unsuitable for use in places where automatic
conversions do not take place
Could you please enlighten me why you are replying to my text with
statements whose meanings are contained in this text?
| |
| Rainer Weikusat 2007-05-17, 8:04 am |
| Bjorn Reese <breese@see.signature> writes:
> Rainer Weikusat wrote:
>
> Given that you question the advice to the user to use a "char *" cast
> for argN (the terminating null pointer) when using execl(), you are
> alluding that there may be execl() implementations that expect argN to
> be of an incompatible type.
I have (repeatedly) stated that the execl-definition does not specifiy
a type for the final null pointer (as of date of this post). This is
an easily verifable fact, look it up yourself.
| |
| Thomas Dickey 2007-05-17, 8:04 am |
| Casper H.S. Dik <Casper.Dik@sun.com> wrote:
> Do you think the cast is needed here:
> execl(......, (char *)0)
On some systems (I don't recall that they include Solaris), the compiler's
nice enough to point out if the terminating zero is not really a pointer.
--
Thomas E. Dickey
http://invisible-island.net
ftp://invisible-island.net
| |
| Bjorn Reese 2007-05-18, 10:03 pm |
| Casper H.S. Dik wrote:
> Here's my challenge to you:
>
> write the code such that it works when the last argument passed
> in is a 0. (A null pointer constant).
>
> Portably, on all platforms.
Have I ever argued that it is possible?
--
mail1dotstofanetdotdk
| |
| Bjorn Reese 2007-05-18, 10:03 pm |
| Casper H.S. Dik wrote:
> That's not the point; the point is that the compiled has no idea
> what to do with the "null pointer constant" when passing it as
> an argument. Since NULL can be defined as just a single zero (0),
> not using the cast may cause the compiler to pass in a 32 bit 0 int value
> instead of a 64 bit NULL pointer value.
>
> Do you think the cast is needed here:
>
> execl(......, (char *)0)
>
> (using 0 as a NULL pointer constant)
>
> Do you think the cast is needed here:
>
> execl(......, (char *)NULL)
>
> If the answers to both questions differ, then why?
> (Both are null pointer constants according to the C standard)
>
> If you think the answer two both questions is "no", then why?
I get the feeling that you may have misunderstood me completely. I agree
that the (char *) cast is needed in both cases. I have not claimed
otherwise.
I initially responded to this discussion because Rainer claimed that the
(char *) cast was not necessarily portable because SUS, according to his
strict interpretation, only specifies that argN must be a null pointer
but not of what type, which means that there may theoretically be an
execl() implemention that could requires a (float *) cast for argN, e.g.
execl(......, (float *)0);
The point that I was trying to make is that even if you want to, you
cannot make such an implementation because of the way variadic lists
work, and therefore that the (char *) cast is a portable solution.
--
mail1dotstofanetdotdk
| |
| Bjorn Reese 2007-05-18, 10:03 pm |
| Rainer Weikusat wrote:
> I have (repeatedly) stated that the execl-definition does not specifiy
> a type for the final null pointer (as of date of this post). This is
> an easily verifable fact, look it up yourself.
First, I notice that you have failed to respond to my challenge, so I
assume that you have been unable to find a solution.
Second, I know exactly what your claim is, but I disagree with your
very strict interpretation of SUS.
SUS states:
"The arguments represented by arg0,... are pointers to null-terminated
character strings. These strings shall constitute the argument list
available to the new process image. The list is terminated by a null
pointer."
I interpret the above as meaning that all arguments, including the
terminating null pointer, must be compatible with "char *". This
interpretation is supported by:
1) The use of "char *" in the synopsis.
2) The use of the term "null pointer" elsewhere in the description
of execl() (see [*].)
3) The consistent use of "char *" in all examples.
4) The fact that you cannot write an execl() implementation with
incompatible pointers even if you wanted to.
If you still insist on your very strict interpretation, then I suggest
that you submit a defect report to the Open Group.
[*] SUS also states that:
"The argv and environ arrays are each terminated by a null pointer."
and
"The argument argv is an array of character pointers to null-terminated
strings. The application shall ensure that the last member of this
array is a null pointer."
where argv is defined as "char *argv[]" and environ as "extern char **
environ". Are you going to tell us that these null pointers also can be
of an arbitrary pointer type as well?
--
mail1dotstofanetdotdk
| |
| Rainer Weikusat 2007-05-18, 10:03 pm |
| Bjorn Reese <breese@see.signature> writes:
> Rainer Weikusat wrote:
>
> First, I notice that you have failed to respond to my challenge, so I
> assume that you have been unable to find a solution.
I didn't even read it, because 'possible implementation behaviour'
isn't relevant for 'specification content' (:-> ).
> Second, I know exactly what your claim is, but I disagree with your
> very strict interpretation of SUS.
I have provided no 'interpretation'. That was the difference.
> SUS states:
>
> "The arguments represented by arg0,... are pointers to null-terminated
> character strings. These strings shall constitute the argument list
> available to the new process image. The list is terminated by a null
> pointer."
And 6.3.2.3|3 if ISO/IEC 9899:1999 (E) states that
An integer constant expression with the value 0, or such an
expression cast to type void *, is called a null pointer
constant. If a null pointer constant is converted to a
pointer type, the resulting pointer, called a null pointer, is
guaranteed to compare unequal to a pointer to any object or
function.
This means 'a null pointer' has an underspecified pointer type (it is
know that it is a pointer type, but the type of the pointed-to object
is unknown).
> I interpret the above as meaning that all arguments, including the
> terminating null pointer, must be compatible with "char *".
This depends entirely on the implementation of execl, because the text
does not specify so (presumably, by omission).
> This interpretation is supported by:
>
> 1) The use of "char *" in the synopsis.
The synposis contains the comment /* (char *)0 */. No explanation is
given as to what this comment is supposed to mean. Especially (and
that used to be one of Mr Lane's claims), nothing is detailed wrt to
using (void *)0 (required to have the same representation and
alignment (char *)0) or (any_type *)0 (required to compare equal to
any other null pointer and, as of some XPG extension, IIRC, required
to have the same representation as all other pointer types).
> 2) The use of the term "null pointer" elsewhere in the description
> of execl() (see [*].)
> 3) The consistent use of "char *" in all examples.
> 4) The fact that you cannot write an execl() implementation with
> incompatible pointers even if you wanted to.
>
> If you still insist on your very strict interpretation, then I suggest
> that you submit a defect report to the Open Group.
>
> [*] SUS also states that:
>
> "The argv and environ arrays are each terminated by a null pointer."
>
> and
>
> "The argument argv is an array of character pointers to null-terminated
> strings. The application shall ensure that the last member of this
> array is a null pointer."
>
> where argv is defined as "char *argv[]" and environ as "extern char **
> environ". Are you going to tell us that these null pointers also can be
> of an arbitrary pointer type as well?
In absence of information, I just don't know. Especially (as outlined
above), I don't know if the intended meaning of the text included that
char * is possibly different from other pointer types.
| |
| Bjorn Reese 2007-05-19, 10:06 pm |
| Rainer Weikusat wrote:
> Bjorn Reese <breese@see.signature> writes:
>
> I didn't even read it, because 'possible implementation behaviour'
> isn't relevant for 'specification content' (:-> ).
I am puzzled. Here implementation is irrelevant, but later in your reply
it becomes relevant again...
> I have provided no 'interpretation'. That was the difference.
Yes you have. In your interpretation you ignore context.
[...][color=darkred]
> This means 'a null pointer' has an underspecified pointer type (it is
> know that it is a pointer type, but the type of the pointed-to object
> is unknown).
This is where you ignore context. You focus narowly on the single
sentence "[t]the list is terminated by a null pointer". If that was the
only sentence describing the arguments to execl(), then I would agree
with you, but it is not. The preceding statements specify the type, or
may you can explain to me how "pointers to null-terminated character
strings" can be interpreted as "any_type *"?
>
> This depends entirely on the implementation of execl, because the text
> does not specify so (presumably, by omission).
Now that you admit that the execl() implementation is relevant after
all, maybe you could return to my challenge. Show me an implementation
of execl() where "(char *)0" does not work for argN.
> In absence of information, I just don't know. Especially (as outlined
> above), I don't know if the intended meaning of the text included that
> char * is possibly different from other pointer types.
All information is there, you just refuse to see it. Btw, C99 also uses
the term "null pointer" without specifying its type [5.1.2.2.1/2].
--
mail1dotstofanetdotdk
| |
| Rainer Weikusat 2007-05-20, 8:05 am |
| Bjorn Reese <breese@see.signature> writes:
> Rainer Weikusat wrote:
>
> I am puzzled. Here implementation is irrelevant, but later in your reply
> it becomes relevant again...
Not in this way.
>
>
> Yes you have. In your interpretation you ignore context.
Nice. So your claim is that by not interpreting the context I provided
an 'interpretation' with the 'interpretation property' 'no
interpretation'?
> [...]
>
> This is where you ignore context. You focus narowly on the single
> sentence "[t]the list is terminated by a null pointer".
Exactly. It happens to be the single sentence that contains the
information about the type of this pointer. Since I am not trying to
'interpret' it away, I don't need to cover it with unrelated
statements.
> If that was the only sentence describing the arguments to execl(),
> then I would agree with you, but it is not. The preceding statements
> specify the type, or may you can explain to me how "pointers to
> null-terminated character strings" can be interpreted as "any_type
> *"?
I am convinced that you would find a way, if you so desired. This is a
statement about the other arguments.
>
> Now that you admit that the execl() implementation is relevant after
> all,
The execl-implementation is relevant to determine behaviour of the
execl implementation.
> maybe you could return to my challenge. Show me an implementation
> of execl() where "(char *)0" does not work for argN.
int execl(char *p, ...)
{
puts("Why would I want to work for you?");
return -1;
}
>
> All information is there, you just refuse to see it. Btw, C99 also uses
> the term "null pointer" without specifying its type [5.1.2.2.1/2].
Since this null pointer is supposed to be the last member of a char
**, its type is specified (char *).
There is actually a sensible interpretation of this specification, and
it would be "since UNIX(*) never had different pointer types, the
person who wrote the spec didn't consider it necessary to document
generally available knowledge".
| |
|
|
|
|
| Bjorn Reese 2007-05-21, 10:06 pm |
| Rainer Weikusat wrote:
>
> I am convinced that you would find a way, if you so desired. This is a
> statement about the other arguments.
Where does it say it say that? "The arguments represented by arg0,..."
does not exclude the last argument.
>
> int execl(char *p, ...)
> {
> puts("Why would I want to work for you?");
> return -1;
> }
You are dodging the issue. Here is the challenge once more:
#include <stdarg.h>
#include <unistd.h>
int my_execl(const char *path, const char *arg, ...)
{
va_list ap;
char *argv[1024];
char *env[1] = {0}; /* Not implemented */
int i;
/* Build argv */
va_start(ap, arg);
argv[0] = (char *)arg;
for (i = 1; i < sizeof(argv) / sizeof(argv[0]); ++i) {
argv[i] = va_arg(ap, char *);
if (argv[i] == 0)
break;
}
va_end(ap);
return execve(path, argv, env);
}
Here is my challenge to you: rewrite the above such that arg1 to argN-1
are read as "char *" and argN is read as "float *".
--
mail1dotstofanetdotdk
| |
| Rainer Weikusat 2007-05-22, 8:05 am |
| Bjorn Reese <breese@see.signature> writes:
> Rainer Weikusat wrote:
>
> Where does it say it say that? "The arguments represented by arg0,..."
> does not exclude the last argument.
Taking into account that the complete sentence is "The arguments
represented by arg0,... are pointers to null-terminated character
strings.", you appear to claim that 'a null-terminated character
string' and 'a null pointer' are the same thing. Which is wrong.
Sophism 101: Don't rely on others not reading the text.
| |
| Bjorn Reese 2007-05-23, 7:07 pm |
| Rainer Weikusat wrote:
> Taking into account that the complete sentence is "The arguments
> represented by arg0,... are pointers to null-terminated character
> strings.", you appear to claim that 'a null-terminated character
> string' and 'a null pointer' are the same thing. Which is wrong.
Let me try one more time...
SUS tells us that arg0 to argN, where argN is the last argument of
execl(), are "pointers to null-terminated character strings". No
exceptions for argN are mentioned. Given that arg0 is of type "const
char *", we can conclude that "pointers to null-terminated character
strings" are of type "const char *" (or "char *" if we ignore
qualification). So, SUS requires that the null pointer in quesion
(argN) must be of that type as well. This means that the "(char *)0"
cast for argN is a portable solution.
If you disagree with the conclusion above, the same conclusion can be
reached directly from the "both types are pointers" exception in
<stdarg.h>.
Either way, do you agree that the "(char *)0" cast for argN is a
portable solution?
--
mail1dotstofanetdotdk
| |
| Rainer Weikusat 2007-05-23, 7:07 pm |
| Bjorn Reese <breese@see.signature> writes:
> Rainer Weikusat wrote:
>
> Let me try one more time...
The reasoning isn't going to become less faulty by repeating it.
>
> SUS tells us that arg0 to argN, where argN is the last argument of
> execl(), are "pointers to null-terminated character strings".
argN is not 'the last argument to execl' but the last of the 'pointers
to null-terminated character strings' that precede it.
> No exceptions for argN are mentioned.
No 'argN' is mentioned.
> Given that arg0 is of type "const char *", we can conclude that
> "pointers to null-terminated character strings" are of type "const
> char *" (or "char *" if we ignore qualification).
Given that a 'pointer to a null-terminated character string' is
necessarily a somehow qualified char * by definition, this isn't a
conclusion but a noise sentence.
> So, SUS requires that the null pointer in quesion (argN) must be of
> that type as well.
Since 'a null pointer' cannot be 'a pointer to a null-terminated
character string', statements about 'pointers to null-terminated
character strings' cannot refer to it.
> This means that the "(char *)0" cast for argN is a portable
> solution.
Since the '(char *)0' is provided in a code example, this is again a
priori true. But this was never doubted by me, the statement '(char
*)0 is required for portability' was.
> If you disagree with the conclusion above,
There is no 'conclusion' above.
> the same conclusion can be reached directly from the "both types are
> pointers" exception in <stdarg.h>.
Leaving the digression about 'magic execl implementations which are not
forbidden' aside, what "follows" from this text is that
execl(..., (long double *)(unsigned long long *)(uintptr_t)(int *)0)
should be as portable, only more cumbersome to write down, because it
has a pointer type, 'a null pointer', ie '0 converted to some pointer
type' is required and both the type above and the unspecified type of
the required null pointer must be compatible because they are both
pointers.
| |
| Bjorn Reese 2007-05-24, 7:07 pm |
| Rainer Weikusat wrote:
> Since the '(char *)0' is provided in a code example, this is again a
> priori true. But this was never doubted by me, the statement '(char
> *)0 is required for portability' was.
I am about your position now, and would need a clarification
before I can address your other comments. Which of the following last
arguments to execl() would you say are portable solutions?
1. execl(..., 0)
2. execl(..., (char *)0)
3. execl(..., (anytype_t *)0)
Is your quarrel that you interpret the word "required" in Geoff's
statement as meaning that only solution 2 is correct?
--
mail1dotstofanetdotdk
| |
| Rainer Weikusat 2007-05-25, 4:18 am |
| Bjorn Reese <breese@see.signature> writes:
> Rainer Weikusat wrote:
>
> I am about your position now, and would need a clarification
> before I can address your other comments. Which of the following last
> arguments to execl() would you say are portable solutions?
>
> 1. execl(..., 0)
> 2. execl(..., (char *)0)
> 3. execl(..., (anytype_t *)0)
A 'portable solution' would be one where the 0 is casted to a pointer
type that is compatible with the pointer type 'expected' by the
execl-implementation. What 'a compatible pointer type' is exactly
supposed to be isn't directly specified. char * is used at least used
in an example in the text, so the assumption that char * is one is
very likely correct. Since void * is required to be an alias for char
* with different C-level semantics, it should be compatible,
too. Anything beyond that depends on the implementation, namely on
- does the architecure have different pointer types for
different pointed-to types
- is the compiler used willing to admit that or has someone
coded a certain 'type-policy' into it (like the current gcc
nonsense that 'signed integers are an infinite set, dammit')
The latter one is an especially serious case (I am not joking),
because, in absence of a simple 'do it this way' requirement, there is
no a-priori method to determine which 'execl'-calls will be purposely
miscompiled by some future gcc-version, because somebody has made an
arbitrary descision supplanting the standard text.
| |
| Bjorn Reese 2007-05-26, 8:04 am |
| Rainer Weikusat wrote:
First of all, thanks for the clarification.
> A 'portable solution' would be one where the 0 is casted to a pointer
> type that is compatible with the pointer type 'expected' by the
> execl-implementation. What 'a compatible pointer type' is exactly
> supposed to be isn't directly specified. char * is used at least used
It is specified. The signature of execl() is defined in the synopsis as:
int execl(const char *path, const char *arg0, ... /*, (char *)0 */);
The "arg0,..." in the text "[t]he arguments represented by arg0,... are
pointers to null-terminated character strings" refers to the second
parameter (arg0) of execl() and subsequent parameters.
Our disagreement is whether or not the terminating null pointer is
included in the subsequent parameters (the "..." part).
In my interpretation the null pointer is part of "arg0,...", and
therefore it must be a null pointer of a type compatible with arg0
(that is "char *", "void *", or any const qualified variation).
In your interpretation the the null pointer is not part of "arg0,...",
and therefore its type is unspecified. However, in this case the null
pointer can only refer to "/*, (char *)0 */" in the synopsis (if you
disagree, then you have to explain to me how to interpret the ellipses
in the synopis and in the text, and why their have different meanings),
and here it is specified to be "char *".
Either way, the type of the null pointer is specified.
--
mail1dotstofanetdotdk
| |
| Bjorn Reese 2007-05-31, 7:08 pm |
| Rainer Weikusat wrote:
> Bjorn Reese <breese@see.signature> writes:
[color=darkred]
> This is your 'disagreement' with the content of this text. Since arg0,
> ... are pointers to null-terminated character strings, none of them
> can be a null pointer. Which the text explicitly states:
Why not? You have already agreed that a "pointer to a null-terminated
character string" is a "somehow qualified char * by definition" (and I
furthermore assume that you agree that "(char *)0" is a null pointer),
so the following text can only be interpreted as meaning that the
terminating null pointer is of type "somehow qualified char *". Unless,
of course, you can explain the different semantics of the ellipses (see
my next comment below).
> The arguments represented by arg0,... are pointers to
> null-terminated character strings. These strings shall
> constitute the argument list available to the new process
> image. The list is terminated by a null pointer.
[...]
>
> This is a comment and it has no meaning that would be defined by the
> used programming language. In absence of additional information (and
Then you need to explain how the "arg0, ..." in the signature is
different from the "arg0,..." in the text (both parts have been quoted
above), and why the ellipsis in the signature includes the null pointer
but the ellipsis in the text does not.
--
mail1dotstofanetdotdk
| |
| Rainer Weikusat 2007-05-31, 7:08 pm |
| Bjorn Reese <breese@see.signature> writes:
> Rainer Weikusat wrote:
>
>
>
> Why not?
Because ISO says so.
| |
| Bjorn Reese 2007-06-05, 7:06 pm |
| Rainer Weikusat wrote:
> Because ISO says so.
That is irrelevant, because the type of the null pointer can be
determined from SUS. Your refusal to acknowledge this is based on
the assumption that the two ellipses by some stroke of magic have
different meanings. Let me repeat the question from my previous reply
that you ignored:
You need to explain how the "arg0, ..." in the signature in the SUS
synopsis is different from the "arg0,..." in the SUS text (both parts
have been quoted below), and why the ellipsis in the signature includes
the null pointer but the ellipsis in the text does not.
SUS synopsis:
int execl(const char *path, const char *arg0, ... /*, (char *)0 */);
SUS text:
"The arguments represented by arg0,... are pointers to null-terminated
character strings. These strings shall constitute the argument list
available to the new process image. The list is terminated by a null
pointer."
--
mail1dotstofanetdotdk
| |
| Rainer Weikusat 2007-06-05, 7:06 pm |
| Bjorn Reese <breese@see.signature> writes:
> Rainer Weikusat wrote:
>
>
> That is irrelevant, because the type of the null pointer can be
> determined from SUS.
'What ISO says' is that a null pointer compares unequal to any valid
pointer and since a 'pointer to a null-terminated string' is a valid
pointer, a statement talking about pointers to null terminated strings
does not talk about null pointers.
[...]
> and why the ellipsis in the signature includes the null pointer but
> the ellipsis in the text does not.
"The arguments represented by arg0,... are pointers to null-terminated
character strings. These strings shall constitute the argument list
available to the new process image. The list is terminated by a null
pointer."
Taking 'never attribute to malice what could equally well be explained
by mere stupidity' as starting positions, I strongly suggest that you
try to get enough English lessons to enable you to understand the
sentence above correctly.
| |
| Bjorn Reese 2007-06-10, 4:15 am |
| Rainer Weikusat wrote:
> "The arguments represented by arg0,... are pointers to null-terminated
> character strings. These strings shall constitute the argument list
> available to the new process image. The list is terminated by a null
> pointer."
>
> Taking 'never attribute to malice what could equally well be explained
> by mere stupidity' as starting positions, I strongly suggest that you
> try to get enough English lessons to enable you to understand the
> sentence above correctly.
Consider the following parallel:
"All cars in the queue are green. The people in the cars are on the
way to the beach. An Audi is at the end of the queue."
Which of the following conclusions would you make about that Audi at
the end of the queue?
1. "The Audi is green"
2. "The Audi is not necessarily green, because the Audi catalogue
shows Audis in all colors"
--
mail1dotstofanetdotdk
| |
| Henry Townsend 2007-06-10, 4:15 am |
| Bjorn Reese wrote:
> Which of the following conclusions would you make about that Audi at
> the end of the queue?
>
> 1. "The Audi is green"
>
> 2. "The Audi is not necessarily green, because the Audi catalogue
> shows Audis in all colors"
3. "Audi makes bicycles too"? :-) Seriously, I'm with you on this one.
HT
| |
| Rainer Weikusat 2007-06-10, 8:05 am |
| Bjorn Reese <breese@see.signature> writes:
> Rainer Weikusat wrote:
>
>
> Consider the following parallel:
There is nothing parallel here. The text above talks about a list of
pointers to null terminated strings, making up the argument list
available to the new process image. And it talks about 'a null
pointer', which terminates the list, ie which is right behind the last
pointer to a null-terminated string. Since every pointer to a null
terminated string must be a valid pointer and 'a null pointer' is one
with the property to be different from any valid pointer, it is
trivially obvious that a pointer to a null terminated strings must be
something which is not a null pointer.
But since this leads nowhere, I suggest that you just continue to
believe that null pointers are valid pointers to null terminated
strings. Some people believe in the tooth fairy. So what?
| |
|
|
| Bjorn Reese 2007-06-19, 7:09 pm |
| Rainer Weikusat wrote:
> This 'elipsis dilemma' is fictious.
It is real, and yours to solve.
> Then you should have no difficulties in demonstrating which part of it
> is wrong because of what.
The argument would have been fine if the type of the terminating null
pointer could not have been determined, but it can and so the entire
argument is void. Let me go through the argument again.
You postulate that the type of the terminating null pointer is
unspecified, because the statement that "[t]he arguments represented
by arg0,... are pointers to null-terminated character strings" does
not apply to the terminating null pointer, because the null pointer
cannot point to a null-terminated string.
However, the quoted statement does not limit itself to the arguments
from arg0 until and excluding the terminating null pointer argument;
it includes _all_ arguments from arg0 onwards; hence the very explicit
"arg0,..." notation. So, the terminating null pointer must have the
same type has the other arguments (from arg0 onwards). This means that
"pointers to null-terminated character strings" must be interpreted as
a "somehow qualified char *" (which you vividly agreed with earlier on).
So the type of the null pointer is a "somehow qualified char *".
As the type of the terminating null pointer can be deduced from SUS,
your postulate that the type is not specified is irrelevant and void.
The Audi is green, no matter what other colors are in the catalogue.
The only way you can defend your postulate is to show that the quoted
statement does not apply to the terminating null pointer, and it order
to do that you must explain how the "arg0,..." in the text differs from
the "arg0, ..." in the signature, or you must explain where in the
signature this magical type-unspecified terminating null pointer occurs.
That is your ellipses (note the plural form) dilemma.
> I am not going to address this again.
You never have.
--
mail1dotstofanetdotdk
| |
| Rainer Weikusat 2007-06-19, 7:09 pm |
| Bjorn Reese <breese@see.signature> writes:
> Rainer Weikusat wrote:
>
> The argument would have been fine if the type of the terminating null
> pointer could not have been determined, but it can and so the entire
> argument is void. Let me go through the argument again.
>
> You postulate that the type of the terminating null pointer is
> unspecified, because the statement that "[t]he arguments represented
> by arg0,... are pointers to null-terminated character strings" does
> not apply to the terminating null pointer, because the null pointer
> cannot point to a null-terminated string.
Exactly. Since all the things refererred to by the ellipsis are
'pointers to null-terminated character strings', the thing which isn't
a 'pointer to a null-terminated character string' is not included in
this set.
> However, the quoted statement does not limit itself to the arguments
> from arg0 until and excluding the terminating null pointer argument;
The text actually says so on two different places:
- by implicitly excluding the terminating null pointer because
the arg0, ... are pointers to null-terminated character
strings without a mentioned exception
- by explicitly stating that the arg0,... list is terminated
of pointers to null-terminated character strings is
'terminated by a null pointer' (not being part of this list).
> it includes _all_ arguments from arg0 onwards;
It includes all argument which are pointers to null-terminated strings
preceding the terminating null pointer. The repeatedly quoted text say
so.
[...]
> The only way you can defend your postulate is to show that the quoted
> statement does not apply to the terminating null pointer, and it order
> to do that you must explain how the "arg0,..." in the text differs from
> the "arg0, ..." in the signature,
Well, it doesn't. The arg0,... in the signature does not include a
terminating null pointer, by virtue of not including anything expcept
arg0, and the arg0,... in the text is defined as not including a null
pointer by virtue of being defined as list of pointers to null-terminated
strings.
> or you must explain where in the signature this magical
> type-unspecified terminating null pointer occurs.
Nowhere. There is a comment which can be interpreted as meaning that a
null pointer of type pointer to char can be the terminating argument.
But since the comment isn't explained anywhere, that's a coniecture.
A comment has no defined meaning.
>
> You never have.
The basic problem here is that you really want to believe that
something exists in this text which isn't there and that I am really
incapable of seeing things which don't exist (which makes me totally
unqualified for being either a politician or a sales person of any
kind :-> ).
And I don't think this will change in either near or far future.
|
|
|
|
|