pointer from integer without a cast

R

Rudra Banerjee

Dear friends,
I am a C novice. I am trying to get the basename of a file(filename) without extension. The problem is much discussed in net, but due to my limited knowledge, I failed to make fruitful use of them, and thought I may use the unix command, as given.

char myCommand[512];
// char *basetmp=strrchr(filename,'/')+1;
sprintf(myCommand,"basename %s .bib",filename);
char *basefn=system((char *)myCommand);
printf (basefn);
variable filename is defined as char*.
This code is giving warnning:308:18: warning: initialization makes pointer from integer without a cast [enabled by default]
where line no 308 is "char *basefn=system((char *)myCommand);"

If anybody kindly show me where I am getting wrong, or better still, a better(and small) method to get the same function without unix command, it will be of huge help.
 
B

Barry Schwarz

Dear friends,
I am a C novice. I am trying to get the basename of a file(filename) without extension. The problem is much discussed in net, but due to my limited knowledge, I failed to make fruitful use of them, and thought I may use the unix command, as given.

char myCommand[512];
// char *basetmp=strrchr(filename,'/')+1;
sprintf(myCommand,"basename %s .bib",filename);
char *basefn=system((char *)myCommand);
printf (basefn);
variable filename is defined as char*.
This code is giving warnning:308:18: warning: initialization makes pointer from integer without a cast [enabled by default]
where line no 308 is "char *basefn=system((char *)myCommand);"

If anybody kindly show me where I am getting wrong, or better still, a better(and small) method to get the same function without unix command, it will be of huge help.

The C function system() does not return the "output" of the command it
executes. It returns an implementation-defined "status" which has
type int. (Most commands return 0 status to indicate success.)

In this case, the output from basename probably went directly to your
screen. One way for you to get the output, if your system supports
redirection (>>), would be to add " >> basename_output_file.txt" to
your sprintf format string and then open the file and read the output.

By the way, your cast in the call to system() serves no purpose.
myCommand is an array of char. In this context, the expression
myCommand is automatically converted to &myCommand[0] which already
has type char*.
 
J

James Kuyper

Dear friends,
I am a C novice. I am trying to get the basename of a file(filename) without extension. The problem is much discussed in net, but due to my limited knowledge, I failed to make fruitful use of them, and thought I may use the unix command, as given.

char myCommand[512];
// char *basetmp=strrchr(filename,'/')+1;
sprintf(myCommand,"basename %s .bib",filename);
char *basefn=system((char *)myCommand);
printf (basefn);
variable filename is defined as char*.
This code is giving warnning:308:18: warning: initialization makes pointer from integer without a cast [enabled by default]
where line no 308 is "char *basefn=system((char *)myCommand);"

If anybody kindly show me where I am getting wrong,

The value returned by system() is an int. Your code tries to implicitly
convert that int to a pointer, which is not permitted, hence the warning
message. Such conversions can only be done explicitly by using a cast.
Inserting (int) before system() would disable the warning message, but
would not solve your problem. The value returned by the system() command
when it is given a non-null pointer argument is an
implementation-defined status code, so converting it to a pointer is
pointless, and could cause your program to crash.

I'm familiar with the basename command as a standard utility available
on unix-like systems like the ones I use at home and at work. I've not
idea whether other systems provide a similar command with the same name.
The rest of this message is specific to my desktop system, and if not
applicable to your system, should be ignored. If the advice is
applicable to your system, any questions should be directed to
comp.programmer.unix, rather than this forum. There are people there who
can give you better answers than I can about such systems.

system() returns "-1 on error (e.g. fork() failed), and the return
status of the command otherwise. This latter return status is in the
format specified in wait(2)." That's not what you want.

What you really want is to capture the string that gets printed out by
the basename command, but capturing that string is a complicated task
that would start with using popen() rather than system(), and gets
steadily more complicated from there. I'd recommend someday learning how
to do that, because someday you might need to, but this isn't that day;
wait until you no longer call yourself a "novice".

You're much better off using the basename() function, rather than
system() and the basename command. Type "man 3 basename" at your command
line for more information.
 
L

Lew Pitcher

Dear friends,
I am a C novice. I am trying to get the basename of a file(filename)
without extension. The problem is much discussed in net, but due to my
limited knowledge, I failed to make fruitful use of them, and thought I
may use the unix command, as given.

char myCommand[512];
// char *basetmp=strrchr(filename,'/')+1;
sprintf(myCommand,"basename %s .bib",filename); [snip
If anybody kindly show me where I am getting wrong, or better still, a
better(and small) method to get the same function without unix command, it
will be of huge help.

The POSIX standard defines a C function called basename() that, on Unix-like
platforms, does most of what you want to do. But, as we are discussing your
problem in comp.lang.c, we'll not discuss POSIX functions; they are best
left for discussion in a POSIX, Unix, or Linux newsgroup.

OTOH, if you know the rules for filename (and presumably pathname) creation
on your platform (like, "paths are names separated by slashes", or "a slash
at the rightmost end of the name can be ignored"), and the rules for the
basename utility program (like, the 2nd parameter is a filename suffix),
*you* can write the equivalent processing in standard C.

Working from right to left (from end-of-string to start-of-string)
- delete the trailing slash if present
- delete the file suffix if present
- find the leftmost slash, or start of string

Your filename starts +1 from that leftmost slash (or +0 from the start of
string), and ends just prior to that file suffix you deleted.
This is /easily/ coded in C, without any function calls.

HTH
 
L

Lew Pitcher

Too quick to post without proofreading

[snip]
Working from right to left (from end-of-string to start-of-string)
- delete the trailing slash if present
- delete the file suffix if present
- find the leftmost slash, or start of string

Gaak! Correction
- find the RIGHTMOST slash, or start of string
Your filename starts +1 from that RIGHTMOST slash (or +0 from the start of
string), and ends just prior to that file suffix you deleted.
 
B

Ben Bacarisse

Rudra Banerjee said:
Dear friends, I am a C novice. I am trying to get the basename of a
file(filename) without extension. The problem is much discussed in
net, but due to my limited knowledge, I failed to make fruitful use of
them, and thought I may use the unix command, as given.
char myCommand[512];
// char *basetmp=strrchr(filename,'/')+1;
sprintf(myCommand,"basename %s .bib",filename);
char *basefn=system((char *)myCommand);
printf (basefn);

NOTE: that's dangerous! All hell will break loose if the file name
includes %s for example.
variable filename is defined as char*.
This code is giving warnning:308:18: warning: initialization makes
pointer from integer without a cast [enabled by default] where line no
308 is "char *basefn=system((char *)myCommand);"

If anybody kindly show me where I am getting wrong, or better still, a
better(and small) method to get the same function without unix
command, it will be of huge help.

For the Unix answer, see if your system has a basename function. To
find out more about it, ask in comp.unix.programmer.

There's a C answer as well, because the problem is just one of
manipulating characters so here is an explanation of a simple way to do
what you want.

You are on the right track with strrchr but exactly what will work for
you depends on what you want to do with the string later and where the
file name came from to start with.

The most flexible method will make a copy of the resulting shortened
file name. The only thing you then need to worry about is freeing the
storage when you no longer need it.

Here are the key parts:

strrchr can find the last / in the name, but it may return NULL if
there was no / to find:

const char *basename = strrchr(filename, '/');
if (basename == NULL)
basename = filename;
else basename += 1; // point to after the '/' just found

strrchr can then also find the . that marks a possible extension.
Again you need to check if there really is one:

const char *end = strrchr(basename, '.');
if (end == NULL)
end = strrchr(basename, 0); // can't fail

If you specifically want to remove only ".bib" it's a little bit more
complicated because you might have a name like "my.bib.file.bib". The
following would work:

const char *end = strchr(basename, 0);
if (strlen(basename) >= 4 && strcmp(end - 4, ".bib") == 0)
end -= 4;

Subtracting two pointers into an array (a string is just an array of
characters) gives you the number of elements between them. To make a
copy of a part of the string, you can use strndup. It's not standard
C, but you don't seem to be looking for a fully standard solution:

return strndup(basename, end - basename);

To avoid strndup, all you need is malloc to get the storage and memcpy
to copy the characters:

char *copy = malloc(end - basename + 1); // +1 for the null we add
if (copy != NULL) {
memcpy(copy, basename, end - basename);
copy[basename - end] = 0;
}
return copy;

The whole thing then needs to be wrapped up into a function for general
use. Something like:

char *get_basename(const char *filename)
{
....
}

Change the name if you choose the version that only removes a trailing
".bib"!
 
B

BartC

Rudra Banerjee said:
I am a C novice. I am trying to get the basename of a file(filename)
without extension. The problem is much discussed in net, but due to my
limited knowledge, I failed to make fruitful use of them, and thought I
may use the unix command, as given.
If anybody kindly show me where I am getting wrong, or better still, a
better(and small) method to get the same function without unix command, it
will be of huge help.

I've knocked together some code here. This does a few extra things, I
believe the functionality you want is in 'ExtractBaseFile()', but as
written, these are interdependent.

This code is little peculiar because it uses a 'string slice' type to return
a result; that is a pointer to a string plus a length, rather than just a
pointer to C-terminated string. This makes it possible to point into the
middle of an existing string (and to avoid messing with malloc()). To use
the result conventionally use the MakeCstring() conversion, or just copy the
bytes into a char array, and add a zero..

(I've assumed that filespecs on your machine are like Windows', except with
no "\" or ":" characters.)

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

typedef struct {char* str; int len;}strslice;

/* ------------------------------------ */
strslice MakeSlice(char* str,int len){
strslice s;
s.str=str;
s.len=len;
return s;
}

/* ------------------------------------ */
strslice ExtractPath(char* filespec){
int i,len;
char *p,*q;

len=strlen(filespec);
p=filespec+len-1; /* point to last char */
for (i=len; i>=1; --i)
if (*p--=='/') {
return MakeSlice(filespec,i);
}
return MakeSlice("",0);
}

/* ------------------------------------ */
strslice ExtractFile(char* filespec){
strslice s;
int len;

s=ExtractPath(filespec);
if (s.len==0) return MakeSlice(filespec,strlen(filespec)); /* No path
*/
return MakeSlice(s.str+s.len,strlen(filespec)-s.len);
}

/* ------------------------------------ */
strslice ExtractExt(char* filespec,int period){
/* period=1: return trailing "." (null ext) as "." rather than "" */
strslice s;
char *p;
int i;

s=ExtractFile(filespec);
if (s.len==0) return s;
p=s.str+s.len-1;
for (i=s.len; i>=1; --i)
if (*p--=='.')
if (i==s.len)
return period?MakeSlice(".",1):MakeSlice("",0);
else
return MakeSlice(p+2,s.len-i+1);
return MakeSlice("",0);
}

/* ------------------------------------ */
strslice ExtractBaseFile(char* filespec){
strslice f,e;

f=ExtractFile(filespec);
if (f.len==0) return f;
e=ExtractExt(filespec,0);
f.len-=e.len;
if (*(f.str+f.len-1)=='.') --f.len;
return f;
}

/* ------------------------------------ */
char* MakeCstring(strslice s){
char *p;
p=malloc(s.len+1);
if (p==NULL) return NULL;
if (s.len) memcpy(p,s.str,s.len);
*(p+s.len)=0;
return p;
}

/* ------------------------------------ */
int main (void){
char *teststr="/path1/path2/basefile.ext";
strslice slice;
char *newstr;

printf("Filespec = %s\n",teststr);

slice=ExtractPath(teststr);
printf("Path = \"%.*s\"\n",slice.len,slice.str);

slice=ExtractFile(teststr);
printf("File = \"%.*s\"\n",slice.len,slice.str);

slice=ExtractBaseFile(teststr);
printf("Base = \"%.*s\"\n",slice.len,slice.str);

slice=ExtractExt(teststr,1);
printf("Ext = \"%.*s\"\n",slice.len,slice.str);

newstr=MakeCstring(slice);
printf("Ext'= \"%s\"\n",newstr);
}
 
B

Ben Bacarisse

BartC said:
/* ------------------------------------ */
strslice ExtractPath(char* filespec){
int i,len;
char *p,*q;

len=strlen(filespec);
p=filespec+len-1; /* point to last char */

This is technically UB when filespec has zero length. You can, I think,
just drop the -1 and change *p-- to *--p below:
for (i=len; i>=1; --i)
if (*p--=='/') {
return MakeSlice(filespec,i);
}
return MakeSlice("",0);
}

BTW, is this layout and accident of posting or is it how you like to do
things?

<snip>
 
B

BartC

Ben Bacarisse said:
This is technically UB when filespec has zero length.

Just pointing to outside the string, without dereferencing (as that's
protected by the following loop)? If that's a serious consideration, then
there might be a couple of places where that could happen and needs a change
of logic. (The original code this was based on just used indexing.)
BTW, is this layout and accident of posting or is it how you like to do
things?

I'd like to say it's how I do it late at night just before I go to bed, but
no that's my normal layout, in my original post anyway.

But I see some leading spaces get left out when you try and quote the code,
which is supposed to look like this when you substitute leading "." with
spaces:

strslice ExtractPath(char* filespec){
int i;

for (i=strlen(filespec)-1; i>=0; --i)
..if (filespec=='/')
...return MakeSlice(filespec,i+1);
return MakeSlice("",0);
}

(I've also changed this function to avoid the UB ... and the extraneous
braces.)
 
J

James Kuyper

Just pointing to outside the string, without dereferencing (as that's
protected by the following loop)?

Yes. You can increment a pointer one past the end of an array with
defined behavior, but not two past the end, and you can't decrement it
to any position before the beginning of the array (6.5.6p8). The
asymmetry of these rules reflects the fact that some very common loop
idioms require incrementing past the end of an array, but no common
idiom requires going past the beginning.

These idioms became commonplace before the restrictions were imposed, so
this is not an example of circular logic (e.g.: it was disallowed
because no one was using it - no one was using it because it wasn't
allowed).

<pedantic>Technically, the standard only endorses end_of_array+1 as a
permitted pointer value. It doesn't cover (end_of_array-1)+2. Some
people have argued that (end_of_array-1)+2 is necessarily equal to
end_of_array+1, but to "prove" that identity they assume the associative
property (a+b)+c == a + (b+c). That property can be proven to be true
based upon 6.5.6p8 when 'a' is a pointer type, but only for the cases
where a, a+b, and (a+b)+c all point within the same array; the proof
cannot be validly extended to include pointers one past the end of the
array.</pedantic>
 
B

Ben Bacarisse

James Kuyper said:
<pedantic>Technically, the standard only endorses end_of_array+1 as a
permitted pointer value. It doesn't cover (end_of_array-1)+2. Some
people have argued that (end_of_array-1)+2 is necessarily equal to
end_of_array+1, but to "prove" that identity they assume the associative
property (a+b)+c == a + (b+c). That property can be proven to be true
based upon 6.5.6p8 when 'a' is a pointer type, but only for the cases
where a, a+b, and (a+b)+c all point within the same array; the proof
cannot be validly extended to include pointers one past the end of the
array.</pedantic>

I'm a bit baffled. I'd get it if you were talking about
(beginning_of_array-1)+1 but I can't see the problem with
(end_of_array-1)+2 in general. Are you talking about the specific case
when the array has one element (so beginning_of_array==end_of_array)?

Another possibility is that you meant end_of_array+2-1 but one way or
another I need more explanation to know what you are getting at.
 
B

Ben Bacarisse

BartC said:
But I see some leading spaces get left out when you try and quote the code,
which is supposed to look like this when you substitute leading "."
with spaces:

strslice ExtractPath(char* filespec){
int i;

for (i=strlen(filespec)-1; i>=0; --i)
.if (filespec=='/')
..return MakeSlice(filespec,i+1);
return MakeSlice("",0);
}


FYI It was missing in the message I received and was not a function of
my quoting the code, so some space presumably did get lost in posting.

<snip>
 
B

Ben Bacarisse

Kenneth Brody said:
Rudra Banerjee said:
Dear friends, I am a C novice. I am trying to get the basename of a
file(filename) without extension. The problem is much discussed in
net, but due to my limited knowledge, I failed to make fruitful use of
them, and thought I may use the unix command, as given.
char myCommand[512];
// char *basetmp=strrchr(filename,'/')+1;
sprintf(myCommand,"basename %s .bib",filename);
char *basefn=system((char *)myCommand);
printf (basefn);

NOTE: that's dangerous! All hell will break loose if the file name
includes %s for example.

Actually, given that:

1 - This appears to be for *nix.
2 - system() will most likely return zero because the command will
probably succeed.
3 - On most systems, the conversion of (int)0 to (char *) will
probably result in NULL.

The filename is pretty much irrelevant, since printf(NULL) will
probably always crash.

Also, what if the filename contains a space, or semicolon, or one of
numerous other characters, which will cause system(myCommand) to fail?

Yes, you are right, but by the time I posted the whole system() thing
had been declared a non-stater. I wanted to point out a new and as yet
unremarked on problem. But I should have snipped much more to make that
clear.

Any reply to a use of system based on a plain sprintf of a file name
should probably include this link:

http://xkcd.com/327/

or at least a remark that '/; rm -rf ~; echo' is my favourite test file.

<snip>
 
J

James Kuyper

I'm a bit baffled. I'd get it if you were talking about
(beginning_of_array-1)+1 but I can't see the problem with
(end_of_array-1)+2 in general. Are you talking about the specific case
when the array has one element (so beginning_of_array==end_of_array)?

Another possibility is that you meant end_of_array+2-1 but one way or
another I need more explanation to know what you are getting at.

To provide a context for the following paragraphs:

int array[10];
int *end_of_array = array+9;
int *P;
int a, b;

The C standard says "if the expression P points to the last
element of an array object, the expression (P)+1 points one past the
last element of the array object," (6.5.6p8). This statement only
applies to additions of 1, and only to pointers that point "to the last
element of an array object. It doesn't apply to adding any number other
than 1, nor does it apply to adding an integer to a pointer that points
at any location other than the last element of an array object.

6.5.6p8 also defines the result of adding an integer to a pointer in a
more general case:
If the pointer operand points to an element of
an array object, and the array is large enough, the result points to an element offset from
the original element such that the difference of the subscripts of the resulting and original
array elements equals the integer expression. In other words, if the expression P points to
the i-th element of an array object, the expressions (P)+N (equivalently, N+(P)) and
(P)-N (where N has the value n) point to, respectively, the i+n-th and i−n-th elements of
the array object, provided they exist.

Note, however, that this definition applies to P+a if and only if p and
p+a both point within the same array. Because of that limitation, while
it is possible to use that definition to prove that (P+a)+b == P +
(a+b), the validity of that identity is limited to situations where P,
P+a, and (P+a)+b all point within the array. In particular, when P ==
end_of_array, (P-1)+2 has no meaning defined by the standard. Therefore,
that identity can't be used to conclude that (end_of_array-1)+2 is
equivalent to end_of_array + 1.

It's also not possible to combine these two definitions to cover that
case. Try it and see. Keep in mind that can't simply assume that the
associative property applies - that's precisely the point I'm asking you
to prove. Associativity certainly applies to addition of ordinary
integers, and it applies (with exceptions for overflow) to C integers,
and it applies to the addition of C pointers and C integers as long as
the pointers stay within the corresponding array. I'm asking you to
formally prove that the associativity can be extended past the end of
the array.

I consider this a defect in the standard, not a statement of how C
actually works, nor of how it should work. I think the definition of the
way a pointer one past the end of an array can be created should be
expanded to cover (array+8)+2 and (array+7)+3, and not just (array+9)+1.
People who are in a position to get the wording corrected don't agree
with me.
 
B

BartC

FYI It was missing in the message I received and was not a function of
my quoting the code, so some space presumably did get lost in posting.

That's odd. The spaces are there when I look at the post using Windows Mail.
I also had a quick look via Googlegroups, and the spaces were there too (on
both new and old versions).
 
B

Ben Bacarisse

James Kuyper said:
I'm a bit baffled. I'd get it if you were talking about
(beginning_of_array-1)+1 but I can't see the problem with
(end_of_array-1)+2 in general. Are you talking about the specific case
when the array has one element (so beginning_of_array==end_of_array)?

Another possibility is that you meant end_of_array+2-1 but one way or
another I need more explanation to know what you are getting at.

To provide a context for the following paragraphs:

int array[10];
int *end_of_array = array+9;
int *P;
int a, b;

The C standard says "if the expression P points to the last
element of an array object, the expression (P)+1 points one past the
last element of the array object," (6.5.6p8). This statement only
applies to additions of 1, and only to pointers that point "to the last
element of an array object. It doesn't apply to adding any number other
than 1, nor does it apply to adding an integer to a pointer that points
at any location other than the last element of an array object.

I see. I did not think you were being that picky. Your example is, to
my mind, rather too specific to highlight your objection. Unless I am
still misunderstanding you, array+10 is not permitted either (by your
reading of the standard) because the "one past" permission applies only
to adding one to a pointer already at the end.

Similarly, if we already have a "one past" pointer, Q, evaluating Q-2 is
(again by your reading) undefined because the permission in 6.5.6 p8
relates only to (Q)-1, yes?

Actually, re-reading your remark, I think you obscured your point
slightly by saying "the standard only endorses end_of_array+1 as a
permitted pointer value". That's true by any reading. What you are
complaining about is that is endorses only (P)+1 as an expression that
can yield this special value.

I consider this a defect in the standard, not a statement of how C
actually works, nor of how it should work. I think the definition of the
way a pointer one past the end of an array can be created should be
expanded to cover (array+8)+2 and (array+7)+3, and not just (array+9)+1.
People who are in a position to get the wording corrected don't agree
with me.

I don't think there's much scope for confusion. I read the key phrase
of 6.5.6 p8 as illustrative of how to form this pointer, not as the only
way, but maybe people have been led astray by this wording.
 
K

Keith Thompson

James Kuyper said:
Yes. You can increment a pointer one past the end of an array with
defined behavior, but not two past the end, and you can't decrement it
to any position before the beginning of the array (6.5.6p8). The
asymmetry of these rules reflects the fact that some very common loop
idioms require incrementing past the end of an array, but no common
idiom requires going past the beginning.
[...]

It also reflects the fact that, if you have an array of large
elements, a pointer one past the end of the array only requires an
addressable location one byte past the end of the array, whereas
a pointer one element before the beginning of the array requires
an addressable location `sizeof (element_type)` bytes outside the
array object.

This isn't likely to matter much for systems with monolithic address
schemes (you probably won't have an array object allocated close to
the lower bound of the address space), but it can matter a great
deal for segmented memory systems, where an array might well be
allocated at the very beginning of a segment. The C standard is
carefully written to cater to such systems.
 
J

James Kuyper

On 08/29/2012 06:22 PM, Ben Bacarisse wrote:
....
Similarly, if we already have a "one past" pointer, Q, evaluating Q-2 is
(again by your reading) undefined because the permission in 6.5.6 p8
relates only to (Q)-1, yes?

Correct. The problem applies in both directions.
I don't think there's much scope for confusion. I read the key phrase
of 6.5.6 p8 as illustrative of how to form this pointer, not as the only
way, but maybe people have been led astray by this wording.

I don't think many people have been led astray by this wording; I think
most people have interpreted it as intended, not as written. As written,
it's not a special-case illustration of a general method for creating a
pointer one past the end of an array. Rather, it provides the only
method mentioned anywhere in the standard for creating such pointers,
and contains nothing to suggest that the method can be generalized
(other than the fact that we all "know" it can be generalized).
 
K

Keith Thompson

Kenneth Brody said:
Also, what if the filename contains a space, or semicolon, or one of
numerous other characters, which will cause system(myCommand) to fail?

Or what if the file name is something like "rm -rf $HOME", which could
cause system(myCommand) to succeed with disastrous results?
 
B

Ben Bacarisse

BartC said:
That's odd. The spaces are there when I look at the post using Windows
Mail. I also had a quick look via Googlegroups, and the spaces were
there too (on both new and old versions).

I checked. They are there and not there! In the literal text of the
message they are present, but you've posted with format=flowed and
a leading single space has a special meaning in f=f text. I suspect
your mailer should have added a space to all the lines carrying code so
they all get one removed.

For the moment I'll trust that my news reader is showing me the right
thing and the GG is not, though I am no expert on how f=f works.
 

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

Members online

No members online now.

Forum statistics

Threads
473,733
Messages
2,569,440
Members
44,832
Latest member
GlennSmall

Latest Threads

Top