Thread: How to use strverscmp()?

  1. #1
    Registered User
    Join Date
    Feb 2013
    Location
    Sweden
    Posts
    89

    How to use strverscmp()?

    I've read a little about a function to compare version numbers, strverscmp(), and I find a lot of information about it, but not how to use it.

    Seems to be a ”GNU extension”, what does that mean?
    What do I need to #include?
    Is there anything I need to know when compiling (using gcc), such as -lSomething?

    It seems to be installed on my system already; at least the man page is there.

  2. #2
    - - - - - - - - oogabooga's Avatar
    Join Date
    Jan 2008
    Posts
    2,808
    If you have the man page then you have all the information you need to use it. That is, after all, the purpose of man pages.

    Under SYNOPSIS it says to include <string.h> after defining _GNU_SOURCE. The reason for the define is that it is, as you say, a GNU extension.

    The DESCRIPTION tells you what it does. Basically, instead of a braindead lexicographical order, it compares as one would expect. If you called qsort with strcmp on a certain array of strings, you might get:

    abc1
    abc11
    abc2
    abc22
    abc3
    abc33

    whereas strverscmp would give you

    abc1
    abc2
    abc3
    abc11
    abc22
    abc33
    The cost of software maintenance increases with the square of the programmer's creativity. - Robert D. Bliss

  3. #3
    Registered User
    Join Date
    Feb 2013
    Location
    Sweden
    Posts
    89
    Quote Originally Posted by oogabooga View Post
    If you have the man page then you have all the information you need to use it. That is, after all, the purpose of man pages.
    I know. Some man pages fail with that mission, though. They are not written for stupid people like me, but rather for programmers…
    Quote Originally Posted by oogabooga View Post
    Under SYNOPSIS it says to include <string.h> after defining _GNU_SOURCE. The reason for the define is that it is, as you say, a GNU extension.
    Aah, thanks. I just misunderstood that part of the man page. I thought they meant that <string.h> is included in the strverscmp source code file, not that I should include it.

    And still, the man page didn't mention if something special is required in order to compile something that use strverscmp. Like, if you compile something using math.h, you need the -lm flag with gcc. Maybe that's not the right place to mention things like that, but I didn't find any information about it any place else either.

    Of course I can just write something simple and see what happens, and I guess that's what I'm going to do, next time when I have some time left…
    Quote Originally Posted by oogabooga View Post
    The DESCRIPTION tells you what it does. Basically, instead of a braindead lexicographical order, it compares as one would expect. If you called qsort with strcmp on a certain array of strings, you might get:

    abc1
    abc11
    abc2
    abc22
    abc3
    abc33

    whereas strverscmp would give you

    abc1
    abc2
    abc3
    abc11
    abc22
    abc33
    Yes, I knew that. That's why I want to use strverscmp in the first place.

  4. #4
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,909
    Quote Originally Posted by guraknugen View Post
    And still, the man page didn't mention if something special is required in order to compile something that use strverscmp. Like, if you compile something using math.h, you need the -lm flag with gcc. Maybe that's not the right place to mention things like that, but I didn't find any information about it any place else either.
    Perhaps you're misunderstanding the man page. The synopsis section typically provides an overview of what is needed to use that function. It gives you anything you need to #define (_GNU_SOURCE in this case) and any files you need to #include (string.h in this case). It also gives you the prototype, so you know how to call it, and know what parameters are being referred to in the "Description" section. Defining the _GNU_SOURCE macro enables the GNU extensions, like strverscmp. Also, notice that the man page says "See feature_test_macros(7)". Whenever you see some_topic(N), that is telling you to look at the man page for some_topic, in section N. You do that by typing "man N some_topic". In this case "man 7 feature_test_macros" would provide you with this info, further detailing what _GNU_SOURCE does.

  5. #5
    - - - - - - - - oogabooga's Avatar
    Join Date
    Jan 2008
    Posts
    2,808
    And you shouldn't have to use -lm to use <math.h>.
    If a library is needed for a function the man page will tell you.
    The cost of software maintenance increases with the square of the programmer's creativity. - Robert D. Bliss

  6. #6
    Registered User
    Join Date
    Feb 2013
    Location
    Sweden
    Posts
    89
    Quote Originally Posted by anduril462 View Post
    Perhaps you're misunderstanding the man page. The synopsis section typically provides an overview of what is needed to use that function. It gives you anything you need to #define (_GNU_SOURCE in this case) and any files you need to #include (string.h in this case). It also gives you the prototype, so you know how to call it, and know what parameters are being referred to in the "Description" section. Defining the _GNU_SOURCE macro enables the GNU extensions, like strverscmp. Also, notice that the man page says "See feature_test_macros(7)". Whenever you see some_topic(N), that is telling you to look at the man page for some_topic, in section N. You do that by typing "man N some_topic". In this case "man 7 feature_test_macros" would provide you with this info, further detailing what _GNU_SOURCE does.
    Yes, I misunderstood it completely. It would have been much easier if the man pages were translated to my language… (some of them actually are, but not this one).
    However, I think I understand it better now, thanks to you guys.

  7. #7
    Registered User
    Join Date
    Feb 2013
    Location
    Sweden
    Posts
    89
    Okay, so I tried to come up with something, but I didn't get it to work properly, I think. Sometimes the output was unexpected, or maybe I just expected the wrong things to happen.
    My little test program is supposed to print the latest version of those entered by the user as parameters to the program. Currently I let it print the result for every value of i in the for loop.
    Code:
    #define _GNU_SOURCE
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int main(int argc, const char *argv[])
    {
        if (argc<2) {
            fprintf(stderr, "Arguments missing.\n");
            exit (1);
        }
        const char *max;
        max=argv[1];
        printf("1 – %6s (Max: %s)\n", argv[1], max);            // Remove this line
        for (int i=2; i<argc; i++) {
            if (strverscmp(argv[i], max)==1)
                max=argv[i];
            printf("%d – %6s (Max: %s)\n", i, argv[i], max);    // Remove this line
        }
    //    printf("%s\n", max);
        return 0;
    }
    Now, compiling and a few test runs:
    Code:
    $ gcc -std=gnu99 -o LatestVersion LatestVersion.c
    $ ./LatestVersion 2.10.0 2.30.0
    1 – 2.10.0 (Max: 2.10.0)
    2 – 2.30.0 (Max: 2.10.0)
    $ ./LatestVersion 2.19.0 2.30.0
    1 – 2.19.0 (Max: 2.19.0)
    2 – 2.30.0 (Max: 2.19.0)
    $ ./LatestVersion 2.20.0 2.30.0
    1 – 2.20.0 (Max: 2.20.0)
    2 – 2.30.0 (Max: 2.30.0)
    $ ./LatestVersion 1.20.0 1.30.0
    1 – 1.20.0 (Max: 1.20.0)
    2 – 1.30.0 (Max: 1.30.0)
    $ ./LatestVersion 1.10.0 1.30.0
    1 – 1.10.0 (Max: 1.10.0)
    2 – 1.30.0 (Max: 1.10.0)
    $ ./LatestVersion 1.10.0 1.90.0
    1 – 1.10.0 (Max: 1.10.0)
    2 – 1.90.0 (Max: 1.10.0)
    $ ./LatestVersion 1.10.0 1.99.0
    1 – 1.10.0 (Max: 1.10.0)
    2 – 1.99.0 (Max: 1.10.0)
    $ ./LatestVersion 1.20.0 1.99.0
    1 – 1.20.0 (Max: 1.20.0)
    2 – 1.99.0 (Max: 1.20.0)
    $ ./LatestVersion 1.30.0 1.99.0
    1 – 1.30.0 (Max: 1.30.0)
    2 – 1.99.0 (Max: 1.30.0)
    $ ./LatestVersion 1.80.0 1.99.0
    1 – 1.80.0 (Max: 1.80.0)
    2 – 1.99.0 (Max: 1.99.0)
    $ ./LatestVersion 1.70.0 1.99.0
    1 – 1.70.0 (Max: 1.70.0)
    2 – 1.99.0 (Max: 1.70.0)
    $ ./LatestVersion 1.79.0 1.99.0
    1 – 1.79.0 (Max: 1.79.0)
    2 – 1.99.0 (Max: 1.79.0)
    $
    Some of the results are unexpected and some are expected.
    Since I'm not very good at this, I suspect that I didn't do this quite right… But what? Or maybe I just misunderstood how strverscmp works.

    I expected this output:
    Code:
    $ ./LatestVersion 2.10.0 2.30.0
    1 – 2.10.0 (Max: 2.10.0)
    2 – 2.30.0 (Max: 2.30.0)
    $ ./LatestVersion 2.19.0 2.30.0
    1 – 2.19.0 (Max: 2.19.0)
    2 – 2.30.0 (Max: 2.30.0)
    $ ./LatestVersion 2.20.0 2.30.0
    1 – 2.20.0 (Max: 2.20.0)
    2 – 2.30.0 (Max: 2.30.0)
    $ ./LatestVersion 1.20.0 1.30.0
    1 – 1.20.0 (Max: 1.20.0)
    2 – 1.30.0 (Max: 1.30.0)
    $ ./LatestVersion 1.10.0 1.30.0
    1 – 1.10.0 (Max: 1.10.0)
    2 – 1.30.0 (Max: 1.30.0)
    $ ./LatestVersion 1.10.0 1.90.0
    1 – 1.10.0 (Max: 1.10.0)
    2 – 1.90.0 (Max: 1.90.0)
    $ ./LatestVersion 1.10.0 1.99.0
    1 – 1.10.0 (Max: 1.10.0)
    2 – 1.99.0 (Max: 1.99.0)
    $ ./LatestVersion 1.20.0 1.99.0
    1 – 1.20.0 (Max: 1.20.0)
    2 – 1.99.0 (Max: 1.99.0)
    $ ./LatestVersion 1.30.0 1.99.0
    1 – 1.30.0 (Max: 1.30.0)
    2 – 1.99.0 (Max: 1.99.0)
    $ ./LatestVersion 1.80.0 1.99.0
    1 – 1.80.0 (Max: 1.80.0)
    2 – 1.99.0 (Max: 1.99.0)
    $ ./LatestVersion 1.70.0 1.99.0
    1 – 1.70.0 (Max: 1.70.0)
    2 – 1.99.0 (Max: 1.99.0)
    $ ./LatestVersion 1.79.0 1.99.0
    1 – 1.79.0 (Max: 1.79.0)
    2 – 1.99.0 (Max: 1.99.0)
    $
    Last edited by guraknugen; 10-01-2013 at 03:01 PM.

  8. #8
    - - - - - - - - oogabooga's Avatar
    Join Date
    Jan 2008
    Posts
    2,808
    strverscmp does not necessarily return 1 to mean greater-than. It's only defined to return a value greater than 0. So instead of == 1, try > 0. This is how strcmp is also defined.

    And your tests should include the values in the opposite order too:
    $ ./LatestVersion 2.30.0 2.10.0

    And, of course, more than 2 values, but I'm sure you know that.

    EDIT: Notice that in the cases that worked, the deciding character is exactly 1 above the previous character. E.g., 2.20.0, 2.30.0 worked because 3 is exactly 1 above 2. So strverscmp is presumably returning the difference between the deciding characters. (But that cannot be portably counted on.)
    Here's the cases in your tests that worked:
    2.20.0, 2.30.0
    1.20.0, 1.30.0
    1.80.0, 1.99.0
    Last edited by oogabooga; 10-01-2013 at 04:16 PM.
    The cost of software maintenance increases with the square of the programmer's creativity. - Robert D. Bliss

  9. #9
    Registered User
    Join Date
    Feb 2013
    Location
    Sweden
    Posts
    89
    Yes, that has to be it. I actually thought about that before I went to sleep, but I had already shut down my computer. Now that you mention it I think I remember reading that it returns a positive value, a negative value our zero, depending on the result of the comparison. I am not sure why I thought that positive value means 1 and negative value means -1…
    The few tests I did before writing this file didn't return anything else, but I don't remember what values I used in those tests.
    There's still one odd thing though, that I noticed in my earliest tests. I wrote a simple programme that just returned the return value of strverscmp. I ran it in a gnome-terminal and checked the return value with echo $?. Whenever -1 was expected, 255 was returned. That's odd I think, because strverscmp returns an integer, so the return value should be at least 65535 in that case, shouldn't it? But maybe it's a Bash thing to interpret -1 to 255, I don't know. Maybe I should rather ask that question in a Bash or Linux forum.

    Thanks for replying!

  10. #10
    - - - - - - - - oogabooga's Avatar
    Join Date
    Jan 2008
    Posts
    2,808
    In bash on cygwin using strcmp instead of strverscmp I get the same result.
    Code:
    Me@compy ~
    $ cat > strcmp.c
    #include <stdio.h>
    #include <string.h>
    int main(int argc, char **argv) {
        int ret = strcmp(argv[1], argv[2]);
        printf("%d\n", ret);
        return ret;
    }
    
    Me@compy ~
    $ gcc -Wall strcmp.c
    
    Me@compy ~
    $ ./a A B; echo $?
    -1
    255
    
    Me@compy ~
    $ ./a B A; echo $?
    1
    1
    
    Me@compy ~
    $ ./a C A; echo $?
    2
    2
    
    Me@compy ~
    $ ./a A C; echo $?
    -2
    254
    
    Me@compy ~
    $ ./a A A; echo $?
    0
    0
    Here's the result of a program that simply returns 1000.

    Code:
    Me@compy ~
    $ cat > ret1000.c
    int main(void) { return 1000; }
    
    Me@compy ~
    $ gcc ret1000.c
    
    Me@compy ~
    $ ./a; echo $?
    232
    1000 in hex is 0x3E8. 0xE8 is 232.

    This indicates that only the low-order byte of the int return value is used, and is interpreted as an unsigned char. This turns -1 into 255 and -2 into 254 due to two's complement representation. I don't know if this is peculiar to bash or representative of *nix in general.

    Running the same program in the Windows command interpreter and echoing %errorlevel% yields -1 where bash gave 255 and 1000 where bash gave 232.
    Last edited by oogabooga; 10-02-2013 at 09:25 AM.
    The cost of software maintenance increases with the square of the programmer's creativity. - Robert D. Bliss

Popular pages Recent additions subscribe to a feed