• FreeTDS port to VMS V9.x on x86?

    From Richard Jordan@21:1/5 to All on Tue Jun 3 11:22:20 2025
    Is there a downloadable build of the FreeTDS package for VSI VMS on x86 available? I don't have access to a C compiler to try building it.

    Thanks!

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to All on Tue Jun 3 13:45:03 2025
    On 6/3/2025 1:27 PM, Arne Vajhøj wrote:
    On 6/3/2025 12:22 PM, Richard Jordan wrote:
    Is there a downloadable build of the FreeTDS package for VSI VMS on
    x86 available?  I don't have access to a C compiler to try building it.

    1.5.2 descrip_mms.template has:

    ... Craig A. Berry ... 23-JAN-2003

    But I could take a look at building on x86-64.

    Did.

    * download 1.5.2
    * unpack on VMS
    * @[.vms]configure
    * mmk
    * note only I message
    * zip tree with -V
    * upload

    https://www.vajhoej.dk/arne/temp/freetds-1_5_2.zip

    Nothing is tested. No guarantees.

    (I actually do have some TDS code somewhere that I could test with,
    but I assume that you have your test setup ready, so faster that
    you get it)

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Richard Jordan on Tue Jun 3 13:27:35 2025
    On 6/3/2025 12:22 PM, Richard Jordan wrote:
    Is there a downloadable build of the FreeTDS package for VSI VMS on x86 available?  I don't have access to a C compiler to try building it.

    1.5.2 descrip_mms.template has:

    ... Craig A. Berry ... 23-JAN-2003

    But I could take a look at building on x86-64.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Richard Jordan@21:1/5 to All on Tue Jun 3 17:03:11 2025
    On 6/3/25 12:45 PM, Arne Vajhøj wrote:
    On 6/3/2025 1:27 PM, Arne Vajhøj wrote:
    On 6/3/2025 12:22 PM, Richard Jordan wrote:
    Is there a downloadable build of the FreeTDS package for VSI VMS on
    x86 available?  I don't have access to a C compiler to try building it.

    1.5.2 descrip_mms.template has:

    ... Craig A. Berry ... 23-JAN-2003

    But I could take a look at building on x86-64.

    Did.

    * download 1.5.2
    * unpack on VMS
    * @[.vms]configure
    * mmk
    * note only I message
    * zip tree with -V
    * upload

    https://www.vajhoej.dk/arne/temp/freetds-1_5_2.zip

    Nothing is tested. No guarantees.

    (I actually do have some TDS code somewhere that I could test with,
    but I assume that you have your test setup ready, so faster that
    you get it)

    Arne



    Thanks Arne! I don't actually have access to an x86 VMS server yet,
    we're lining up all the ducks to see what is needed to move from the
    current RX server to a cloud hosted x86 server. Found Hunter's new
    netlib build, still trying to track down if the GSoap kits have been updated/rebuilt.

    Thank you also Craig, good to know its functional.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Craig A. Berry@21:1/5 to All on Tue Jun 3 16:48:03 2025
    On 6/3/25 12:45 PM, Arne Vajhøj wrote:
    On 6/3/2025 1:27 PM, Arne Vajhøj wrote:
    On 6/3/2025 12:22 PM, Richard Jordan wrote:
    Is there a downloadable build of the FreeTDS package for VSI VMS on
    x86 available?  I don't have access to a C compiler to try building it.

    1.5.2 descrip_mms.template has:

    ... Craig A. Berry ... 23-JAN-2003

    But I could take a look at building on x86-64.

    Did.

    * download 1.5.2
    * unpack on VMS
    * @[.vms]configure
    * mmk
    * note only I message
    * zip tree with -V
    * upload

    https://www.vajhoej.dk/arne/temp/freetds-1_5_2.zip

    Nothing is tested. No guarantees.

    (I actually do have some TDS code somewhere that I could test with,
    but I assume that you have your test setup ready, so faster that
    you get it)

    It comes with an extensive test suite:

    1.) Populate PWD.in with server and credentials
    2.) $ define DECC$EFS_CHARSET ENABLE ! for some of the test infrastructure
    3.) mmk test

    I had it working pretty well on x86 against SQL 2022 a couple of months
    ago. I think there was one test failure I never finished tracking down.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Craig A. Berry on Thu Jun 5 10:55:06 2025
    On 6/3/2025 5:48 PM, Craig A. Berry wrote:

    On 6/3/25 12:45 PM, Arne Vajhøj wrote:
    On 6/3/2025 1:27 PM, Arne Vajhøj wrote:
    On 6/3/2025 12:22 PM, Richard Jordan wrote:
    Is there a downloadable build of the FreeTDS package for VSI VMS on
    x86 available?  I don't have access to a C compiler to try building it. >>>
    1.5.2 descrip_mms.template has:

    ... Craig A. Berry ... 23-JAN-2003

    But I could take a look at building on x86-64.

    Did.

    * download 1.5.2
    * unpack on VMS
    * @[.vms]configure
    * mmk
    * note only I message
    * zip tree with -V
    * upload

    https://www.vajhoej.dk/arne/temp/freetds-1_5_2.zip

    Nothing is tested. No guarantees.

    (I actually do have some TDS code somewhere that I could test with,
    but I assume that you have your test setup ready, so faster that
    you get it)

    It comes with an extensive test suite:

    1.) Populate PWD.in with server and credentials
    2.) $ define DECC$EFS_CHARSET ENABLE ! for some of the test infrastructure 3.) mmk test

    I had it working pretty well on x86 against SQL 2022 a couple of months ago.  I think there was one test failure I never finished tracking down.

    I want to test with my own code.

    Old DBLIB code. :-)

    First I had some problems with connection. Apparently the client lib
    and my SQLServer had a chit chat and decided to use SSL which failed.

    freetds.conf with:

    [arnepc5]
    host = arnepc5
    port = 1433
    tds version = 7.0
    encryption = off

    solved that.

    Now I have queries working.

    Updates fail with:

    TDS DBLIB error: Unable to allocate sufficient memory
    TDS OS error: error 0
    TDS error in dbcmd

    But I suspect that is a bug in my code.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to All on Thu Jun 5 12:47:05 2025
    On 6/5/2025 10:55 AM, Arne Vajhøj wrote:
    Updates fail with:

    TDS DBLIB error: Unable to allocate sufficient memory
    TDS OS error: error 0
    TDS error in dbcmd

    But I suspect that is a bug in my code.

    Mysterious.

    stat = dbfcmd(con, "INSERT INTO t1 VALUES(%d, '%s')", f1, f2esc);

    fails.

    char buf[1000];
    sprintf(buf, "INSERT INTO t1 VALUES(%d, '%s')", f1, f2esc);
    stat = dbcmd(con, buf);

    works.

    And DBLIB.C contains:

    RETCODE
    dbfcmd(DBPROCESS * dbproc, const char *fmt, ...)
    {
    va_list ap;
    char *s;
    int len;
    RETCODE ret;

    tdsdump_log(TDS_DBG_FUNC, "dbfcmd(%p, %s, ...)\n", dbproc, fmt);
    CHECK_CONN(FAIL);
    CHECK_NULP(fmt, "dbfcmd", 2, FAIL);

    va_start(ap, fmt);
    len = vasprintf(&s, fmt, ap);
    va_end(ap);

    if (len < 0) {
    dbperror(dbproc, SYBEMEM, errno);
    return FAIL;
    }

    ret = dbcmd(dbproc, s);
    free(s);

    return ret;
    }

    ????

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Simon Clubley@21:1/5 to arne@vajhoej.dk on Thu Jun 5 17:33:47 2025
    On 2025-06-05, Arne Vajhj <arne@vajhoej.dk> wrote:
    On 6/5/2025 10:55 AM, Arne Vajhj wrote:
    Updates fail with:

    TDS DBLIB error: Unable to allocate sufficient memory
    TDS OS error: error 0
    TDS error in dbcmd

    But I suspect that is a bug in my code.

    Mysterious.

    stat = dbfcmd(con, "INSERT INTO t1 VALUES(%d, '%s')", f1, f2esc);

    fails.

    char buf[1000];
    sprintf(buf, "INSERT INTO t1 VALUES(%d, '%s')", f1, f2esc);
    stat = dbcmd(con, buf);

    works.

    And DBLIB.C contains:

    RETCODE
    dbfcmd(DBPROCESS * dbproc, const char *fmt, ...)
    {
    va_list ap;
    char *s;
    int len;
    RETCODE ret;

    tdsdump_log(TDS_DBG_FUNC, "dbfcmd(%p, %s, ...)\n", dbproc, fmt);
    CHECK_CONN(FAIL);
    CHECK_NULP(fmt, "dbfcmd", 2, FAIL);

    va_start(ap, fmt);
    len = vasprintf(&s, fmt, ap);
    va_end(ap);

    if (len < 0) {
    dbperror(dbproc, SYBEMEM, errno);
    return FAIL;
    }

    ret = dbcmd(dbproc, s);
    free(s);

    return ret;
    }

    ????


    Does this _exact_ code work on Alpha ?

    If yes, try building your test code with compiler optimisation disabled.

    If that doesn't work, try building the library itself with compiler optimisation disabled.

    32-bit or 64-bit build ?

    Any compiler build warnings either in the library or your test code ?
    (Try setting warnings to fatal to abort the build if you get a lot of
    output during building).

    I am trying to eliminate the possibility you may have stumbled across
    a compiler bug.

    Simon.

    --
    Simon Clubley, clubley@remove_me.eisner.decus.org-Earth.UFP
    Walking destinations on a map are further away than they appear.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Craig A. Berry@21:1/5 to Simon Clubley on Thu Jun 5 16:16:33 2025
    On 6/5/25 12:33 PM, Simon Clubley wrote:
    On 2025-06-05, Arne Vajhøj <arne@vajhoej.dk> wrote:
    On 6/5/2025 10:55 AM, Arne Vajhøj wrote:
    Updates fail with:

    TDS DBLIB error: Unable to allocate sufficient memory
    TDS OS error: error 0
    TDS error in dbcmd

    But I suspect that is a bug in my code.

    Mysterious.

    stat = dbfcmd(con, "INSERT INTO t1 VALUES(%d, '%s')", f1, f2esc);

    fails.

    char buf[1000];
    sprintf(buf, "INSERT INTO t1 VALUES(%d, '%s')", f1, f2esc);
    stat = dbcmd(con, buf);

    works.

    And DBLIB.C contains:

    RETCODE
    dbfcmd(DBPROCESS * dbproc, const char *fmt, ...)
    {
    va_list ap;
    char *s;
    int len;
    RETCODE ret;

    tdsdump_log(TDS_DBG_FUNC, "dbfcmd(%p, %s, ...)\n", dbproc, fmt);
    CHECK_CONN(FAIL);
    CHECK_NULP(fmt, "dbfcmd", 2, FAIL);

    va_start(ap, fmt);
    len = vasprintf(&s, fmt, ap);
    va_end(ap);

    if (len < 0) {
    dbperror(dbproc, SYBEMEM, errno);
    return FAIL;
    }

    ret = dbcmd(dbproc, s);
    free(s);

    return ret;
    }

    ????


    Does this _exact_ code work on Alpha ?

    If yes, try building your test code with compiler optimisation disabled.

    If that doesn't work, try building the library itself with compiler optimisation disabled.


    $ mmk clean
    $ @[.vms]configure
    $ mmk/MACRO=__DEBUG__=1

    should do that plus provide debug versions of all the library code.

    32-bit or 64-bit build ?

    It's the traditional DEC C compiler interface, so all the pointers would
    be 32-bit, if that's what you're asking.

    Any compiler build warnings either in the library or your test code ?
    (Try setting warnings to fatal to abort the build if you get a lot of
    output during building).

    I am trying to eliminate the possibility you may have stumbled across
    a compiler bug.

    Simon.


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Craig A. Berry on Thu Jun 5 21:35:50 2025
    On 6/5/2025 5:16 PM, Craig A. Berry wrote:
    On 6/5/25 12:33 PM, Simon Clubley wrote:
    On 2025-06-05, Arne Vajhøj <arne@vajhoej.dk> wrote:
    Mysterious.

    stat = dbfcmd(con, "INSERT INTO t1 VALUES(%d, '%s')", f1, f2esc);

    fails.

    char buf[1000];
    sprintf(buf, "INSERT INTO t1 VALUES(%d, '%s')", f1, f2esc);
    stat = dbcmd(con, buf);

    works.

    And DBLIB.C contains:

    RETCODE
    dbfcmd(DBPROCESS * dbproc, const char *fmt, ...)
    {

          va_start(ap, fmt);
          len = vasprintf(&s, fmt, ap);
          va_end(ap);

    Does this _exact_ code work on Alpha ?

    If yes, try building your test code with compiler optimisation disabled.

    If that doesn't work, try building the library itself with compiler
    optimisation disabled.


    $ mmk clean
    $ @[.vms]configure
    $ mmk/MACRO=__DEBUG__=1


    It is vasprintf that return -1.

    It happens with both normal build and /DEB/NOOPT.

    I have not tried on Alpha yet.

    Any compiler build warnings either in the library or your test code ?
    (Try setting warnings to fatal to abort the build if you get a lot of
    output during building).

    There are just a few %CC-I-QUESTCOMPARE.

    I am trying to eliminate the possibility you may have stumbled across
    a compiler bug.

    It is a mystery.

    I have a query that use dbfcmd and it works. But it fails for
    my updates.

    I created a standalone example doing the same just without any FreeTDS.

    It works fine.

    $ typ z.c
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <stdarg.h>

    void dbfcmd(void *dbproc, const char *fmt, ...)
    {
    va_list ap;
    char *s;
    int len;
    va_start(ap, fmt);
    len = vasprintf(&s, fmt, ap);
    va_end(ap);
    printf("len=%d s=|%s|\n", len, s);
    free(s);
    }

    int main()
    {
    dbfcmd(NULL, "INSERT INTO t1 VALUES(%d, '%s')", 999, "XXX");
    dbfcmd(NULL, "INSERT INTO t1 VALUES(%d, '%s')", 999, "XXX");
    dbfcmd(NULL, "INSERT INTO t1 VALUES(%d, '%s')", 999, "XXXXXX");
    return 0;
    }

    $ r z
    len=33 s=|INSERT INTO t1 VALUES(999, 'XXX')|
    len=33 s=|INSERT INTO t1 VALUES(999, 'XXX')|
    len=36 s=|INSERT INTO t1 VALUES(999, 'XXXXXX')|

    I must be missing something.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Craig A. Berry@21:1/5 to All on Fri Jun 6 07:54:21 2025
    On 6/5/25 8:35 PM, Arne Vajhøj wrote:
    On 6/5/2025 5:16 PM, Craig A. Berry wrote:
    On 6/5/25 12:33 PM, Simon Clubley wrote:
    On 2025-06-05, Arne Vajhøj <arne@vajhoej.dk> wrote:
    Mysterious.

    stat = dbfcmd(con, "INSERT INTO t1 VALUES(%d, '%s')", f1, f2esc);

    fails.

    char buf[1000];
    sprintf(buf, "INSERT INTO t1 VALUES(%d, '%s')", f1, f2esc);
    stat = dbcmd(con, buf);

    works.

    And DBLIB.C contains:

    RETCODE
    dbfcmd(DBPROCESS * dbproc, const char *fmt, ...)
    {

          va_start(ap, fmt);
          len = vasprintf(&s, fmt, ap);
          va_end(ap);

    Does this _exact_ code work on Alpha ?

    If yes, try building your test code with compiler optimisation disabled. >>>
    If that doesn't work, try building the library itself with compiler
    optimisation disabled.


    $ mmk clean
    $ @[.vms]configure
    $ mmk/MACRO=__DEBUG__=1


    It is vasprintf that return -1.

    It happens with both normal build and /DEB/NOOPT.

    I have not tried on Alpha yet.

    Any compiler build warnings either in the library or your test code ?
    (Try setting warnings to fatal to abort the build if you get a lot of
    output during building).

    There are just a few %CC-I-QUESTCOMPARE.

    I am trying to eliminate the possibility you may have stumbled across
    a compiler bug.

    It is a mystery.

    I have a query that use dbfcmd and it works. But it fails for
    my updates.

    I created a standalone example doing the same just without any FreeTDS.

    It works fine.

    $ typ z.c
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <stdarg.h>

    void dbfcmd(void *dbproc, const char *fmt, ...)
    {
        va_list ap;
        char *s;
        int len;
        va_start(ap, fmt);
        len = vasprintf(&s, fmt, ap);
        va_end(ap);
        printf("len=%d s=|%s|\n", len, s);
        free(s);
    }

    int main()
    {
        dbfcmd(NULL, "INSERT INTO t1 VALUES(%d, '%s')", 999, "XXX");
        dbfcmd(NULL, "INSERT INTO t1 VALUES(%d, '%s')", 999, "XXX");
        dbfcmd(NULL, "INSERT INTO t1 VALUES(%d, '%s')", 999, "XXXXXX");
        return 0;
    }

    $ r z
    len=33 s=|INSERT INTO t1 VALUES(999, 'XXX')|
    len=33 s=|INSERT INTO t1 VALUES(999, 'XXX')|
    len=36 s=|INSERT INTO t1 VALUES(999, 'XXXXXX')|

    I must be missing something.

    Check for the line in your generated descrip.mms that starts with:

    VASPRINTFOBJ =

    If the right-hand side is empty, you are using the system-supplied
    vasprintf, which is what should happen. If it's not empty, you are
    getting a replacement vasprintf() from FreeTDS, which in turn means the detection code in configure.com is not working right (unless you are on
    a version of VMS before vasprintf() was added to the CRTL). Either
    should work in principle, but it could be one difference between your standalone test and your FreeTDS test.

    It sounds like you are set up to use the debugger now so you can examine
    the arguments being passed to vasprintf(). But you can also create a
    dump file like so:

    $ define TDSDUMP tdsdump.log

    and you'll get all sorts of logging info.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Craig A. Berry on Fri Jun 6 10:42:00 2025
    On 6/6/2025 8:54 AM, Craig A. Berry wrote:
    On 6/5/25 8:35 PM, Arne Vajhøj wrote:
    On 6/5/2025 5:16 PM, Craig A. Berry wrote:
    On 6/5/25 12:33 PM, Simon Clubley wrote:
    On 2025-06-05, Arne Vajhøj <arne@vajhoej.dk> wrote:
    And DBLIB.C contains:

    RETCODE
    dbfcmd(DBPROCESS * dbproc, const char *fmt, ...)
    {

    va_start(ap, fmt);
    len = vasprintf(&s, fmt, ap);
    va_end(ap);

    It is vasprintf that return -1.

    It happens with both normal build and /DEB/NOOPT.
    I created a standalone example doing the same just without any FreeTDS.

    It works fine.
    I must be missing something.

    Check for the line in your generated descrip.mms that starts with:

    VASPRINTFOBJ =

    If the right-hand side is empty, you are using the system-supplied vasprintf, which is what should happen. If it's not empty, you are
    getting a replacement vasprintf() from FreeTDS, which in turn means the detection code in configure.com is not working right (unless you are on
    a version of VMS before vasprintf() was added to the CRTL). Either
    should work in principle, but it could be one difference between your standalone test and your FreeTDS test.

    Bingo.

    $ diff descrip.mms
    ************
    File DKA0:[arne.freetds.freetds-1_5_2]descrip.mms;2
    149 VASPRINTFOBJ =
    150 STRTOK_ROBJ =
    ******
    File DKA0:[arne.freetds.freetds-1_5_2]descrip.mms;1
    149 VASPRINTFOBJ = [.src.replacements]vasprintf$(OBJ),
    150 STRTOK_ROBJ =
    ************

    $ diff [.include]config.h
    ************
    File DKA0:[arne.freetds.freetds-1_5_2.include]config.h;2
    234 #define HAVE_VASPRINTF 1
    235
    ******
    File DKA0:[arne.freetds.freetds-1_5_2.include]config.h;1
    234 #define HAVE_VASPRINTF 0
    235
    ************

    And now dbfcmd works.

    Of course it does not explain why the replacement doesn't work.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Craig A. Berry@21:1/5 to All on Fri Jun 6 10:14:37 2025
    On 6/6/25 9:42 AM, Arne Vajhøj wrote:
    On 6/6/2025 8:54 AM, Craig A. Berry wrote:
    On 6/5/25 8:35 PM, Arne Vajhøj wrote:
    On 6/5/2025 5:16 PM, Craig A. Berry wrote:
    On 6/5/25 12:33 PM, Simon Clubley wrote:
    On 2025-06-05, Arne Vajhøj <arne@vajhoej.dk> wrote:
    And DBLIB.C contains:

    RETCODE
    dbfcmd(DBPROCESS * dbproc, const char *fmt, ...)
    {

           va_start(ap, fmt);
           len = vasprintf(&s, fmt, ap);
           va_end(ap);

    It is vasprintf that return -1.

    It happens with both normal build and /DEB/NOOPT.
    I created a standalone example doing the same just without any FreeTDS.

    It works fine.
    I must be missing something.

    Check for the line in your generated descrip.mms that starts with:

    VASPRINTFOBJ =

    If the right-hand side is empty, you are using the system-supplied vasprintf, which is what should happen.  If it's not empty, you are getting a replacement vasprintf() from FreeTDS, which in turn means the detection code in configure.com is not working right (unless you are on
    a version of VMS before vasprintf() was added to the CRTL).  Either should work in principle, but it could be one difference between your standalone test and your FreeTDS test.

    Bingo.

    $ diff descrip.mms
    ************
    File DKA0:[arne.freetds.freetds-1_5_2]descrip.mms;2
      149   VASPRINTFOBJ =
      150   STRTOK_ROBJ =
    ******
    File DKA0:[arne.freetds.freetds-1_5_2]descrip.mms;1
      149   VASPRINTFOBJ = [.src.replacements]vasprintf$(OBJ),
      150   STRTOK_ROBJ =
    ************

    $ diff [.include]config.h
    ************
    File DKA0:[arne.freetds.freetds-1_5_2.include]config.h;2
      234   #define HAVE_VASPRINTF 1
      235
    ******
    File DKA0:[arne.freetds.freetds-1_5_2.include]config.h;1
      234   #define HAVE_VASPRINTF 0
      235
    ************

    And now dbfcmd works.

    Of course it does not explain why the replacement doesn't work.

    Nor why the detection code doesn't correctly identify that vasprintf is present. That detection code has been there unchanged for 23 years:

    https://github.com/FreeTDS/freetds/blob/a381342bbfccafc0aa9ed2376e38470907d53225/vms/configure.com#L267

    #include <stdarg.h>
    #include <stdio.h>
    #include <stdlib.h>
    int main()
    {
    char *ptr;
    vasprintf(&ptr,"%d,%d",1,2);
    exit(0);
    }

    But it isn't using va_start/va_end, which it surely should. My guess is
    this worked by accident on pre-x86 but something tightened up on x86
    that makes the vasprintf call fail and causes FreeTDS to use its
    fallback implementation. I'm without access at the moment so can't test
    this theory out myself.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Craig A. Berry on Fri Jun 6 11:18:27 2025
    On 6/6/2025 11:14 AM, Craig A. Berry wrote:
    On 6/6/25 9:42 AM, Arne Vajhøj wrote:
    And now dbfcmd works.

    Of course it does not explain why the replacement doesn't work.

    Nor why the detection code doesn't correctly identify that vasprintf is present.  That detection code has been there unchanged for 23 years:

    https://github.com/FreeTDS/freetds/blob/ a381342bbfccafc0aa9ed2376e38470907d53225/vms/configure.com#L267

    #include <stdarg.h>
    #include <stdio.h>
    #include <stdlib.h>
    int main()
    {
    char *ptr;
    vasprintf(&ptr,"%d,%d",1,2);
    exit(0);
    }

    But it isn't using va_start/va_end, which it surely should. My guess is
    this worked by accident on pre-x86 but something tightened up on x86
    that makes the vasprintf call fail and causes FreeTDS to use its
    fallback implementation.  I'm without access at the moment so can't test this theory out myself.

    I can confirm that.

    On x86-64:

    $ typ zzz.c
    #include <stdarg.h>
    #include <stdio.h>
    #include <stdlib.h>
    int main()
    {
    char *ptr;
    vasprintf(&ptr,"%d,%d",1,2);
    exit(0);
    }
    $ cc zzz

    vasprintf(&ptr,"%d,%d",1,2);
    .......................^
    %CC-W-CVTDIFTYPES, In this statement, "1" of type "int", is being
    converted to "long pointer to char".
    at line number 7 in file DKA0:[arne.freetds]zzz.c;1

    vasprintf(&ptr,"%d,%d",1,2);
    ^
    %CC-E-TOOMANYARGS, In this statement, "vasprintf" expects 3 arguments,
    but 4 are supplied.
    at line number 7 in file DKA0:[arne.freetds]zzz.c;1

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to All on Fri Jun 6 11:26:26 2025
    On 6/6/2025 11:18 AM, Arne Vajhøj wrote:
    On x86-64:

    $ typ zzz.c
    #include <stdarg.h>
    #include <stdio.h>
    #include <stdlib.h>
    int main()
    {
    char *ptr;
    vasprintf(&ptr,"%d,%d",1,2);
    exit(0);
    }
    $ cc zzz

    vasprintf(&ptr,"%d,%d",1,2);
    .......................^
    %CC-W-CVTDIFTYPES, In this statement, "1" of type "int", is being
    converted to "long pointer to char".
    at line number 7 in file DKA0:[arne.freetds]zzz.c;1

    vasprintf(&ptr,"%d,%d",1,2);
    ^
    %CC-E-TOOMANYARGS, In this statement, "vasprintf" expects 3 arguments,
    but 4 are supplied.
    at line number 7 in file DKA0:[arne.freetds]zzz.c;1

    On Alpha:

    $ typ zzz.c
    #include <stdarg.h>
    #include <stdio.h>
    #include <stdlib.h>
    int main()
    {
    char *ptr;
    vasprintf(&ptr,"%d,%d",1,2);
    exit(0);
    }
    $ cc zzz

    vasprintf(&ptr,"%d,%d",1,2);
    ^
    %CC-I-IMPLICITFUNC, In this statement, the identifier "vasprintf" is
    implicitly declared as a function.
    at line number 7 in file DISK2:[ARNE]zzz.c;1
    $ link zzz
    %LINK-W-NUDFSYMS, 1 undefined symbol:
    %LINK-I-UDFSYM, VASPRINTF
    %LINK-W-USEUNDEF, undefined symbol VASPRINTF referenced
    in psect $LINK$ offset %X00000030
    in module ZZZ file DISK2:[ARNE]zzz.OBJ;1

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to All on Sat Jun 14 14:09:04 2025
    On 6/3/2025 1:45 PM, Arne Vajhøj wrote:
    On 6/3/2025 1:27 PM, Arne Vajhøj wrote:
    On 6/3/2025 12:22 PM, Richard Jordan wrote:
    Is there a downloadable build of the FreeTDS package for VSI VMS on
    x86 available?  I don't have access to a C compiler to try building it.

    1.5.2 descrip_mms.template has:

    ... Craig A. Berry ... 23-JAN-2003

    But I could take a look at building on x86-64.

    Did.

    * download 1.5.2
    * unpack on VMS
    * @[.vms]configure

    $ diff descrip.mms
    ************
    File DKA0:[arne.freetds.freetds-1_5_2]descrip.mms;2
    149 VASPRINTFOBJ =
    150 STRTOK_ROBJ =
    ******
    File DKA0:[arne.freetds.freetds-1_5_2]descrip.mms;1
    149 VASPRINTFOBJ = [.src.replacements]vasprintf$(OBJ),
    150 STRTOK_ROBJ =
    ************

    $ diff [.include]config.h
    ************
    File DKA0:[arne.freetds.freetds-1_5_2.include]config.h;2
    234 #define HAVE_VASPRINTF 1
    235
    ******
    File DKA0:[arne.freetds.freetds-1_5_2.include]config.h;1
    234 #define HAVE_VASPRINTF 0
    235
    ************

    * mmk
    * note only I message
    * zip tree with -V
    * upload

    https://www.vajhoej.dk/arne/temp/freetds-1_5_2.zip

    I have updated the ZIP with build that use builtin vasprintf
    so dbfcmd functions works.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Sat Jun 14 23:39:28 2025
    On Sat, 14 Jun 2025 14:09:04 -0400, Arne Vajhøj wrote:

    $ diff descrip.mms

    Is there a “diff -u” option? I ask because unified diffs are the common format for exchanging source patches in a format that patch(1) (and other compatible tools) will accept.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Lawrence D'Oliveiro on Sat Jun 14 20:38:47 2025
    On 6/14/2025 7:39 PM, Lawrence D'Oliveiro wrote:
    On Sat, 14 Jun 2025 14:09:04 -0400, Arne Vajhøj wrote:
    $ diff descrip.mms

    Is there a “diff -u” option? I ask because unified diffs are the common format for exchanging source patches in a format that patch(1) (and other compatible tools) will accept.

    $ DIFF /SLP and $ EDIT /SUM

    or grab one of the many ports of *nix diff and patch.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Sun Jun 15 01:58:49 2025
    On Sat, 14 Jun 2025 20:38:47 -0400, Arne Vajhøj wrote:

    On 6/14/2025 7:39 PM, Lawrence D'Oliveiro wrote:

    On Sat, 14 Jun 2025 14:09:04 -0400, Arne Vajhøj wrote:

    $ diff descrip.mms

    Is there a “diff -u” option? I ask because unified diffs are the common >> format for exchanging source patches in a format that patch(1) (and
    other compatible tools) will accept.

    $ DIFF /SLP and $ EDIT /SUM

    But those generate precise editor commands, do they not, complete with
    line numbers that must match in order for the patch to work.

    or grab one of the many ports of *nix diff and patch.

    You mean, they’re not standard equipment?

    The clever thing about the patch(1) tool is, it can apply a patch to a
    file that has already had other patches applied to it: if all they’ve done
    is move the target area a few lines forward or backward, the patch command
    will still succeed.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Lawrence D'Oliveiro on Sun Jun 15 20:22:13 2025
    On 6/14/2025 9:58 PM, Lawrence D'Oliveiro wrote:
    On Sat, 14 Jun 2025 20:38:47 -0400, Arne Vajhøj wrote:

    On 6/14/2025 7:39 PM, Lawrence D'Oliveiro wrote:

    On Sat, 14 Jun 2025 14:09:04 -0400, Arne Vajhøj wrote:

    $ diff descrip.mms

    Is there a “diff -u” option? I ask because unified diffs are the common >>> format for exchanging source patches in a format that patch(1) (and
    other compatible tools) will accept.

    $ DIFF /SLP and $ EDIT /SUM

    But those generate precise editor commands, do they not, complete with
    line numbers that must match in order for the patch to work.

    or grab one of the many ports of *nix diff and patch.

    You mean, they’re not standard equipment?

    The clever thing about the patch(1) tool is, it can apply a patch to a
    file that has already had other patches applied to it: if all they’ve done is move the target area a few lines forward or backward, the patch command will still succeed.

    Context/unified diff & patch are smart.

    But if someone want them then they can get them.

    For command line utilities then I will suggest:
    http://antinode.info/dec/sw/diffutils.html

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Mon Jun 16 01:20:19 2025
    On Sun, 15 Jun 2025 20:22:13 -0400, Arne Vajhøj wrote:

    Context/unified diff & patch are smart.

    But if someone want them then they can get them.

    diff()1 and patch(1) are pretty fundamental tools for open-source collaboration. They should be part of some core installation package,
    like Python.

    For command line utilities then I will suggest:
    http://antinode.info/dec/sw/diffutils.html

    The newest version appears to be 3.8. Checking the upstream site <https://www.gnu.org/software/diffutils/>, this is nearly four years
    old.

    Admittedly, my Debian Unstable only has 3.10, which is still about two
    years old.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Lawrence D'Oliveiro on Sun Jun 15 22:35:20 2025
    On 6/15/2025 9:20 PM, Lawrence D'Oliveiro wrote:
    On Sun, 15 Jun 2025 20:22:13 -0400, Arne Vajhøj wrote:
    Context/unified diff & patch are smart.

    But if someone want them then they can get them.

    diff()1 and patch(1) are pretty fundamental tools for open-source collaboration. They should be part of some core installation package,
    like Python.

    You can do diff as a (long) one liner in Python.

    :-)

    $ python -c "import sys ; import difflib ; sys.stdout.writelines(difflib.unified_diff(open('1.txt').readlines(), open('2.txt').readlines(), '1.txt', '2.txt'))"
    --- 1.txt
    +++ 2.txt
    @@ -1,5 +1,5 @@
    A
    -BB
    CCC
    +XX
    DDDD
    -EEEEE
    +EEXEE

    For command line utilities then I will suggest:
    http://antinode.info/dec/sw/diffutils.html

    The newest version appears to be 3.8. Checking the upstream site <https://www.gnu.org/software/diffutils/>, this is nearly four years
    old.

    Feel free to port latest.

    But I suspect that 3.8 will do fine for most.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Craig A. Berry@21:1/5 to All on Mon Jun 23 17:33:06 2025
    On 6/6/25 10:26 AM, Arne Vajhøj wrote:
    On 6/6/2025 11:18 AM, Arne Vajhøj wrote:
    On x86-64:

    $ typ zzz.c
    #include <stdarg.h>
    #include <stdio.h>
    #include <stdlib.h>
    int main()
    {
    char *ptr;
    vasprintf(&ptr,"%d,%d",1,2);
    exit(0);
    }
    $ cc zzz

    vasprintf(&ptr,"%d,%d",1,2);
    .......................^
    %CC-W-CVTDIFTYPES, In this statement, "1" of type "int", is being
    converted to "long pointer to char".
    at line number 7 in file DKA0:[arne.freetds]zzz.c;1

    vasprintf(&ptr,"%d,%d",1,2);
    ^
    %CC-E-TOOMANYARGS, In this statement, "vasprintf" expects 3 arguments,
    but 4 are supplied.
    at line number 7 in file DKA0:[arne.freetds]zzz.c;1

    On Alpha:

    $ typ zzz.c
    #include <stdarg.h>
    #include <stdio.h>
    #include <stdlib.h>
    int main()
    {
    char *ptr;
    vasprintf(&ptr,"%d,%d",1,2);
    exit(0);
    }
    $ cc zzz

    vasprintf(&ptr,"%d,%d",1,2);
    ^
    %CC-I-IMPLICITFUNC, In this statement, the identifier "vasprintf" is implicitly declared as a function.
    at line number 7 in file DISK2:[ARNE]zzz.c;1
    $ link zzz
    %LINK-W-NUDFSYMS, 1 undefined symbol:
    %LINK-I-UDFSYM,         VASPRINTF
    %LINK-W-USEUNDEF, undefined symbol VASPRINTF referenced
            in psect $LINK$ offset %X00000030
            in module ZZZ file DISK2:[ARNE]zzz.OBJ;1


    vasprintf is not present on Alpha or Itanium with RTL ECO v6 or earlier.
    It only comes along with RTL ECO v9. The only place I can find it
    documented is in a special set of release notes that you get after
    installation (or after rooting through the product kit):

    SYS$HELP:VSI_OPENVMS_CRTL_ECO9_RELEASE_NOTES.TXT

    Note that this is a supplement to the release notes that apply to the
    whole kit and are separately downloadable ahead of time; those are named something like VMS842L1A_RTL-V0900.RELEASE_NOTES.*

    I do hope the new stuff gets into online help and the version dependency
    tables in the CRTL manual at some point.

    I have sent a pull request upstream to the FreeTDS maintainers with a
    better detector for vasprintf:

    https://github.com/FreeTDS/freetds/pull/660

    I haven't had time yet to dig into why the replacement wasn't working.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Craig A. Berry on Mon Jun 23 20:17:57 2025
    On 6/23/2025 6:33 PM, Craig A. Berry wrote:
    I haven't had time yet to dig into why the replacement wasn't working.

    I did a little digging.

    tds_vasprintf contains:

    // fp = NLA0:
    len = vfprintf(fp, fmt, ap);
    // buf = malloc(len + 1)
    if (vsprintf(buf, fmt, ap) != len)
    return -1;

    And the test fails.

    And it can be recreated outside of FreeTDS.

    $ type zzzz.c
    #include <stdio.h>
    #include <stdarg.h>

    void test(const char *fmt, ...)
    {
    va_list ap;
    va_start(ap, fmt);
    int len_1 = vfprintf(stdout, fmt, ap);
    // va_end(ap);
    fprintf(stdout, " [len=%d]\n", len_1);
    char buf[1000];
    // va_start(ap, fmt);
    int len_2 = vsprintf(buf, fmt, ap);
    va_end(ap);
    printf("%s [len=%d]\n", buf,len_2);
    }

    int main(int argc, char *argv[])
    {
    test("INSERT INTO t1 VALUES(%d, '%s')", 999, "XXX");
    test("INSERT INTO t1 VALUES(%d, '%s')", 999, "XXX");
    test("INSERT INTO t1 VALUES(%d, '%s')", 999, "XXX");
    return 0;
    }
    $ cc zzzz
    $ link zzzz
    $ r zzzz
    INSERT INTO t1 VALUES(999, 'XXX') [len=33]
    INSERT INTO t1 VALUES(2060040508, '') [len=37]
    INSERT INTO t1 VALUES(999, 'XXX') [len=33]
    INSERT INTO t1 VALUES(2068038401, '') [len=37]
    INSERT INTO t1 VALUES(999, 'XXX') [len=33]
    INSERT INTO t1 VALUES(2068038401, '') [len=37]

    It seems like vfprintf mess up ap so it is no longer good
    for vsprintf.

    And that is supposedly perfectly legal. C99 says:

    <quote>
    As the functions vfprintf, vfscanf, vprintf, vscanf, vsnprintf,
    vsprintf, and
    vsscanf invoke the va_arg macro, the value of arg after the return is indeterminate.
    </quote>

    Unfortunately that means that the entire fallback algorithm
    in tds_vasprintf (write to null device to find output size,
    allocate buffer of correct size and then write to buffer)
    is broken.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Tue Jun 24 00:37:19 2025
    On Mon, 23 Jun 2025 20:17:57 -0400, Arne Vajhøj wrote:

    Unfortunately that means that the entire fallback algorithm
    in tds_vasprintf (write to null device to find output size,
    allocate buffer of correct size and then write to buffer)
    is broken.

    What a dumb way to do it. Using the null device means making OS I/O calls
    -- needless overhead just to find out how long a string -- which is being
    put together entirely in userland -- needs to be.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Lawrence D'Oliveiro on Mon Jun 23 20:40:21 2025
    On 6/23/2025 8:37 PM, Lawrence D'Oliveiro wrote:
    On Mon, 23 Jun 2025 20:17:57 -0400, Arne Vajhøj wrote:
    Unfortunately that means that the entire fallback algorithm
    in tds_vasprintf (write to null device to find output size,
    allocate buffer of correct size and then write to buffer)
    is broken.

    What a dumb way to do it. Using the null device means making OS I/O calls
    -- needless overhead just to find out how long a string -- which is being
    put together entirely in userland -- needs to be.

    Fallback algorithms are often primitive.

    But if you have a simple better way of achieving it then please send
    the code to Craig so he can send it upstream.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Tue Jun 24 01:41:18 2025
    On Mon, 23 Jun 2025 20:40:21 -0400, Arne Vajhøj wrote:

    On 6/23/2025 8:37 PM, Lawrence D'Oliveiro wrote:

    What a dumb way to do it. Using the null device means making OS I/O
    calls -- needless overhead just to find out how long a string -- which
    is being put together entirely in userland -- needs to be.

    But if you have a simple better way of achieving it ...

    snprintf(3) and vsnprintf(3) return the total number of bytes needed for
    the final result. So just call them once with all the right arguments and
    a zero-length buffer, and use the result to allocate a buffer of the right length, and call them again.

    <https://manpages.debian.org/vsnprintf(3)>

    And of course you should be using va_end(3) and va_start(3) again before
    the second call. This allows for traversing the argument list multiple
    times.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Craig A. Berry@21:1/5 to All on Mon Jun 23 20:49:10 2025
    On 6/23/25 7:40 PM, Arne Vajhøj wrote:
    On 6/23/2025 8:37 PM, Lawrence D'Oliveiro wrote:
    On Mon, 23 Jun 2025 20:17:57 -0400, Arne Vajhøj wrote:
    Unfortunately that means that the entire fallback algorithm
    in tds_vasprintf (write to null device to find output size,
    allocate buffer of correct size and then write to buffer)
    is broken.

    Thanks for chasing that down.

    What a dumb way to do it. Using the null device means making OS I/O calls
    -- needless overhead just to find out how long a string -- which is being
    put together entirely in userland -- needs to be.

    Fallback algorithms are often primitive.

    But if you have a simple better way of achieving it then please send
    the code to Craig so he can send it upstream.

    This one looks pretty simple and depends only on vsnprintf, which has
    been in VMS since 7.3-2, so it was probably in some ancient standard,
    though I haven't looked yet:

    https://github.com/jkaivo/asprintf/blob/asprintf/asprintf.h

    It's MIT which I think can be included in a GPL project like FreeTDS. I
    will try to remember to look at this when I get some other things sorted
    out.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Lawrence D'Oliveiro on Mon Jun 23 22:10:19 2025
    On 6/23/2025 9:41 PM, Lawrence D'Oliveiro wrote:
    On Mon, 23 Jun 2025 20:40:21 -0400, Arne Vajhøj wrote:
    On 6/23/2025 8:37 PM, Lawrence D'Oliveiro wrote:
    What a dumb way to do it. Using the null device means making OS I/O
    calls -- needless overhead just to find out how long a string -- which
    is being put together entirely in userland -- needs to be.

    But if you have a simple better way of achieving it ...

    snprintf(3) and vsnprintf(3) return the total number of bytes needed for
    the final result. So just call them once with all the right arguments and
    a zero-length buffer, and use the result to allocate a buffer of the right length, and call them again.

    That is a lot better than vfprintf to null.

    But it does not solve the problem of multiple v*printf calls.

    And of course you should be using va_end(3) and va_start(3) again before
    the second call. This allows for traversing the argument list multiple
    times.

    Yes. But the argument list for vasprintf is as it is. This is
    code replacing a missing RTL function with a given argument list.

    So calling va_* multiple times means switching from a replacement
    function to #ifdef code in calling function (dbfcmd).

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to All on Mon Jun 23 22:20:10 2025
    On 6/23/2025 10:16 PM, Arne Vajhøj wrote:
    On 6/23/2025 9:49 PM, Craig A. Berry wrote:
    This one looks pretty simple and depends only on vsnprintf, which has
    been in VMS since 7.3-2, so it was probably in some ancient standard,
    though I haven't looked yet:

    https://github.com/jkaivo/asprintf/blob/asprintf/asprintf.h

    It's MIT which I think can be included in a GPL project like FreeTDS. I
    will try to remember to look at this when I get some other things sorted
    out.

    It could definitely be used.

    But why not just steal the trick from it instead of the whole code?

    The trick they use is va_copy.

    I think tds_vasprintf could use va_copy to solve the problem as well!

    But question is of course for both solutions: do we have a problem
    with va_copy missing on older systems?

    It should be added to the standard in C99.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to All on Mon Jun 23 22:35:18 2025
    On 6/23/2025 10:16 PM, Arne Vajhøj wrote:
    On 6/23/2025 9:49 PM, Craig A. Berry wrote:
    This one looks pretty simple and depends only on vsnprintf, which has
    been in VMS since 7.3-2, so it was probably in some ancient standard,
    though I haven't looked yet:

    https://github.com/jkaivo/asprintf/blob/asprintf/asprintf.h

    It's MIT which I think can be included in a GPL project like FreeTDS. I
    will try to remember to look at this when I get some other things sorted
    out.

    It could definitely be used.

    But why not just steal the trick from it instead of the whole code?

    The trick they use is va_copy.

    I think tds_vasprintf could use va_copy to solve the problem as well!

    On the other side, then ditching existing code and taking this
    would also switch form vfprintf to vsnprintf for length
    determination, which Lawrence is correct would be
    better.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Craig A. Berry on Mon Jun 23 22:16:48 2025
    On 6/23/2025 9:49 PM, Craig A. Berry wrote:
    This one looks pretty simple and depends only on vsnprintf, which has
    been in VMS since 7.3-2, so it was probably in some ancient standard,
    though I haven't looked yet:

    https://github.com/jkaivo/asprintf/blob/asprintf/asprintf.h

    It's MIT which I think can be included in a GPL project like FreeTDS. I
    will try to remember to look at this when I get some other things sorted
    out.

    It could definitely be used.

    But why not just steal the trick from it instead of the whole code?

    The trick they use is va_copy.

    I think tds_vasprintf could use va_copy to solve the problem as well!

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Tue Jun 24 03:20:10 2025
    On Mon, 23 Jun 2025 22:10:19 -0400, Arne Vajhøj wrote:

    But the argument list for vasprintf is as it is.

    Use va_copy(3) if you need to traverse a va_list multiple times.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Craig A. Berry@21:1/5 to All on Tue Jun 24 06:51:33 2025
    On 6/23/25 9:35 PM, Arne Vajhøj wrote:
    On 6/23/2025 10:16 PM, Arne Vajhøj wrote:
    On 6/23/2025 9:49 PM, Craig A. Berry wrote:
    This one looks pretty simple and depends only on vsnprintf, which has
    been in VMS since 7.3-2, so it was probably in some ancient standard,
    though I haven't looked yet:

    https://github.com/jkaivo/asprintf/blob/asprintf/asprintf.h

    It's MIT which I think can be included in a GPL project like FreeTDS. I
    will try to remember to look at this when I get some other things sorted >>> out.

    It could definitely be used.

    But why not just steal the trick from it instead of the whole code?

    The trick they use is va_copy.

    I think tds_vasprintf could use va_copy to solve the problem as well!

    On the other side, then ditching existing code and taking this
    would also switch form vfprintf to vsnprintf for length
    determination, which Lawrence is correct would be
    better.

    It turns out tds_vasprtintf already uses vsnprintf if HAVE_VSNPRINTF is
    defined and only falls back to the /dev/null hack if not:

    https://github.com/FreeTDS/freetds/blob/master/src/replacements/vasprintf.c

    So my next change to FreeTDS will look something like this:

    --- vms/config_h.vms;-0 Tue Sep 26 09:23:25 2023
    +++ vms/config_h.vms Tue Jun 24 05:23:20 2025
    @@ -237,7 +237,9 @@
    #define HAVE_SNPRINTF @D_SNPRINTF@

    /* Define to 1 if you have the `vsnprintf' function. */
    -#define HAVE_VSNPRINTF 0
    +#if __CRTL_VER >= 70302000
    +#define HAVE_VSNPRINTF 1
    +#endif

    /* Define as const if the declaration of iconv() needs const. */
    #if HAVE_ICONV
    [end_of_patch]

    That should cover everybody with a VMS system from recent decades.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Craig A. Berry on Tue Jun 24 12:25:50 2025
    On 6/24/2025 7:51 AM, Craig A. Berry wrote:
    On 6/23/25 9:35 PM, Arne Vajhøj wrote:
    On 6/23/2025 10:16 PM, Arne Vajhøj wrote:
    But why not just steal the trick from it instead of the whole code?

    The trick they use is va_copy.

    I think tds_vasprintf could use va_copy to solve the problem as well!

    On the other side, then ditching existing code and taking this
    would also switch form vfprintf to vsnprintf for length
    determination, which Lawrence is correct would be
    better.

    It turns out tds_vasprtintf already uses vsnprintf if HAVE_VSNPRINTF is defined and only falls back to the /dev/null hack if not:

    https://github.com/FreeTDS/freetds/blob/master/src/replacements/vasprintf.c

    So my next change to FreeTDS will look something like this:

    --- vms/config_h.vms;-0    Tue Sep 26 09:23:25 2023
    +++ vms/config_h.vms    Tue Jun 24 05:23:20 2025
    @@ -237,7 +237,9 @@
     #define HAVE_SNPRINTF @D_SNPRINTF@

     /* Define to 1 if you have the `vsnprintf' function. */
    -#define HAVE_VSNPRINTF 0
    +#if __CRTL_VER >= 70302000
    +#define HAVE_VSNPRINTF 1
    +#endif

     /* Define as const if the declaration of iconv() needs const. */
     #if HAVE_ICONV
    [end_of_patch]

    That should cover everybody with a VMS system from recent decades.

    I don't like the #elif HAVE_VSNPRINTF code in tds_vasprintf.

    :-)

    The algorithm is basically to try vsnprintf with buffers
    increasing in size 512, 1024, 1536, ... until there is space.

    That is very inefficient compared to just use the return value.

    And since it does not use va_copy, then only the first call to
    vsnprintf will work.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to All on Tue Jun 24 20:38:57 2025
    On 6/24/2025 12:25 PM, Arne Vajhøj wrote:
    I don't like the #elif HAVE_VSNPRINTF code in tds_vasprintf.

    :-)

    The algorithm is basically to try vsnprintf with buffers
    increasing in size 512, 1024, 1536, ... until there is space.

    That is very inefficient compared to just use the return value.

    And since it does not use va_copy, then only the first call to
    vsnprintf will work.

    Even if va_copy was called and we ignore the option of
    using vsnprintf return value, then +512 is not good
    compared to *2. +512 with a 1 GB CLOB value would mean
    2 million calls to vsnprintf.

    (I have not checked if TDS actually allow that long
    statements, but +512 is a problem)

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Craig A. Berry@21:1/5 to All on Wed Jun 25 07:15:43 2025
    On 6/24/25 7:38 PM, Arne Vajhøj wrote:
    On 6/24/2025 12:25 PM, Arne Vajhøj wrote:
    I don't like the #elif HAVE_VSNPRINTF code in tds_vasprintf.

    :-)

    The algorithm is basically to try vsnprintf with buffers
    increasing in size 512, 1024, 1536, ... until there is space.

    That is very inefficient compared to just use the return value.

    And since it does not use va_copy, then only the first call to
    vsnprintf will work.

    Even if va_copy was called and we ignore the option of
    using vsnprintf return value, then +512 is not good
    compared to *2. +512 with a 1 GB CLOB value would mean
    2 million calls to vsnprintf.

    (I have not checked if TDS actually allow that long
    statements, but +512 is a problem)

    TDS packet length is negotiated but must be between 512 and 32,767 bytes:

    https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-tds/c1cddd03-b448-470a-946a-9b1b908f27a7

    And vasprintf appears to be used in FreeTDS only for query strings and commands, not for data.

    You aren't wrong, but improving the vasprintf implementation isn't my
    highest priority; feel free to take a crack at it and submit a PR. To
    work against current sources, you either need to use autoconf with a git checkout or get a nightly snapshot from <https://www.freetds.org>.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Fri Jun 27 00:03:04 2025
    On Tue, 24 Jun 2025 12:25:50 -0400, Arne Vajhøj wrote:

    The algorithm is basically to try vsnprintf with buffers increasing in
    size 512, 1024, 1536, ... until there is space.

    That is very inefficient compared to just use the return value.

    It’s a good choice in the absence of any other clues as to how much to allocate. I have used it myself. (Of course having an explicit number
    returned is best of all.)

    One may conduct statistical analyses, I suppose to decide on the optimum multiplication factor. E.g. would going up in powers of 3 better than
    powers of 2? I suspect larger factors (too far from e = 2.71828 ...) would
    not be worth using.

    And don’t forget to do a realloc(3) at the end, to trim the unused part of the last allocation.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Lawrence D'Oliveiro on Tue Jul 1 16:14:19 2025
    On 6/26/2025 8:03 PM, Lawrence D'Oliveiro wrote:
    On Tue, 24 Jun 2025 12:25:50 -0400, Arne Vajhøj wrote:
    The algorithm is basically to try vsnprintf with buffers increasing in
    size 512, 1024, 1536, ... until there is space.

    That is very inefficient compared to just use the return value.

    It’s a good choice in the absence of any other clues as to how much to allocate. I have used it myself. (Of course having an explicit number returned is best of all.)

    One may conduct statistical analyses, I suppose to decide on the optimum multiplication factor. E.g. would going up in powers of 3 better than
    powers of 2? I suspect larger factors (too far from e = 2.71828 ...) would not be worth using.

    The specific factor is not as important as using multiplication instead
    of addition.

    Starting at 512 going towards a 1 GB CLOB:

    +512 => 2 million
    *1.5 => 36
    *2 => 21
    *3 => 13

    And don’t forget to do a realloc(3) at the end, to trim the unused part of the last allocation.

    That seems very natural.

    But for FreeTDS dbfcmd then it has:

    vasprintf call to format buffer
    test of return value
    dbcmd call with buffer
    free call for buffer

    No point in realloc.

    And I suspect that the pattern is common: format, use
    once and free immediately.

    So I don't know if it makes sense.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Craig A. Berry on Tue Jul 1 15:57:19 2025
    On 6/25/2025 8:15 AM, Craig A. Berry wrote:
    On 6/24/25 7:38 PM, Arne Vajhøj wrote:
    On 6/24/2025 12:25 PM, Arne Vajhøj wrote:
    I don't like the #elif HAVE_VSNPRINTF code in tds_vasprintf.

    :-)

    The algorithm is basically to try vsnprintf with buffers
    increasing in size 512, 1024, 1536, ... until there is space.

    That is very inefficient compared to just use the return value.

    And since it does not use va_copy, then only the first call to
    vsnprintf will work.

    Even if va_copy was called and we ignore the option of
    using vsnprintf return value, then +512 is not good
    compared to *2. +512 with a 1 GB CLOB value would mean
    2 million calls to vsnprintf.

    (I have not checked if TDS actually allow that long
    statements, but +512 is a problem)

    TDS packet length is negotiated but must be between 512 and 32,767 bytes:

    https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-tds/ c1cddd03-b448-470a-946a-9b1b908f27a7

    And vasprintf appears to be used in FreeTDS only for query strings and commands, not for data.

    dbfcmd (which calls vasprintf) are for SQL statements in general.

    SELECT and DELETE statements are not likely to be really long.

    But INSERT and UPDATE statements does contain data and can
    therefore be very long.

    I tried using dbfcmd to insert 100 KB, 200 KB and 400 KB values.
    They all succeeded (very slow but I don't know if that is the library
    or something in my setup). So somewhere it must be able to chunk up such
    a long SQL statement into multiple TDS packets.

    You aren't wrong, but improving the vasprintf implementation isn't my
    highest priority;

    There are 8 billion people on this planet. It is not your
    responsibility.

    feel free to take a crack at it and submit a PR.  To
    work against current sources, you either need to use autoconf with a git checkout or get a nightly snapshot from <https://www.freetds.org>.

    I may take a look.

    Submitting a PR may be a problem. I have never had a personal
    Github account and I suspect that my work Github account has
    been deactivated. But I will worry about that when/if I have
    a good fix.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Wed Jul 2 00:10:19 2025
    On Tue, 1 Jul 2025 15:57:19 -0400, Arne Vajhøj wrote:

    Submitting a PR may be a problem. I have never had a personal Github
    account and I suspect that my work Github account has been
    deactivated.

    I’m not a fan of tying your workflows into GitHub-specific functions.
    Git is much more versatile than that.

    Linus Torvalds on GitHub pull requests: <https://github.com/torvalds/linux/pull/17#issuecomment-5654674>

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to All on Thu Jul 3 11:05:09 2025
    On 7/1/2025 3:57 PM, Arne Vajhøj wrote:
    On 6/25/2025 8:15 AM, Craig A. Berry wrote:
                      feel free to take a crack at it and submit a PR.  To
    work against current sources, you either need to use autoconf with a git
    checkout or get a nightly snapshot from <https://www.freetds.org>.

    I may take a look.

    Submitting a PR may be a problem. I have never had a personal
    Github account and I suspect that my work Github account has
    been deactivated. But I will worry about that when/if I have
    a good fix.

    First take:

    https://www.vajhoej.dk/arne/temp/tds_vasprintf.c

    Total rewrite. I did not like the old code at all.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Dan Cross on Thu Jul 3 14:15:33 2025
    On 7/3/2025 1:40 PM, Dan Cross wrote:
    In article <68669c25$0$690$14726298@news.sunsite.dk>,
    Arne Vajhøj <arne@vajhoej.dk> wrote:
    On 7/1/2025 3:57 PM, Arne Vajhøj wrote:
    On 6/25/2025 8:15 AM, Craig A. Berry wrote:
                      feel free to take a crack at it and submit a PR.  To
    work against current sources, you either need to use autoconf with a git >>>> checkout or get a nightly snapshot from <https://www.freetds.org>.

    I may take a look.

    Submitting a PR may be a problem. I have never had a personal
    Github account and I suspect that my work Github account has
    been deactivated. But I will worry about that when/if I have
    a good fix.

    First take:

    https://www.vajhoej.dk/arne/temp/tds_vasprintf.c

    Total rewrite. I did not like the old code at all.

    Your code is incorrect.

    Note that `vsnprintf` et al do not include space for the NUL
    terminator in their return value, and you do not account for
    this when you malloc your destination buffer.

    Ouch.

    I will add +1 everywhere for space.

    Thanks.

    As the main logic is the same in each case (find out the buffer
    size, allocate, and then assign and return), and the only real
    difference is in finding out the buffer length, your code would
    be more readable with some helper functions that you delegated
    to.

    As states in the comments then it was a design goal to have
    simple code blocks - no nested if's, no functions - for a given
    context just 1-10 lines of code.

    You refer to `_PATH_DEVNULL` but do not `#include <paths.h>`, as
    required by POSIX.

    This must support non *nix systems (as an example
    VMS !!) - config.h is expected to provide that with the rest.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Dan Cross@21:1/5 to arne@vajhoej.dk on Thu Jul 3 17:40:59 2025
    In article <68669c25$0$690$14726298@news.sunsite.dk>,
    Arne Vajhøj <arne@vajhoej.dk> wrote:
    On 7/1/2025 3:57 PM, Arne Vajhøj wrote:
    On 6/25/2025 8:15 AM, Craig A. Berry wrote:
                      feel free to take a crack at it and submit a PR.  To
    work against current sources, you either need to use autoconf with a git >>> checkout or get a nightly snapshot from <https://www.freetds.org>.

    I may take a look.

    Submitting a PR may be a problem. I have never had a personal
    Github account and I suspect that my work Github account has
    been deactivated. But I will worry about that when/if I have
    a good fix.

    First take:

    https://www.vajhoej.dk/arne/temp/tds_vasprintf.c

    Instead of putting this on a web page on your private sever, you
    should consider putting it on github or similar in a place where
    people can comment directly on the code.

    Total rewrite. I did not like the old code at all.

    Your code is incorrect.

    Note that `vsnprintf` et al do not include space for the NUL
    terminator in their return value, and you do not account for
    this when you malloc your destination buffer. Since you then
    use the (unsafe, unchecked) `vsprintf` function to actually
    format, you're writing off the end of the allocated buffer,
    resulting in undefined behavior. I get that you use `vsprintf`
    here for maximum portability, but it's a poor substitute for
    `vsnprintf`. Sufficiently so, that I think you're better off
    using it if you can.

    As the main logic is the same in each case (find out the buffer
    size, allocate, and then assign and return), and the only real
    difference is in finding out the buffer length, your code would
    be more readable with some helper functions that you delegated
    to.

    You refer to `_PATH_DEVNULL` but do not `#include <paths.h>`, as
    required by POSIX.

    Your standards matrix includes C90, but note that C90 is not
    (generally) going to be supported by your code, unless the
    system already supports `vasprintf` or `va_copy` as an
    extension. In particular, every option that does not include
    `vsnprintf` requires `va_copy`, so you may as well test for that
    once and error immediately.

    An obvious optimization, in the cases where you try `vsnprintf`,
    would be to observe that most strings are going to be fairly
    small (say, a K or less) and speculatively write into a
    stack-allocated buffer when determining the size; if the result
    fits, then simply `strdup` the result and return. This would
    avoid having to call into the (comparatively expensive)
    formatting code twice, but it hardly seems worth it; I didn't
    bother in my implementation (which is below).

    - Dan C.

    /*
    * SPDX-License-Identifier: LGPL 2.1 OR Apache 2.0
    *
    * vasprintf implementation
    */

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

    //#include "config.h"

    static const size_t FAIL = ~(size_t)0;

    #ifdef HAVE_VASPRINTF

    int
    tds_vasprintf(char **outp, const char *fmt, va_list ap)
    {
    return vasprintf(outp, fmt, ap);
    }

    #else

    #ifndef va_copy
    #error "va_copy macro required"
    #endif

    #ifdef HAVE_VSNPRINTF

    static inline int
    tds_vsnprintf(char *dst, size_t len, const char *fmt, va_list ap)
    {
    return vsnprintf(dst, len, fmt, ap);
    }

    static inline size_t
    tds_vasprintf_bufsize(const char *fmt, va_list ap)
    {
    int ret = vsnprintf(NULL, 0, fmt, ap);
    if (ret < 0)
    return FAIL;
    return ret;
    }

    #else

    // Note: this is extremely dangerous, as `len` is mostly ignored.
    static inline int
    tds_vsnprintf(char *dst, size_t len, const char *fmt, va_list ap)
    {
    if (dst == NULL || len == 0)
    return -1;
    return vsprintf(dst, fmt, ap);
    }

    #include <paths.h>

    #ifdef REENTRANT
    #define neednull(fp) 1
    #define putnull(fp) fclose(fp)
    #else
    #define neednull(fp) ((fp) == NULL)
    #define putnull(fp)
    #endif

    static inline size_t
    tds_vasprintf_bufsize(const char *fmt, va_list ap)
    {
    #ifdef HAVE_VSCPRINTF
    return _vscprintf(fmt, ap);
    #endif
    static FILE *fp = NULL;

    if (neednull(fp))
    fp = fopen(_PATH_DEVNULL, "w");
    if (fp == NULL)
    return FAIL;
    int len = vfprintf(fp, fmt, ap);
    if(len < 0)
    return FAIL;
    putnull(fp);
    return (size_t)len;
    }

    #endif

    int tds_vasprintf(char **outp, const char *fmt, va_list ap)
    {
    va_list save_ap;
    va_copy(save_ap, ap);
    size_t len = tds_vasprintf_bufsize(fmt, ap);
    if (len == ~(size_t)0)
    return -1;

    char *dst = malloc(len);
    if (dst == NULL)
    return -1;

    vsprintf(dst, fmt, save_ap);
    *outp = dst;

    return (int)len;
    }

    #endif

    #ifdef TEST

    #include <assert.h>
    #include <string.h>

    char *
    dovasprintf(const char *fmt, ...)
    {
    char *out = NULL;
    int rv = 0;
    va_list ap;
    va_start(ap, fmt);
    out = NULL;
    rv = tds_vasprintf(&out, fmt, ap);
    va_end(ap);
    if (rv < 0)
    return NULL;
    return out;
    }

    int
    main()
    {
    char *p = dovasprintf("This is the %dth test: %s", 10, "test string");
    assert(strcmp(p, "This is the 10th test: test string") == 0);
    return EXIT_SUCCESS;
    }

    #endif

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Craig A. Berry@21:1/5 to Dan Cross on Thu Jul 3 17:31:07 2025
    On 7/3/25 3:07 PM, Dan Cross wrote:
    In article <1046hc5$62th$2@dont-email.me>,
    Arne Vajhøj <arne@vajhoej.dk> wrote:
    On 7/3/2025 1:40 PM, Dan Cross wrote:

    You refer to `_PATH_DEVNULL` but do not `#include <paths.h>`, as
    required by POSIX.

    This must support non *nix systems (as an example
    VMS !!) - config.h is expected to provide that with the rest.

    If copyright dates are anything to judge by, `<paths.h>` has
    been a thing since 1989, but I was wrong in that it is not
    actually in POSIX: it is a BSD extension, though common.

    In general, it is good practice and good hygiene in C programs
    to `#include` the header files that are documented to define the
    symbols and types that you use in your program, instead of
    relying on transitive includes to populate things ambiantly via
    e.g. `config.h`.

    Regardless, in that case, you shouldn't use `_PATH_DEVNULL`.
    Note the leading `_`: that signifies a reserved identifier,
    which is not something you should be defining yourself. If you
    want to punt this to e.g. config.h, better would be to define a
    new name (say, `TDS_PATH_DEVNULL`) and use that.

    The code ya'll are talking about replacing:

    https://github.com/FreeTDS/freetds/blob/Branch-1_5/src/replacements/vasprintf.c

    does the following:

    #if HAVE_PATHS_H
    #include <paths.h>
    #endif /* HAVE_PATHS_H */
    . . .
    #ifndef _PATH_DEVNULL
    #define _PATH_DEVNULL "/dev/null"
    #endif

    FreeTDS is relying on autoconf to sort out whether paths.h is available
    and will only include it if HAVE_PATHS_H is defined in config.h, and
    thus only defines _PATH_DEVNULL if it's known not to exist. Obviously
    one should avoid making up one's own reserved identifiers and clobbering documented and well-known names, but that's not really the same thing as providing an implementation for one that's known to be missing. This
    fallback approach could of course be added to Arne's implementation if
    desired; to me it seems a lot cleaner than defining yet another macro,
    which would need to be defined in terms of _PATH_DEVNULL when it exists
    but otherwise not.

    The problem for VMS is that the workaround doesn't work. Or at least it
    didn't when I was first porting FreeTDS 20+ years ago because the CRTL
    at the time did not recognize '/dev/null' as a valid path. I believe
    recentish CRTLs (maybe VMS 8.x and later?) do have special case code
    that translates it to the native null device. But that wasn't
    available, so I added the following line to the template from which
    config.h is generated on VMS:

    #define _PATH_DEVNULL "_NLA0:"

    The main advantage of this approach is that it worked, and it did so
    with a one-line change to a file I was already maintaining. I didn't
    have to sprinkle '#ifdef __VMS' in files that already existed and were
    already working on other platforms. I didn't have to create a lot of new
    files, the presence of which in the repository I couldn't test without autoconf/automake, which I didn't have access to at the time. All of
    which meant upstream maintainers were far more likely to accept my changes.

    I'm sure I'll get a lecture about everything I did wrong, but at least
    now the context is out there for why it was reasonable for Arne to
    depend on prior art, i.e., the presence of _PATH_DEVNULL in config.h.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Dan Cross@21:1/5 to arne@vajhoej.dk on Thu Jul 3 20:07:23 2025
    In article <1046hc5$62th$2@dont-email.me>,
    Arne Vajhøj <arne@vajhoej.dk> wrote:
    On 7/3/2025 1:40 PM, Dan Cross wrote:
    In article <68669c25$0$690$14726298@news.sunsite.dk>,
    Arne Vajhøj <arne@vajhoej.dk> wrote:
    On 7/1/2025 3:57 PM, Arne Vajhøj wrote:
    On 6/25/2025 8:15 AM, Craig A. Berry wrote:
                      feel free to take a crack at it and submit a PR.  To
    work against current sources, you either need to use autoconf with a git >>>>> checkout or get a nightly snapshot from <https://www.freetds.org>.

    I may take a look.

    Submitting a PR may be a problem. I have never had a personal
    Github account and I suspect that my work Github account has
    been deactivated. But I will worry about that when/if I have
    a good fix.

    First take:

    https://www.vajhoej.dk/arne/temp/tds_vasprintf.c

    Total rewrite. I did not like the old code at all.

    Your code is incorrect.

    Note that `vsnprintf` et al do not include space for the NUL
    terminator in their return value, and you do not account for
    this when you malloc your destination buffer.

    Ouch.

    Yeah, well, stupidly my own code had the same problem when I
    posted it, plus a bunch of other bogons, which is why my test
    did not catch the errors. Silly me.

    I will add +1 everywhere for space.

    Then you should probably test for overflow, since *printf return
    a signed int, and signed integer overflow is UB. It is easier
    to do the arithmetic unsigned, as I have done.

    Thanks.

    As the main logic is the same in each case (find out the buffer
    size, allocate, and then assign and return), and the only real
    difference is in finding out the buffer length, your code would
    be more readable with some helper functions that you delegated
    to.

    As states in the comments then it was a design goal to have
    simple code blocks - no nested if's, no functions - for a given
    context just 1-10 lines of code.

    That's a bad design goal.

    This sort of organization leads to lots of duplication and tends
    to produce code that is overly verbose and fragile, where one
    section may be updated and the others skipped or missed. A
    little bit of duplication for the sake of comprehensibility is
    ok, but this is excessive.

    A helper function to abstract away some of the boilerplate is
    not going to hurt you. See below.

    You refer to `_PATH_DEVNULL` but do not `#include <paths.h>`, as
    required by POSIX.

    This must support non *nix systems (as an example
    VMS !!) - config.h is expected to provide that with the rest.

    If copyright dates are anything to judge by, `<paths.h>` has
    been a thing since 1989, but I was wrong in that it is not
    actually in POSIX: it is a BSD extension, though common.

    In general, it is good practice and good hygiene in C programs
    to `#include` the header files that are documented to define the
    symbols and types that you use in your program, instead of
    relying on transitive includes to populate things ambiantly via
    e.g. `config.h`.

    Regardless, in that case, you shouldn't use `_PATH_DEVNULL`.
    Note the leading `_`: that signifies a reserved identifier,
    which is not something you should be defining yourself. If you
    want to punt this to e.g. config.h, better would be to define a
    new name (say, `TDS_PATH_DEVNULL`) and use that.

    Unfortunately, the C language does not provide any mechanism
    beyond the extremely crude notion of "translation units" and
    `static` for building namespaces that limit the visibility of
    non-local identifiers, and the preprocessor bypasses even that,
    so you have to resort to hacky and fragile things like manually
    adding text prefaces to have some approximation of the desired
    effect. But you have already got one of those, so you may as
    well use it.

    - Dan C.

    Here is a version that avoids nested #if:

    /*
    * SPDX-License-Identifier: LGPL 2.1 OR Apache 2.0
    *
    * vasprintf implementation
    */

    #include <assert.h>
    #include <stdarg.h>
    #include <stdio.h>
    #include <stdlib.h>

    #include "config.h"

    static const size_t FAIL = ~(size_t)0;

    static inline int
    tds_vsnprintf(char *dst, size_t len, const char *fmt, va_list ap)
    {
    #ifdef HAVE_VSNPRINTF
    return vsnprintf(dst, len, fmt, ap);
    #else
    assert(dst != NULL);
    assert(len != 0);
    return vsprintf(dst, fmt, ap);
    #endif
    }


    #ifdef REENTRANT
    #define neednull(fp) 1
    #define putnull(fp) fclose(fp)
    #else
    #define neednull(fp) ((fp) == NULL)
    #define putnull(fp)
    #endif

    static inline size_t
    tds_vasprintf_bufsize(const char *fmt, va_list ap)
    {
    int len;
    #if defined(HAVE_VSCPRINTF)
    len = _vscprintf(fmt, ap);
    #elif defined(HAVE_VSNPRINTF)
    len = vsnprintf(NULL, 0, fmt, ap);
    #else
    static FILE *fp = NULL;
    if (neednull(fp))
    fp = fopen(TDS_PATH_DEVNULL, "w");
    if (fp == NULL)
    return FAIL;
    len = vfprintf(fp, fmt, ap);
    putnull(fp);
    #endif
    if(len < 0)
    return FAIL;
    return (size_t)len + 1;
    }

    int
    tds_vasprintf(char **outp, const char *fmt, va_list ap)
    {
    #if defined(HAVE_VASPRINTF)
    return vasprintf(outp, fmt, ap);
    #elif !defined(va_copy)
    #error "va_copy macro required"
    #else
    va_list save_ap;
    va_copy(save_ap, ap);
    size_t len = tds_vasprintf_bufsize(fmt, ap);
    if (len == FAIL)
    return -1;
    char *dst = malloc(len);
    if (dst == NULL)
    return -1;
    tds_vsnprintf(dst, len, fmt, save_ap);
    *outp = dst;
    return (int)len;
    #endif
    }

    #ifdef TEST

    #include <assert.h>
    #include <string.h>

    char *
    dovasprintf(const char *fmt, ...)
    {
    va_list ap;
    va_start(ap, fmt);
    char *out = NULL;
    int rv = tds_vasprintf(&out, fmt, ap);
    va_end(ap);
    if (rv < 0)
    return NULL;
    return out;
    }

    int
    main()
    {
    char *p = dovasprintf("This is the %dth test: %s", 10, "test string");
    assert(strcmp(p, "This is the 10th test: test string") == 0);
    return EXIT_SUCCESS;
    }

    #endif

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Fri Jul 4 00:51:04 2025
    On Thu, 3 Jul 2025 14:15:33 -0400, Arne Vajhøj wrote:

    You refer to `_PATH_DEVNULL` but do not `#include <paths.h>`, as
    required by POSIX.

    This must support non *nix systems (as an example VMS !!) - config.h is expected to provide that with the rest.

    Also, there is no paths.h in POSIX. It doesn’t make any sense to have it
    in POSIX.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Dan Cross on Thu Jul 3 20:21:50 2025
    On 7/3/2025 4:07 PM, Dan Cross wrote:
    In article <1046hc5$62th$2@dont-email.me>,
    Arne Vajhøj <arne@vajhoej.dk> wrote:
    On 7/3/2025 1:40 PM, Dan Cross wrote:
    As the main logic is the same in each case (find out the buffer
    size, allocate, and then assign and return), and the only real
    difference is in finding out the buffer length, your code would
    be more readable with some helper functions that you delegated
    to.

    As states in the comments then it was a design goal to have
    simple code blocks - no nested if's, no functions - for a given
    context just 1-10 lines of code.

    That's a bad design goal.

    This sort of organization leads to lots of duplication and tends
    to produce code that is overly verbose and fragile, where one
    section may be updated and the others skipped or missed. A
    little bit of duplication for the sake of comprehensibility is
    ok, but this is excessive.

    It is very easy to read this way.

    Just scan sequentially down until one find the relevant
    #if context and then read 1-10 lines sequentially.
    No jumping up and down.

    You refer to `_PATH_DEVNULL` but do not `#include <paths.h>`, as
    required by POSIX.

    This must support non *nix systems (as an example
    VMS !!) - config.h is expected to provide that with the rest.

    If copyright dates are anything to judge by, `<paths.h>` has
    been a thing since 1989, but I was wrong in that it is not
    actually in POSIX: it is a BSD extension, though common.

    This code should also work on non-*nix. It does not matter
    if all or most *nix has had it since 1989.

    In general, it is good practice and good hygiene in C programs
    to `#include` the header files that are documented to define the
    symbols and types that you use in your program, instead of
    relying on transitive includes to populate things ambiantly via
    e.g. `config.h`.

    Regardless, in that case, you shouldn't use `_PATH_DEVNULL`.
    Note the leading `_`: that signifies a reserved identifier,
    which is not something you should be defining yourself. If you
    want to punt this to e.g. config.h, better would be to define a
    new name (say, `TDS_PATH_DEVNULL`) and use that.

    This has to fit into an existing project that has its
    way to setup things.

    I also think that its way makes sense. It would not be good
    to start putting OS specific includes/defines into lots
    of C files. That is what the configure process is supposed to
    handle.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Craig A. Berry on Thu Jul 3 20:24:39 2025
    On 7/3/2025 6:31 PM, Craig A. Berry wrote:
    https://github.com/FreeTDS/freetds/blob/Branch-1_5/src/replacements/ vasprintf.c

    does the following:

    #if HAVE_PATHS_H
    #include <paths.h>
    #endif /* HAVE_PATHS_H */
    . . .
    #ifndef _PATH_DEVNULL
    #define _PATH_DEVNULL "/dev/null"
    #endif

    FreeTDS is relying on autoconf to sort out whether paths.h is available
    and will only include it if HAVE_PATHS_H is defined in config.h, and
    thus only defines _PATH_DEVNULL if it's known not to exist. Obviously
    one should avoid making up one's own reserved identifiers and clobbering documented and well-known names, but that's not really the same thing as providing an implementation for one that's known to be missing. This
    fallback approach could of course be added to Arne's implementation if desired; to me it seems a lot cleaner than defining yet another macro,
    which would need to be defined in terms of _PATH_DEVNULL when it exists
    but otherwise not.

    The problem for VMS is that the workaround doesn't work.  Or at least it didn't when I was first porting FreeTDS 20+ years ago because the CRTL
    at the time did not recognize '/dev/null' as a valid path.  I believe recentish CRTLs (maybe VMS 8.x and later?) do have special case code
    that translates it to the native null device.  But that wasn't
    available, so I added the following line to the template from which
    config.h is generated on VMS:

    #define _PATH_DEVNULL "_NLA0:"

    The main advantage of this approach is that it worked, and it did so
    with a one-line change to a file I was already maintaining. I didn't
    have to sprinkle '#ifdef __VMS' in files that already existed and were already working on other platforms. I didn't have to create a lot of new files, the presence of which in the repository I couldn't test without autoconf/automake, which I didn't have access to at the time.  All of
    which meant upstream maintainers were far more likely to accept my changes.

    So you are saying that for the code to work on *nix then I need
    to keep those, because having it defined in config.h is a non-*nix
    thing and for *nix it only set HAVE_PATHS_H which requires the
    include in this file?

    I will update.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Craig A. Berry@21:1/5 to All on Thu Jul 3 20:00:45 2025
    On 7/3/25 7:24 PM, Arne Vajhøj wrote:
    On 7/3/2025 6:31 PM, Craig A. Berry wrote:
    https://github.com/FreeTDS/freetds/blob/Branch-1_5/src/replacements/
    vasprintf.c

    does the following:

    #if HAVE_PATHS_H
    #include <paths.h>
    #endif /* HAVE_PATHS_H */
    . . .
    #ifndef _PATH_DEVNULL
    #define _PATH_DEVNULL "/dev/null"
    #endif

    FreeTDS is relying on autoconf to sort out whether paths.h is available
    and will only include it if HAVE_PATHS_H is defined in config.h, and
    thus only defines _PATH_DEVNULL if it's known not to exist. Obviously
    one should avoid making up one's own reserved identifiers and clobbering
    documented and well-known names, but that's not really the same thing as
    providing an implementation for one that's known to be missing. This
    fallback approach could of course be added to Arne's implementation if
    desired; to me it seems a lot cleaner than defining yet another macro,
    which would need to be defined in terms of _PATH_DEVNULL when it exists
    but otherwise not.

    The problem for VMS is that the workaround doesn't work.  Or at least it
    didn't when I was first porting FreeTDS 20+ years ago because the CRTL
    at the time did not recognize '/dev/null' as a valid path.  I believe
    recentish CRTLs (maybe VMS 8.x and later?) do have special case code
    that translates it to the native null device.  But that wasn't
    available, so I added the following line to the template from which
    config.h is generated on VMS:

    #define _PATH_DEVNULL "_NLA0:"

    The main advantage of this approach is that it worked, and it did so
    with a one-line change to a file I was already maintaining. I didn't
    have to sprinkle '#ifdef __VMS' in files that already existed and were
    already working on other platforms. I didn't have to create a lot of new
    files, the presence of which in the repository I couldn't test without
    autoconf/automake, which I didn't have access to at the time.  All of
    which meant upstream maintainers were far more likely to accept my
    changes.

    So you are saying that for the code to work on *nix then I need
    to keep those, because having it defined in config.h is a non-*nix
    thing and for *nix it only set HAVE_PATHS_H which requires the
    include in this file?

    I will update.

    I think preserving the HAVE_PATHS_H check is probably a good idea. It
    makes sure you get the correct, system-specific version of
    _PATH_DEVNULL, if any. And it preserves an existing approach to
    portability that you aren't worried about so you can focus on the
    vasprintf() guts.

    I haven't done any testing of your code. I do agree with Dan that
    reducing the repeated code would be a benefit. You don't need the first
    #if because the file won't even be built if HAS_VASPRINTF is detected
    during configuration. Which means for the remaining 4 cases you've got
    this in all of them that could be put outside the #ifdefs, and without
    any nesting:

    va_list cp;
    va_copy(cp, ap);
    <something1>
    (len < 0) return len;
    <something2>
    *ret = malloc(len + 1);
    if(!*ret) return -1;
    return vsprintf(*ret, fmt, cp);

    where something1 is always 1-3 lines and something2 is zero or one line

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Craig A. Berry on Thu Jul 3 21:14:01 2025
    On 7/3/2025 9:00 PM, Craig A. Berry wrote:

    On 7/3/25 7:24 PM, Arne Vajhøj wrote:
    On 7/3/2025 6:31 PM, Craig A. Berry wrote:
    https://github.com/FreeTDS/freetds/blob/Branch-1_5/src/replacements/
    vasprintf.c

    does the following:

    #if HAVE_PATHS_H
    #include <paths.h>
    #endif /* HAVE_PATHS_H */
    . . .
    #ifndef _PATH_DEVNULL
    #define _PATH_DEVNULL "/dev/null"
    #endif

    FreeTDS is relying on autoconf to sort out whether paths.h is available
    and will only include it if HAVE_PATHS_H is defined in config.h, and
    thus only defines _PATH_DEVNULL if it's known not to exist. Obviously
    one should avoid making up one's own reserved identifiers and clobbering >>> documented and well-known names, but that's not really the same thing as >>> providing an implementation for one that's known to be missing. This
    fallback approach could of course be added to Arne's implementation if
    desired; to me it seems a lot cleaner than defining yet another macro,
    which would need to be defined in terms of _PATH_DEVNULL when it exists
    but otherwise not.

    The problem for VMS is that the workaround doesn't work.  Or at least it >>> didn't when I was first porting FreeTDS 20+ years ago because the CRTL
    at the time did not recognize '/dev/null' as a valid path.  I believe
    recentish CRTLs (maybe VMS 8.x and later?) do have special case code
    that translates it to the native null device.  But that wasn't
    available, so I added the following line to the template from which
    config.h is generated on VMS:

    #define _PATH_DEVNULL "_NLA0:"

    The main advantage of this approach is that it worked, and it did so
    with a one-line change to a file I was already maintaining. I didn't
    have to sprinkle '#ifdef __VMS' in files that already existed and were
    already working on other platforms. I didn't have to create a lot of new >>> files, the presence of which in the repository I couldn't test without
    autoconf/automake, which I didn't have access to at the time.  All of
    which meant upstream maintainers were far more likely to accept my
    changes.

    So you are saying that for the code to work on *nix then I need
    to keep those, because having it defined in config.h is a non-*nix
    thing and for *nix it only set HAVE_PATHS_H which requires the
    include in this file?

    I will update.

    I think preserving the HAVE_PATHS_H check is probably a good idea.  It
    makes sure you get the correct, system-specific version of
    _PATH_DEVNULL, if any.  And it preserves an existing approach to
    portability that you aren't worried about so you can focus on the
    vasprintf() guts.

    I have updated.

    I haven't done any testing of your code.  I do agree with Dan that
    reducing the repeated code would be a benefit.  You don't need the first
    #if because the file won't even be built if HAS_VASPRINTF is detected
    during configuration.

    I know. But just in case it did get called.

    Which means for the remaining 4 cases you've got
    this in all of them that could be put outside the #ifdefs, and without
    any nesting:

    va_list cp;
    va_copy(cp, ap);
    <something1>
    (len < 0) return len;
    <something2>
    *ret = malloc(len + 1);
    if(!*ret) return -1;
    return vsprintf(*ret, fmt, cp);

    where something1 is always 1-3 lines and something2 is zero or one line

    There are duplicated lines.

    But I still believe the one block of code per case is more readable
    than common code + some case specific code + common code + some
    case specific code + common code.

    Also imagine if someone was to add yet another approach. Now it
    is just put in the #if for the case and then write the block of
    code. If mixing it would all depend on the new approach - maybe
    it would fit into the existing structure, maybe it would require
    changing the structure if it does not fit.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From =?UTF-8?Q?Arne_Vajh=C3=B8j?=@21:1/5 to Lawrence D'Oliveiro on Thu Jul 3 21:21:16 2025
    On 7/3/2025 8:51 PM, Lawrence D'Oliveiro wrote:
    On Thu, 3 Jul 2025 14:15:33 -0400, Arne Vajhøj wrote:
    You refer to `_PATH_DEVNULL` but do not `#include <paths.h>`, as
    required by POSIX.

    This must support non *nix systems (as an example VMS !!) - config.h is
    expected to provide that with the rest.

    Also, there is no paths.h in POSIX.

    It is not in POSIX or SUS.

    But it is in a lot of POSIX/SUS OS.

    It doesn’t make any sense to have it
    in POSIX.

    Given that it is common on POSIX/SUS OS then someone could have
    thrown it into the standard. They just didn't.

    Arne

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Fri Jul 4 01:41:17 2025
    On Thu, 3 Jul 2025 21:21:16 -0400, Arne Vajhøj wrote:

    On 7/3/2025 8:51 PM, Lawrence D'Oliveiro wrote:

    It doesn’t make any sense to have it in POSIX.

    Given that it is common on POSIX/SUS OS then someone could have thrown
    it into the standard. They just didn't.

    There isn’t even a standard for what is supposed to go into it.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Dan Cross@21:1/5 to Craig A. Berry on Fri Jul 4 04:31:16 2025
    In article <10470be$dalk$1@dont-email.me>,
    Craig A. Berry <craigberry@nospam.mac.com> wrote:

    On 7/3/25 3:07 PM, Dan Cross wrote:
    In article <1046hc5$62th$2@dont-email.me>,
    Arne Vajhøj <arne@vajhoej.dk> wrote:
    On 7/3/2025 1:40 PM, Dan Cross wrote:

    You refer to `_PATH_DEVNULL` but do not `#include <paths.h>`, as
    required by POSIX.

    This must support non *nix systems (as an example
    VMS !!) - config.h is expected to provide that with the rest.

    If copyright dates are anything to judge by, `<paths.h>` has
    been a thing since 1989, but I was wrong in that it is not
    actually in POSIX: it is a BSD extension, though common.

    In general, it is good practice and good hygiene in C programs
    to `#include` the header files that are documented to define the
    symbols and types that you use in your program, instead of
    relying on transitive includes to populate things ambiantly via
    e.g. `config.h`.

    Regardless, in that case, you shouldn't use `_PATH_DEVNULL`.
    Note the leading `_`: that signifies a reserved identifier,
    which is not something you should be defining yourself. If you
    want to punt this to e.g. config.h, better would be to define a
    new name (say, `TDS_PATH_DEVNULL`) and use that.

    The code ya'll are talking about replacing:

    https://github.com/FreeTDS/freetds/blob/Branch-1_5/src/replacements/vasprintf.c

    does the following:

    #if HAVE_PATHS_H
    #include <paths.h>
    #endif /* HAVE_PATHS_H */
    . . .
    #ifndef _PATH_DEVNULL
    #define _PATH_DEVNULL "/dev/null"
    #endif

    FreeTDS is relying on autoconf to sort out whether paths.h is available
    and will only include it if HAVE_PATHS_H is defined in config.h, and
    thus only defines _PATH_DEVNULL if it's known not to exist. Obviously
    one should avoid making up one's own reserved identifiers and clobbering >documented and well-known names, but that's not really the same thing as >providing an implementation for one that's known to be missing. This
    fallback approach could of course be added to Arne's implementation if >desired; to me it seems a lot cleaner than defining yet another macro,
    which would need to be defined in terms of _PATH_DEVNULL when it exists
    but otherwise not.

    The problem for VMS is that the workaround doesn't work. Or at least it >didn't when I was first porting FreeTDS 20+ years ago because the CRTL
    at the time did not recognize '/dev/null' as a valid path. I believe >recentish CRTLs (maybe VMS 8.x and later?) do have special case code
    that translates it to the native null device. But that wasn't
    available, so I added the following line to the template from which
    config.h is generated on VMS:

    #define _PATH_DEVNULL "_NLA0:"

    The main advantage of this approach is that it worked, and it did so
    with a one-line change to a file I was already maintaining. I didn't
    have to sprinkle '#ifdef __VMS' in files that already existed and were >already working on other platforms. I didn't have to create a lot of new >files, the presence of which in the repository I couldn't test without >autoconf/automake, which I didn't have access to at the time. All of
    which meant upstream maintainers were far more likely to accept my changes.

    I'm sure I'll get a lecture about everything I did wrong, but at least
    now the context is out there for why it was reasonable for Arne to
    depend on prior art, i.e., the presence of _PATH_DEVNULL in config.h.

    Meh. If it works and it's not new code, then that's fine. But
    I don't see how having a "compat.h" header with something like:

    #ifndef TDS_PATH_DEVNULL
    #if defined(HAVE_PATHS_H)
    #include <paths.h>
    #define TDS_PATH_DEVNULL _PATH_DEVNULL
    #elif defined(VMS)
    #define TDS_PATH_DEVNULL "_NLA0:"
    #else
    #define TDS_PATH_DEVNULL "/dev/null"
    #endif
    #endif

    ...in it is either appreciably different or much cleaner.

    The point isn't to sprinkle VMS-isms all over, but rather, to
    centralize platform-specifics and provide an application
    specific portability layer. Given that `_PATH_DEVNULL` is not
    standard, but _is_ provided by system headers on a lot of
    systems, using it directly is already injecting a kind of system
    dependency. By isolating its use as above, you instead decouple
    that from the rest of the system

    Anyway, not meant to be a lecture, just an observation.

    - Dan C.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Dan Cross@21:1/5 to arne@vajhoej.dk on Fri Jul 4 05:06:36 2025
    In article <10476qu$edc3$1@dont-email.me>,
    Arne Vajhøj <arne@vajhoej.dk> wrote:
    On 7/3/2025 4:07 PM, Dan Cross wrote:
    In article <1046hc5$62th$2@dont-email.me>,
    Arne Vajhøj <arne@vajhoej.dk> wrote:
    On 7/3/2025 1:40 PM, Dan Cross wrote:
    As the main logic is the same in each case (find out the buffer
    size, allocate, and then assign and return), and the only real
    difference is in finding out the buffer length, your code would
    be more readable with some helper functions that you delegated
    to.

    As states in the comments then it was a design goal to have
    simple code blocks - no nested if's, no functions - for a given
    context just 1-10 lines of code.

    That's a bad design goal.

    This sort of organization leads to lots of duplication and tends
    to produce code that is overly verbose and fragile, where one
    section may be updated and the others skipped or missed. A
    little bit of duplication for the sake of comprehensibility is
    ok, but this is excessive.

    It is very easy to read this way.

    Just scan sequentially down until one find the relevant
    #if context and then read 1-10 lines sequentially.
    No jumping up and down.

    Well, it's subjective whether it's readable or not.

    But I didn't say that each stanza was difficult to read. I said
    that the repetition makes the code overly verbose (because so
    much boilerplate is duplicated) and fragile (because it's easy
    to miss a place that needs to be updated because of all the
    repetition).

    Those critics remain true.

    You refer to `_PATH_DEVNULL` but do not `#include <paths.h>`, as
    required by POSIX.

    This must support non *nix systems (as an example
    VMS !!) - config.h is expected to provide that with the rest.

    If copyright dates are anything to judge by, `<paths.h>` has
    been a thing since 1989, but I was wrong in that it is not
    actually in POSIX: it is a BSD extension, though common.

    This code should also work on non-*nix.

    Yup. That's why you shouldn't use `_PATH_DEVNULL` in the first
    place.

    It does not matter if all or most *nix has had it since 1989.

    I just think it's sad that the header hasn't been picked up by
    other systems in 35 years, but I think you missed the larger
    overall point.

    In general, it is good practice and good hygiene in C programs
    to `#include` the header files that are documented to define the
    symbols and types that you use in your program, instead of
    relying on transitive includes to populate things ambiantly via
    e.g. `config.h`.

    Regardless, in that case, you shouldn't use `_PATH_DEVNULL`.
    Note the leading `_`: that signifies a reserved identifier,
    which is not something you should be defining yourself. If you
    want to punt this to e.g. config.h, better would be to define a
    new name (say, `TDS_PATH_DEVNULL`) and use that.

    This has to fit into an existing project that has its
    way to setup things.

    Let's be clear: this entire interface is pretty trivial. There
    are no great mysteries here, and munging this code to be a bit
    cleaner won't have a major impact on the rest of the project.
    Arguments about "fitting into the existing project" are thus
    not terribly persuasive for this specific example.

    Note that the code that I posted made liberal use of `static` to
    hide helper functions and so on; moreover, there is no great
    burden added by using some new identifier that is not
    `_PATH_DEVNULL` (which is non-portable anyway).

    I also think that its way makes sense. It would not be good
    to start putting OS specific includes/defines into lots
    of C files. That is what the configure process is supposed to
    handle.

    I don't know why you brought this up; I don't think anyone
    suggested putting OS-specific things "into lots of C files."

    Actually, the OS-specific identifier is `_PATH_DEVNULL`, which
    is already in use. Since it became evident that the header file
    it comes from is not portable, my suggestion is to _not_ use
    that identifier, but instead introduce a new identifier that can
    be defined in a single place using OS-specific code (e.g., some
    kind of compatability header) and use that throughout. That
    may be defined in terms of `_PATH_DEVNULL` on systems where
    <paths.h> exists, but critically that would be isolated in a
    single place: no need to sprinkle OS-specific code all over the
    place.

    Re: header files, code files that make use of an identifier
    should `#include` the header file that identifier is documented
    to come from (where "documentation" is some relevant standard,
    a man page, or other system- or library-specific documentation).
    This generally makes builds more efficient, aids tools for
    static analysis, IDEs, linters, makes unit-level testing easier,
    and highlights problematic design areas (see below).

    You should _not_ have some "god" header that #include's a bunch
    of stuff, and is in turn included everywhere. This leads to
    presenting bloat to the compiler, increasing build times and
    wasting resources, polluting the global namespace unnecessarily,
    and leads to hacks going into headers that are a drag on
    maintenace going forward.

    Indeed, <paths.h> and using `_PATH_DEVNULL` is a good example of
    the downsides here: since one cannot portably include the header
    where it is most commonly defined in the places where that
    symbol is used, this strongly suggests that the symbol is being
    used in places where it shouldn't. This is an example of a
    non-portable, system-specific symbol being shoe-horned into use
    on a system where it's not otherwise defined.

    If you need to depend on specific _features_, then define
    macros for those (as autoconf does). Using system definitions
    as a proxy for what features exist is poor practice. Fun fact:
    I was the person who put that policy in place at Google, from
    which it became the mechanism for configuring options in Abseil
    (e.g., https://abseil.io/docs/cpp/platforms/feature_checks).

    - Dan C.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)