[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.c

scandir file_select, text operations on const struct dirent *namelist

Sascha Wüstemann

8/2/2011 9:57:00 PM

56: int file_select(const struct dirent *namelist) {
57: if ( ( strcmp(namelist->d_name, "." ) == 0 ) || ( strcmp(
namelist->d_name, ".." ) == 0) )
58: return (0);
59: char *buff=namelist->d_name;
60: char *snippet;
61: char *treffer = strtok(buff, ".");
62: while ( treffer != 0 ) {
63: snippet = treffer;
64: treffer = strtok(NULL, ".");
65: }
66: if ( ( strcmp(snippet, "xdb") == 0 ) ) {
67: return (1);
68: }
69: else
70: return (0);
71:}

73:int main(
....
135: databases_count = scandir(dst, &namelist, file_select, alphasort);

gcc throws a warning:

ab.c: In function â??file_selectâ??:
ab.c:59:14: warning: initialization discards qualifiers from pointer
target type

I'd like to make shure scandir targets only '*.xdb' files. I first took

if ( strstr( namelist->d_name, ".xdb" ) != NULL )
return (1);
but that targets '*.xdb*' so I cannot use it.

My code above works without errors, but I don't know why
- strcmp(namelist->d_name, "." ) operates fine without warning and
char *buff=namelist->d_name; does not.

Would you please enlighten me and what if you can tell how to make it
without warnings, thanks.

Greetings from Braunschweig, Germany.
Sascha Wüstemann
4 Answers

pacman

8/2/2011 11:10:00 PM

0

In article <ge8mg8-1ur.ln1@killerghost.killerhippy.lan>,
Sascha Wüstemann <neues@killerhippy.de> wrote:
>56: int file_select(const struct dirent *namelist) {
>57: if ( ( strcmp(namelist->d_name, "." ) == 0 ) || ( strcmp(
>namelist->d_name, ".." ) == 0) )
>58: return (0);
>59: char *buff=namelist->d_name;

This d_name is part of a const struct, so it is not guaranteed to be
modifiable. By converting it to a pointer to non-const char you're declaring
your intent to modify it. That's the warning.

>60: char *snippet;
>61: char *treffer = strtok(buff, ".");

By passing the pointer to strtok you actually do modify the d_name contents.
That's bad. scandir is allowed to assume that you don't modify the dirent.
You cheated and modified it. Anything can happen now.

>62: while ( treffer != 0 ) {
>63: snippet = treffer;
>64: treffer = strtok(NULL, ".");
>65: }

Your whole strtok loop is unnecessary anyway, since you're only using it to
find the last '.' in the string. Use strrchr instead.

>66: if ( ( strcmp(snippet, "xdb") == 0 ) ) {
>67: return (1);
>68: }

char *lastdot = strrchr(namelist->d_name, '.');
if(lastdot && strcmp(lastdot+1, "xdb")) { ... }

--
Alan Curry

Ben Bacarisse

8/2/2011 11:15:00 PM

0

Sascha Wüstemann <neues@killerhippy.de> writes:

> 56: int file_select(const struct dirent *namelist) {
> 57: if ( ( strcmp(namelist->d_name, "." ) == 0 ) || ( strcmp(
> namelist->d_name, ".." ) == 0) )
> 58: return (0);
> 59: char *buff=namelist->d_name;
> 60: char *snippet;
> 61: char *treffer = strtok(buff, ".");
> 62: while ( treffer != 0 ) {
> 63: snippet = treffer;
> 64: treffer = strtok(NULL, ".");
> 65: }
> 66: if ( ( strcmp(snippet, "xdb") == 0 ) ) {
> 67: return (1);
> 68: }
> 69: else
> 70: return (0);
> 71:}
>
> 73:int main(
> ...
> 135: databases_count = scandir(dst, &namelist, file_select, alphasort);

Listing without line numbers are generally easier to read. I know you
wanted to highlight this:

> gcc throws a warning:
>
> ab.c: In function â??file_selectâ??:
> ab.c:59:14: warning: initialization discards qualifiers from pointer
> target type

but you can do that with, say, a comment:

char *buff=namelist->d_name; /* line 59 */

or just say it refers to the initialisation of 'buff'.

> I'd like to make shure scandir targets only '*.xdb' files. I first took
>
> if ( strstr( namelist->d_name, ".xdb" ) != NULL )
> return (1);
> but that targets '*.xdb*' so I cannot use it.

Yes, that's right. The looping you do will work but strtok has all
sorts of issues that make it a bad idea. For one thing, altering the
string makes the result very odd (try printing the name when you've
matched x.y.xdb). You don't need to alter the name at all. strrchr
will find the last occurrence of a character (or return NULL) so you can
test for

const char *dot = strrchr(namelist->d_name, '.');
return dot && strcmp(dot + 1, "xdb") == 0;

Note that I prefer to return the result of a test rather than test it in
an if statement and then return either 0 or 1. That's just too much
code.

> My code above works without errors, but I don't know why
> - strcmp(namelist->d_name, "." ) operates fine without warning and
> char *buff=namelist->d_name; does not.

You declared namelist to be a pointer to a const structure. That names
all it members const as well. Had you written

const char *buff = namelist->d_name;

you'd be OK, but then you'd get an error trying to pass buff to strtok.
One way or another, having declared namelist to point to a const object
you can get a non-const pointer to any part of it without a warning or
an error from the compiler.

strcmp's first argument is declared as a void pointer to const, so there
is no complaint about that version.

> Would you please enlighten me and what if you can tell how to make it
> without warnings, thanks.

A small point, do you need to test for "." and ".." first? I would have
though that's covered by you other test. Also, return is clearer
without the ()s. Have you been looking at very old C? The ()s in
return (1); used to be required a very, very long time ago!

--
Ben.

Sascha Wüstemann

8/3/2011 12:49:00 AM

0

Ben Bacarisse wrote:
> Sascha Wüstemann <neues@killerhippy.de> writes:
>
....
>> 135: databases_count = scandir(dst, &namelist, file_select, alphasort);
>
> Listing without line numbers are generally easier to read. I know you
> wanted to highlight this:
>
>> gcc throws a warning:
>>
>> ab.c: In function â??file_selectâ??:
>> ab.c:59:14: warning: initialization discards qualifiers from pointer
>> target type
>
> but you can do that with, say, a comment:
>
> char *buff=namelist->d_name; /* line 59 */

Thanks for guiding my next posting.

>
> or just say it refers to the initialisation of 'buff'.
>
>> I'd like to make shure scandir targets only '*.xdb' files. I first took
>>
>> if ( strstr( namelist->d_name, ".xdb" ) != NULL )
>> return (1);
>> but that targets '*.xdb*' so I cannot use it.
>
> Yes, that's right. The looping you do will work but strtok has all
> sorts of issues that make it a bad idea. For one thing, altering the
> string makes the result very odd (try printing the name when you've
> matched x.y.xdb). You don't need to alter the name at all. strrchr
> will find the last occurrence of a character (or return NULL) so you can
> test for

I haven't noticed I am doing bad to namelist by using my construct, but
Alan and you made this perfectly clear.

>
> const char *dot = strrchr(namelist->d_name, '.');
> return dot && strcmp(dot + 1, "xdb") == 0;
>

And thank you both for pointing me to strrchr which turns using strcmp
into success at this point. Great.

> Note that I prefer to return the result of a test rather than test it in
> an if statement and then return either 0 or 1. That's just too much
> code.
>
>> My code above works without errors, but I don't know why
>> - strcmp(namelist->d_name, "." ) operates fine without warning and
>> char *buff=namelist->d_name; does not.
>
> You declared namelist to be a pointer to a const structure. That names
> all it members const as well. Had you written
>
> const char *buff = namelist->d_name;
>
> you'd be OK, but then you'd get an error trying to pass buff to strtok.

Well, yes. You hit me.

> One way or another, having declared namelist to point to a const object
> you can get a non-const pointer to any part of it without a warning or
> an error from the compiler.
>
> strcmp's first argument is declared as a void pointer to const, so there
> is no complaint about that version.

I learned that now, thanks to you and Alan.

>
>> Would you please enlighten me and what if you can tell how to make it
>> without warnings, thanks.
>
> A small point, do you need to test for "." and ".." first? I would have
> though that's covered by you other test. Also, return is clearer
> without the ()s. Have you been looking at very old C? The ()s in
> return (1); used to be required a very, very long time ago!
>

Right.

I am learning by doing and where I am not smart enough I google for code
which works well so far.

I have one C Programming book from 2004, that is from Suse Press "C
Programmierung unter Linux, Window, Unix" I assume it is a good starting
point but lacks professional coding style.

At least I have understood dynamic array management I needed to know for
my little project which makes it a good book :-)

I am working at a program which reads mounted devices at /media and
interactively reads and writes to sqlite3 databases about the media
contents. See, I spend my spare time in writing C, lately, I haven't
studied programming. It's fun to make your tools yourself.

As I haven't found a lot or a lot different examples for filtering
scandir, I was lost, so I had to ask here. I was sure you guys would
read and I am happy about your competent answers. Finding the database
files works, now.

Greetings from Braunschweik, Germany.
Sascha Wüstemann

Ike Naar

8/3/2011 7:27:00 AM

0

On 2011-08-02, Sascha W?stemann <neues@killerhippy.de> wrote:
> 56: int file_select(const struct dirent *namelist) {
> 57: if ( ( strcmp(namelist->d_name, "." ) == 0 ) || ( strcmp(
> namelist->d_name, ".." ) == 0) )
> 58: return (0);
> 59: char *buff=namelist->d_name;
> 60: char *snippet;
> 61: char *treffer = strtok(buff, ".");
> 62: while ( treffer != 0 ) {
> 63: snippet = treffer;
> 64: treffer = strtok(NULL, ".");
> 65: }

Others have already pointed out why strtok is best avoided here.

Here's an additional problem that hasn't been mentioned yet:
if the name consists of only dots (e.g. "...") the first call to
strtok() returns NULL, the loop body is never executed and snippet
remains uninitialized, so the strcmp() call below has undefined
behaviour.

> 66: if ( ( strcmp(snippet, "xdb") == 0 ) ) {
> 67: return (1);
> 68: }
> 69: else
> 70: return (0);
> 71:}