scandir file_select, text operations on const struct dirent *namelist)

  • Thread starter Sascha Wüstemann
  • Start date
S

Sascha Wüstemann

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
 
A

Alan Curry

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")) { ... }
 
B

Ben Bacarisse

Sascha Wüstemann said:
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!
 
S

Sascha Wüstemann

Ben said:
....

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


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'.


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.


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.
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
 
I

Ike Naar

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.
 

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


Members online

Forum statistics

Threads
473,766
Messages
2,569,569
Members
45,042
Latest member
icassiem

Latest Threads

Top