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

Discussion in 'C Programming' started by Sascha Wüstemann, Aug 2, 2011.

  1. 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
    Sascha Wüstemann, Aug 2, 2011
    #1
    1. Advertising

  2. Sascha Wüstemann

    Alan Curry Guest

    In article <>,
    Sascha Wüstemann <> 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
    Alan Curry, Aug 3, 2011
    #2
    1. Advertising

  3. Sascha Wüstemann <> 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.
    Ben Bacarisse, Aug 3, 2011
    #3
  4. Re: scandir file_select, text operations on const struct dirent*namelist)

    Ben Bacarisse wrote:
    > Sascha Wüstemann <> 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
    Sascha Wüstemann, Aug 3, 2011
    #4
  5. Sascha Wüstemann

    Ike Naar Guest

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

    On 2011-08-02, Sascha W?stemann <> 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:}
    Ike Naar, Aug 3, 2011
    #5
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Martin Pohlack

    sizeof(struct dirent.d_ino)

    Martin Pohlack, Sep 8, 2003, in forum: C Programming
    Replies:
    19
    Views:
    1,086
    Keith Thompson
    Sep 10, 2003
  2. Replies:
    9
    Views:
    562
    J. J. Farrell
    Dec 8, 2003
  3. Max

    NameList implementation

    Max, Sep 6, 2006, in forum: XML
    Replies:
    1
    Views:
    318
    Joseph Kesselman
    Sep 6, 2006
  4. Javier
    Replies:
    2
    Views:
    559
    James Kanze
    Sep 4, 2007
  5. eddie

    Need help with struct *** dirent ???

    eddie, Jan 10, 2009, in forum: C Programming
    Replies:
    7
    Views:
    502
    Richard
    Jan 12, 2009
Loading...

Share This Page