• Re: Variadic functions

    From Kaz Kylheku@21:1/5 to Malcolm McLean on Fri Jan 19 18:42:54 2024
    On 2024-01-19, Malcolm McLean <malcolm.arthur.mclean@gmail.com> wrote:
    Who has actually used a variadic function for any purpose other than implementing printf-style format strings?

    1. The fellows that designed some of the exec family syscalls in Unix.

    2. Catenation of multiple strings. This type of thing:

    cat_str(target, s1, s2, s3, ..., (char *) 0);

    3. Similarly, constructing a list of elements:

    make_list(e1, e2, ..., NOTVAL);

    4. Emulating a fixed, optional argument in certain situations.
    Example of this is the open function in POSIX.

    int open(const char *pathname, int flags);
    int open(const char *pathname, int flags, mode_t mode);

    The mode requirement is only required if the flags indicate
    creation, and this is implemented as:

    int open(const char *, int, ...);

    I have done a very similar thing a handful of times.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Malcolm McLean on Fri Jan 19 23:14:37 2024
    On Fri, 19 Jan 2024 17:59:18 +0000, Malcolm McLean wrote:

    Who has actually used a variadic function for any purpose other than implementing printf-style format strings?

    GEGL and BABL <https://gegl.org/> have lots of these. For example, from /usr/include/gegl-0.4/gegl-apply.h:

    /**
    * gegl_apply_op:
    * @buffer: the #GeglBuffer to apply onto
    * @operation_name: name of the operation to apply
    * @...: the settings for the operation. Zero or more key/value pairs,
    * ended terminated with NULL.
    *
    * Apply the operation to buffer, overwritting the contents of buffer.
    *
    */
    void gegl_apply_op (GeglBuffer *buffer,
    const gchar *operation_name,
    ...) G_GNUC_NULL_TERMINATED;

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to Malcolm McLean on Fri Jan 19 18:33:08 2024
    On Fri, 19 Jan 2024 17:59:18 +0000, Malcolm McLean wrote:

    Who has actually used a variadic function for any purpose other than implementing printf-style format strings?

    I've used the IMSL C library for several programs. Each function in that library took a fair number of optional argument groups. Each group
    started with an integer identifying which group it was. The types of the arguments varied from one option group to another. The IMSL functions
    had to use the option group ID to determine what types to pass to
    va_arg() when retrieving the values of those arguments. A special
    integer value was reserved to indicate that there were no more optional arguments.

    We had to deliver our code to many different users, some of whom had
    licenses for the IMSL library, and others that didn't. I created a
    pseudo-IMSL library, whose functions had the same name and interface as
    the the actual IMSL library. The actual functions were a very limited
    subset of the actual IMSL library, and had seriously constrained
    functionality. For instance, we only needed cubic splines, so my
    replacement for the IMSL spline routine treated requests for splines of
    all other orders as errors. I wrote them based upon publicly available algorithms and black-box testing to determine how the real IMSL
    functions dealt with various error conditions and corner cases. I was
    surprised to find that they failed to catch a number of error
    conditions, but I wrote bug-compatible substitutes that failed in
    exactly the same way. I never looked at the actual IMSL code. The
    comments inside those functions clearly credited both IMSL and the
    public sources I used for those algorithms.

    I informed my superiors of what I was doing, and asked them to check
    with the company's lawyers to find out whether I was violating any
    intellectual property laws. They never got back to me. We delivered my
    minimum IMSL clone library to those of our users who didn't have IMSL
    licenses.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to Malcolm McLean on Sat Jan 20 11:15:23 2024
    On 19.01.2024 18:59, Malcolm McLean wrote:
    Who has actually used a variadic function for any purpose other than implementing printf-style format strings?

    In "own" projects only rarely. One was for debugging with nested macros
    that finally lead to, yes, printf arguments. (That was decades ago, so
    memories are faint about other applications.) Later in C++ I used other
    code patterns for such purposes.

    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Richard Harnden@21:1/5 to Malcolm McLean on Sat Jan 20 18:19:51 2024
    On 20/01/2024 16:58, Malcolm McLean wrote:
    On 20/01/2024 16:52, Spiros Bousbouras wrote:
    On Sat, 20 Jan 2024 11:19:57 +0000
    Malcolm McLean <malcolm.arthur.mclean@gmail.com> wrote:
    Yes. It seems there are a few libraries that also use variadic
    arguments. I tend not to do that sort of programming and I'm not very
    familar with them.
    But no-one has yet come up with an occasion where they implemented a
    variadic function by choice, for code which they controlled, except for
    printf style formatting. Either a hobby project or work code for which
    they had ownership.

    I asked a similar question on this group many years ago. One example
    given
    was a function for concatenating strings which would accept as
    arguments a
    variable number of strings.

    I've sometimes written a "cat" function. But I always write it to take
    two strings. However I don't do much text processing.


    Did you not see Kaz's reply? This was his 2nd example.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Malcolm McLean on Sat Jan 20 21:37:38 2024
    On Sat, 20 Jan 2024 11:19:57 +0000, Malcolm McLean wrote:

    But no-one has yet come up with an occasion where they implemented a
    variadic function by choice, for code which they controlled ...

    But the people who created these libraries we mentioned certainly did.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Malcolm McLean on Sat Jan 20 23:02:54 2024
    On 20/01/2024 22:37, Malcolm McLean wrote:
    On 20/01/2024 21:37, Lawrence D'Oliveiro wrote:
    On Sat, 20 Jan 2024 11:19:57 +0000, Malcolm McLean wrote:

    But no-one has yet come up with an occasion where they implemented a
    variadic function by choice, for code which they controlled ...

    But the people who created these libraries we mentioned certainly did.

    I was wrong. As was pointed out eslewhere. Kaz gave some examples. But
    his first was of usage.

    Of course someone must have taken the decision that the call to evoke an external process would take a variable list of arguments rather than a
    single string. However In find that, of thousands of functions in my
    code base, not a single one is variadic except a few interfaces to
    vsprintf.


    Variadic functions were likely added to implement *printf functions,
    since the language didn't want to make special provision just for those.

    Then a number of people decided to make use of the feature, although
    less often than you might expect.

    But it is not a desirable feature for general use as it is not type-safe.

    The other thing is, calling a variadic function is a lot easier than
    writing the body of one. That will likely put many off.

    I wouldn't be able to do it without looking up some examples.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Malcolm McLean on Sat Jan 20 22:52:29 2024
    On Sat, 20 Jan 2024 22:37:48 +0000, Malcolm McLean wrote:

    Of course someone must have taken the decision that the call to evoke an external process would take a variable list of arguments rather than a
    single string.

    And it can play merry hell with typesafeness. Though I was able to come up
    with typesafe Python wrappers, for example, around the variadic functions
    in those libraries I mentioned earlier.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Malcolm McLean on Sat Jan 20 16:30:57 2024
    Malcolm McLean <malcolm.arthur.mclean@gmail.com> writes:

    Who has actually used a variadic function for any purpose other than implementing printf-style format strings?

    May I inquire as to why you are motivated to ask the question?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Malcolm McLean on Sun Jan 21 05:16:49 2024
    On Sat, 20 Jan 2024 22:37:48 +0000, Malcolm McLean wrote:

    Of course someone must have taken the decision that the call to evoke an external process would take a variable list of arguments rather than a
    single string. However In find that, of thousands of functions in my
    code base, not a single one is variadic except a few interfaces to
    vsprintf.

    I have the urge to implement one, purely to provide a counterexample. As
    an excuse, I will use it to demonstrate a technique for typesafely calling
    such a function from Python code via ctypes.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Lawrence D'Oliveiro on Sun Jan 21 06:05:01 2024
    On 2024-01-21, Lawrence D'Oliveiro <ldo@nz.invalid> wrote:
    On Sat, 20 Jan 2024 22:37:48 +0000, Malcolm McLean wrote:

    Of course someone must have taken the decision that the call to evoke an
    external process would take a variable list of arguments rather than a
    single string. However In find that, of thousands of functions in my
    code base, not a single one is variadic except a few interfaces to
    vsprintf.

    I have the urge to implement one, purely to provide a counterexample. As
    an excuse, I will use it to demonstrate a technique for typesafely calling such a function from Python code via ctypes.

    You're going to implement variadic functions in Malcom's code base,
    just to create a counterexample?

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Malcolm McLean on Sun Jan 21 07:48:44 2024
    On Sat, 20 Jan 2024 11:19:57 +0000, Malcolm McLean wrote:

    But no-one has yet come up with an occasion where they implemented a
    variadic function by choice, for code which they controlled ...

    OK, I have done one. Just a simple one, to demonstrate how to make
    typesafe calls to it via a Python wrapper. It’s part of my “Python Topics Notebooks” collection <https://gitlab.com/ldo/python_topics_notebooks/>.
    The C code (stack_calc.[ch]) is a very basic RPN calculator, which takes a varargs list of operators (integer) and operands (real), and returns the
    final calculated result.

    The notebook that makes use of it is called “ctypes Tip Extra -- Calling A Variadic Function”.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Lawrence D'Oliveiro on Sun Jan 21 13:27:30 2024
    On 21/01/2024 07:48, Lawrence D'Oliveiro wrote:
    On Sat, 20 Jan 2024 11:19:57 +0000, Malcolm McLean wrote:

    But no-one has yet come up with an occasion where they implemented a
    variadic function by choice, for code which they controlled ...

    OK, I have done one. Just a simple one, to demonstrate how to make
    typesafe calls to it via a Python wrapper. It’s part of my “Python Topics Notebooks” collection <https://gitlab.com/ldo/python_topics_notebooks/>. The C code (stack_calc.[ch]) is a very basic RPN calculator, which takes a varargs list of operators (integer) and operands (real), and returns the final calculated result.

    The notebook that makes use of it is called “ctypes Tip Extra -- Calling A Variadic Function”.

    "Calling such a variadic function from C itself can be tricky, because
    there is very limited type checking that the C compiler can do on such
    calls."

    This is exactly the problem. In your example, you go through a
    verification process before calling the function, which is called
    indirectly.

    That is, the user-code in Python doesn't directly call 'stack_calc'.
    This means that there is no real need for the C function to be variadic.

    It could take a single pointer to any arbitrary C data structure that
    can represent your sample expression. Or maybe two pointers to parallel
    arrays. Or ...

    So it still leaves C variadic functions as a solution looking for a
    problem, a solution which is hard to make type-safe.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Malcolm McLean on Sun Jan 21 17:24:30 2024
    On 21/01/2024 03:44, Malcolm McLean wrote:
    On 20/01/2024 23:02, bart wrote:
    On 20/01/2024 22:37, Malcolm McLean wrote:
    On 20/01/2024 21:37, Lawrence D'Oliveiro wrote:
    On Sat, 20 Jan 2024 11:19:57 +0000, Malcolm McLean wrote:

    But no-one has yet come up with an occasion where they implemented a >>>>> variadic function by choice, for code which they controlled ...

    But the people who created these libraries we mentioned certainly did.
    ;
    I was wrong. As was pointed out eslewhere. Kaz gave some examples.
    But his first was of usage.

    Of course someone must have taken the decision that the call to evoke
    an external process would take a variable list of arguments rather
    than a single string. However In find that, of thousands of functions
    in my code base, not a single one is variadic except a few interfaces
    to vsprintf.


    Variadic functions were likely added to implement *printf functions,
    since the language didn't want to make special provision just for those.

    Then a number of people decided to make use of the feature, although
    less often than you might expect.

    But it is not a desirable feature for general use as it is not type-safe.

    The other thing is, calling a variadic function is a lot easier than
    writing the body of one. That will likely put many off.

    I wouldn't be able to do it without looking up some examples.

    I have occasionally passed the wrong type to printf. Usually a long
    integer to %d. But I can't recall an occasion where the problem wasn't quickly fixed. Xcode gives a warning for mismatched format string
    parameters, but of course it can't do that for roll your own.

    Of course you can.

    XCode uses clang, IIUIC, and both gcc and clang have extensions
    (attributes) for checking printf and scanf style strings and the
    variadic parameters. They only work with compile-time known format
    strings, understandably enough, but are fine for making your own checked functions for logging, debug prints, or whatever.

    That doesn't help for other types of variadic functions, however, and
    you they are never as type-safe as functions with full non-variadic
    prototypes.

    The other thing is passing a raw pointer to %p instead of casting to
    void *. Technically this is an error, but %p is only really useful for temporary debug code, so it's just pointless typing to out the cast in.

    Variardic functions are implemented with macros and it's a bit hard to
    work out what is going on. But it's not that hard. I don't think it's a
    good reason for avoiding them. On the other hand, I implemented a C++ variable length template expansion to produce a "simple JSON" which is
    in effect a varidaic function with type information. But whilst I
    implemented it myself, I can't remember exactly how it is supposed to
    work or what the syntax means.


    Variadic templates in C++ are typesafe, which is a big improvement over C.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Lawrence D'Oliveiro on Sun Jan 21 21:43:13 2024
    On 21/01/2024 21:29, Lawrence D'Oliveiro wrote:
    On Sun, 21 Jan 2024 13:27:30 +0000, bart wrote:

    That is, the user-code in Python doesn't directly call 'stack_calc'.
    This means that there is no real need for the C function to be variadic.

    It could take a single pointer to any arbitrary C data structure that
    can represent your sample expression. Or maybe two pointers to parallel
    arrays. Or ...

    ... and be just as type-unsafe as the variadic option.

    No, it wouldn't.

    If you have that single pointer as type T*, then you can't for example
    pass, when called from C, an int, or float, or U* pointer, or struct, as happens with variadic.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to bart on Sun Jan 21 21:29:48 2024
    On Sun, 21 Jan 2024 13:27:30 +0000, bart wrote:

    That is, the user-code in Python doesn't directly call 'stack_calc'.
    This means that there is no real need for the C function to be variadic.

    It could take a single pointer to any arbitrary C data structure that
    can represent your sample expression. Or maybe two pointers to parallel arrays. Or ...

    ... and be just as type-unsafe as the variadic option.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to bart on Sun Jan 21 23:51:55 2024
    On Sun, 21 Jan 2024 21:43:13 +0000, bart wrote:

    On 21/01/2024 21:29, Lawrence D'Oliveiro wrote:
    On Sun, 21 Jan 2024 13:27:30 +0000, bart wrote:

    That is, the user-code in Python doesn't directly call 'stack_calc'.
    This means that there is no real need for the C function to be
    variadic.

    It could take a single pointer to any arbitrary C data structure that
    can represent your sample expression. Or maybe two pointers to
    parallel
    arrays. Or ...

    ... and be just as type-unsafe as the variadic option.

    No, it wouldn't.

    Go on, then. Show us a typesafe alternative to printf, using your idea.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Lawrence D'Oliveiro on Mon Jan 22 00:09:08 2024
    On 21/01/2024 23:51, Lawrence D'Oliveiro wrote:
    On Sun, 21 Jan 2024 21:43:13 +0000, bart wrote:

    On 21/01/2024 21:29, Lawrence D'Oliveiro wrote:
    On Sun, 21 Jan 2024 13:27:30 +0000, bart wrote:

    That is, the user-code in Python doesn't directly call 'stack_calc'.
    This means that there is no real need for the C function to be
    variadic.

    It could take a single pointer to any arbitrary C data structure that
    can represent your sample expression. Or maybe two pointers to
    parallel
    arrays. Or ...

    ... and be just as type-unsafe as the variadic option.

    No, it wouldn't.

    Go on, then. Show us a typesafe alternative to printf, using your idea.

    I said your example didn't need the C function to be variadic. The point
    of variadic is to be legally able to write, in C:

    f(a) or f(a,b,c,d)

    But your C function wasn't called from C, it isn't called from Python
    user code. From inside your special function, I think it's called as 'func(*all_args)'.

    It could easily be made to work with a non-variadic C function. But my
    call to f() above either has to use a variadic function, or 'f' needs to
    be declared with no parameter list:

    void f(T, ...); // variadic
    void f(); // unchecked arguments

    Both are unsafe.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to bart on Mon Jan 22 00:29:27 2024
    On Mon, 22 Jan 2024 00:09:08 +0000, bart wrote:

    On 21/01/2024 23:51, Lawrence D'Oliveiro wrote:
    On Sun, 21 Jan 2024 21:43:13 +0000, bart wrote:

    On 21/01/2024 21:29, Lawrence D'Oliveiro wrote:
    On Sun, 21 Jan 2024 13:27:30 +0000, bart wrote:

    That is, the user-code in Python doesn't directly call 'stack_calc'. >>>>> This means that there is no real need for the C function to be
    variadic.

    It could take a single pointer to any arbitrary C data structure
    that can represent your sample expression. Or maybe two pointers to
    parallel
    arrays. Or ...

    ... and be just as type-unsafe as the variadic option.

    No, it wouldn't.

    Go on, then. Show us a typesafe alternative to printf, using your idea.

    I said your example didn't need the C function to be variadic.

    You also denied that your alternative would be less type-unsafe.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Malcolm McLean on Mon Jan 22 08:58:51 2024
    On 21/01/2024 22:54, Malcolm McLean wrote:
    On 21/01/2024 16:24, David Brown wrote:
    On 21/01/2024 03:44, Malcolm McLean wrote:

    Variardic functions are implemented with macros and it's a bit hard
    to work out what is going on. But it's not that hard. I don't think
    it's a good reason for avoiding them. On the other hand, I
    implemented a C++ variable length template expansion to produce a
    "simple JSON" which is in effect a varidaic function with type
    information. But whilst I implemented it myself, I can't remember
    exactly how it is supposed to work or what the syntax means.


    Variadic templates in C++ are typesafe, which is a big improvement
    over C.


    A small improvement.

    If you have type safety, it can be easier to know how to call the
    function, because the system can help you by suggesting variables of the right type and rejecting variables of the wrong type. And because the
    type is often documentation about what the parameter means.


    Type-safety means the compiler will reject arguments of the wrong type
    for the function - or, alternatively, select (or generate) the correct
    function for the type of the arguments. Either way, the function and
    arguments match in type. Documentation has nothing to do with it.

    Then of course you can always create a bug by passing a varibale of the
    wrong type.

    No, you can't - that's what type safety means. (Sure, you can - at
    least in C and C++ - go out of your way with cast pointers, low-level
    memory access, etc., to break type safety. No language can entirely
    prevent the user writing intentionally dangerous code.)


    But it's not a big improvement because, if you call a function at all,
    you have to know what parameters it takes and what they mean anyway.

    Yes, obviously. Type-safety means the language is safe in regard to
    types. It doesn't stop other bugs.

    And
    whilst it does eliminate a bug, it's unlikely to be a dangerous bug, it
    will usually be manifested very early and likely caught.

    Type-safety eliminates a huge class of bugs that could otherwise cause
    all sorts of problems - some big, some small, some easily spotted and
    some much more subtle. And it can be used as the basis for a lot more
    safety features and checks if you want.

    So not the type
    of thing that is likely to much effect on delivery time or quality of
    the final product.

    That is contrary to the experience of most high quality software
    developers. There is a reason why few modern languages are weakly
    typed. Strong typing does not stop all problems, nor is it necessary
    for writing correct software, and using typing for safety can involve
    writing more verbose code than otherwise necessary. But it is a major
    tool towards writing good code, and having checks on the code quality.

    However of course that won't always be the case,
    especially if a long integer with all high bits zero happens to have the
    same observable effect as the correct short integer or similar.

    So there's a small advantage, but not a gamechanger.

    My simpleJSON is variadic C++. But of course if you pass
    "Nemployees",10.5, it will just create a JSON structure with a real
    field value 10.5.


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Malcolm McLean on Mon Jan 22 13:58:20 2024
    On 22/01/2024 13:46, Malcolm McLean wrote:
    On 22/01/2024 07:58, David Brown wrote:
    On 21/01/2024 22:54, Malcolm McLean wrote:

    That is contrary to the experience of most high quality software
    developers.  There is a reason why few modern languages are weakly
    typed.  Strong typing does not stop all problems, nor is it necessary
    for writing correct software, and using typing for safety can involve
    writing more verbose code than otherwise necessary.  But it is a major
    tool towards writing good code, and having checks on the code quality.

    No, it's because there is a benefit, and it is easy to write a computer program which checks for type. So there is a point in doing it. Then
    there's often a perception that because something can be done by the computer, it is important and beneficial, because the people who write
    the development system must know what they are doing, and if the
    computer says so, it must be correct.

    Computer projects fail because the complexity of the interactions
    between the sub systems exceeds the capacity of the developers to
    understand them. Overwhelmingly. Not because developers sometimes pass variables of the wrong type to functions.

    Software projects fail for countless different reasons. For many of the possible reasons, computers can't help much. In cases where computers (compilers, static analysis, run-time checkers, etc.) /can/ help, it
    makes sense to use those features - it's more efficient, and usually
    more accurate.

    Strong typing is one of those cases - along with many other higher-level language features.

    (And no, it's not easy to make a language and tools that provide good
    checking, a comfortable and productive language, and quality tools with efficient compilation. It is, I believe, a good deal easier to make
    languages and tools with very little type checking capabilities.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Malcolm McLean on Mon Jan 22 19:44:20 2024
    On 2024-01-22, Malcolm McLean <malcolm.arthur.mclean@gmail.com> wrote:
    Computer projects fail because the complexity of the interactions
    between the sub systems exceeds the capacity of the developers to
    understand them. Overwhelmingly. Not because developers sometimes pass variables of the wrong type to functions.

    If passing wrong types to functions was not caught, either at compile
    time, or at run time (as in dynamic languages) but simply resulted
    in an object of one type being misused as another, then that would be
    a huge source of bugs.

    You can't tell whether wrong types are a source of bugs without working
    in a typeless language.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Blue-Maned_Hawk@21:1/5 to Malcolm McLean on Mon Jan 22 23:27:31 2024
    Malcolm McLean wrote:

    Who has actually used a variadic function for any purpose other than implementing printf-style format strings?

    I'll admit to not having written many of them. But i _have_ written a lot
    of variadic macros, though this has mostly been so as to prevent problems
    with commas in arguments.



    --
    Blue-Maned_Hawk│shortens to Hawk│/ blu.mɛin.dʰak/ │he/him/his/himself/Mr.
    blue-maned_hawk.srht.site

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Blue-Maned_Hawk@21:1/5 to Spiros Bousbouras on Mon Jan 22 23:25:22 2024
    Spiros Bousbouras wrote:

    On Sat, 20 Jan 2024 11:19:57 +0000 Malcolm McLean <malcolm.arthur.mclean@gmail.com> wrote:
    But no-one has yet come up with an occasion where they implemented a
    variadic function by choice, for code which they controlled, except for
    printf style formatting. Either a hobby project or work code for which
    they had ownership.

    I asked a similar question on this group many years ago. One example
    given was a function for concatenating strings which would accept as arguments a variable number of strings.

    I've only ever done chained strcat calls, e.g.
    strcat(strcat(strcat(something, "blah"), "whatever"), "nonsense").


    --
    Blue-Maned_Hawk│shortens to Hawk│/ blu.mɛin.dʰak/ │he/him/his/himself/Mr.
    blue-maned_hawk.srht.site
    “Hotel California” is completely literal and the Eagles are still trapped there.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Malcolm McLean on Tue Jan 23 00:01:39 2024
    On 2024-01-22, Malcolm McLean <malcolm.arthur.mclean@gmail.com> wrote:
    On 22/01/2024 19:44, Kaz Kylheku wrote:
    On 2024-01-22, Malcolm McLean <malcolm.arthur.mclean@gmail.com> wrote:
    Computer projects fail because the complexity of the interactions
    between the sub systems exceeds the capacity of the developers to
    understand them. Overwhelmingly. Not because developers sometimes pass
    variables of the wrong type to functions.

    If passing wrong types to functions was not caught, either at compile
    time, or at run time (as in dynamic languages) but simply resulted
    in an object of one type being misused as another, then that would be
    a huge source of bugs.

    You can't tell whether wrong types are a source of bugs without working
    in a typeless language.

    You can use variadic functions in C.

    When you use your "cat" function, for example, do you find it a problem
    that is you pass a variable that isn't a character pointer, the system
    will reinterpret the bits as though they were a character pointer?

    Yes?

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Mon Jan 22 23:36:26 2024
    On Mon, 22 Jan 2024 23:25:22 -0000 (UTC), Blue-Maned_Hawk wrote:

    I've only ever done chained strcat calls, e.g. strcat(strcat(strcat(something, "blah"), "whatever"), "nonsense").

    You’ve got to be kidding.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Malcolm McLean on Tue Jan 23 09:07:07 2024
    On 22/01/2024 23:25, Malcolm McLean wrote:
    On 22/01/2024 12:58, David Brown wrote:
    On 22/01/2024 13:46, Malcolm McLean wrote:
    On 22/01/2024 07:58, David Brown wrote:
    On 21/01/2024 22:54, Malcolm McLean wrote:

    That is contrary to the experience of most high quality software
    developers.  There is a reason why few modern languages are weakly
    typed.  Strong typing does not stop all problems, nor is it
    necessary for writing correct software, and using typing for safety
    can involve writing more verbose code than otherwise necessary.  But
    it is a major tool towards writing good code, and having checks on
    the code quality.

    No, it's because there is a benefit, and it is easy to write a
    computer program which checks for type. So there is a point in doing
    it. Then there's often a perception that because something can be
    done by the computer, it is important and beneficial, because the
    people who write the development system must know what they are
    doing, and if the computer says so, it must be correct.

    Computer projects fail because the complexity of the interactions
    between the sub systems exceeds the capacity of the developers to
    understand them. Overwhelmingly. Not because developers sometimes
    pass variables of the wrong type to functions.

    Software projects fail for countless different reasons.  For many of
    the possible reasons, computers can't help much.  In cases where
    computers (compilers, static analysis, run-time checkers, etc.) /can/
    help, it makes sense to use those features - it's more efficient, and
    usually more accurate.

    Strong typing is one of those cases - along with many other
    higher-level language features.

    (And no, it's not easy to make a language and tools that provide good
    checking, a comfortable and productive language, and quality tools
    with efficient compilation.  It is, I believe, a good deal easier to
    make languages and tools with very little type checking capabilities.)



    Have you tried to write a compiler or intepreter?


    No (except for tiny DSL's).

    But I also have not tried to fly a plane, yet I know that some types of
    plane are easier to fly than others.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Malcolm McLean on Tue Jan 23 10:52:24 2024
    On 23/01/2024 09:27, Malcolm McLean wrote:
    On 23/01/2024 08:07, David Brown wrote:
    On 22/01/2024 23:25, Malcolm McLean wrote:
    On 22/01/2024 12:58, David Brown wrote:
    On 22/01/2024 13:46, Malcolm McLean wrote:
    On 22/01/2024 07:58, David Brown wrote:
    On 21/01/2024 22:54, Malcolm McLean wrote:

    That is contrary to the experience of most high quality software
    developers.  There is a reason why few modern languages are weakly >>>>>> typed.  Strong typing does not stop all problems, nor is it
    necessary for writing correct software, and using typing for
    safety can involve writing more verbose code than otherwise
    necessary. But it is a major tool towards writing good code, and
    having checks on the code quality.

    No, it's because there is a benefit, and it is easy to write a
    computer program which checks for type. So there is a point in
    doing it. Then there's often a perception that because something
    can be done by the computer, it is important and beneficial,
    because the people who write the development system must know what
    they are doing, and if the computer says so, it must be correct.

    Computer projects fail because the complexity of the interactions
    between the sub systems exceeds the capacity of the developers to
    understand them. Overwhelmingly. Not because developers sometimes
    pass variables of the wrong type to functions.

    Software projects fail for countless different reasons.  For many of
    the possible reasons, computers can't help much.  In cases where
    computers (compilers, static analysis, run-time checkers, etc.)
    /can/ help, it makes sense to use those features - it's more
    efficient, and usually more accurate.

    Strong typing is one of those cases - along with many other
    higher-level language features.

    (And no, it's not easy to make a language and tools that provide
    good checking, a comfortable and productive language, and quality
    tools with efficient compilation.  It is, I believe, a good deal
    easier to make languages and tools with very little type checking
    capabilities.)



    Have you tried to write a compiler or intepreter?


    No (except for tiny DSL's).

    But I also have not tried to fly a plane, yet I know that some types
    of plane are easier to fly than others.


    Bascially it's just a grammar rule, and you'll already have a flexible grammar system because you need it for the rest of the compiler.
    Technically it will be a "constraint" on the grammar as most grammars
    can't express "function_call = function_name open_parentheses variable_of_a_type_that_matches close_parentheses".


    That is not the difference between strong typing and weak typing, nor is
    it all you need for a language to support good typing. But perhaps we
    have lost track of exactly what we are talking about here, and are
    thinking about different things, or at least different aspects of
    typing. Let's leave it for now.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Malcolm McLean on Tue Jan 23 10:49:27 2024
    On 23/01/2024 09:30, Malcolm McLean wrote:
    On 22/01/2024 23:36, Lawrence D'Oliveiro wrote:
    On Mon, 22 Jan 2024 23:25:22 -0000 (UTC), Blue-Maned_Hawk wrote:

    I've only ever done chained strcat calls, e.g.
    strcat(strcat(strcat(something, "blah"), "whatever"), "nonsense").

    You’ve got to be kidding.

    That was why strcat retutns a pointer. It was how it was meant to be
    used. But it never caught on.


    I don't think that is a reasonable way to put it. Rather, say that
    strcat and strcpy return their first argument because that was what the
    library specifiers felt would be the most useful return value. One of
    the possibilities it allows is chaining like this.

    One possible reason why people don't use it - perhaps preferring to use snprintf or non-standard functions for multiple concatenation - is that
    a naïve implementation is unnecessarily inefficient. For such uses, it
    would be far better if strcat and strcpy had returned a pointer to one
    past the end of the resulting string, rather than duplicating the
    initial pointer. (For other uses, maybe it is more convenient to have
    the returns the way strcat and strcpy do.)

    Some optimising compilers will do a better job, and will optimise such
    nested strcat() calls for efficiency, making it a perfectly reasonable construction if you are using something like gcc (or if efficiency is
    not an issue).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Malcolm McLean on Tue Jan 23 15:27:25 2024
    Malcolm McLean <malcolm.arthur.mclean@gmail.com> writes:
    On 22/01/2024 23:36, Lawrence D'Oliveiro wrote:
    On Mon, 22 Jan 2024 23:25:22 -0000 (UTC), Blue-Maned_Hawk wrote:

    I've only ever done chained strcat calls, e.g.
    strcat(strcat(strcat(something, "blah"), "whatever"), "nonsense").

    You’ve got to be kidding.

    That was why strcat retutns a pointer. It was how it was meant to be
    used. But it never caught on.

    For rather good reason. Too easy to overrun the size of the buffer.

    Huge security hole.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Scott Lurndal on Tue Jan 23 21:48:32 2024
    On Tue, 23 Jan 2024 15:27:25 GMT, Scott Lurndal wrote:

    Huge security hole.

    That was the point I was trying to make, but it seemed to go over
    somebody’s head ...

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Blue-Maned_Hawk@21:1/5 to Lawrence D'Oliveiro on Tue Jan 23 22:25:46 2024
    Lawrence D'Oliveiro wrote:

    On Mon, 22 Jan 2024 23:25:22 -0000 (UTC), Blue-Maned_Hawk wrote:

    I've only ever done chained strcat calls, e.g.
    strcat(strcat(strcat(something, "blah"), "whatever"), "nonsense").

    You’ve got to be kidding.

    I am not kidding. Here's an actual fragment of code where i used it to
    format an array of backtrace symbols to be on one line each:

    for (int i = 0; i < depth; ++i)
    strcat(strcat(strcat(btstr, "\t"), btsyms[i]), "\n");



    --
    Blue-Maned_Hawk│shortens to Hawk│/ blu.mɛin.dʰak/ │he/him/his/himself/Mr.
    blue-maned_hawk.srht.site
    Sip it, don't chug it!

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Blue-Maned_Hawk@21:1/5 to bart on Tue Jan 23 22:17:12 2024
    bart wrote:

    But it is not a desirable feature for general use as it is not
    type-safe.

    Data types are a social construct.



    --
    Blue-Maned_Hawk│shortens to Hawk│/ blu.mɛin.dʰak/ │he/him/his/himself/Mr.
    blue-maned_hawk.srht.site
    Very fred kindley!

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Blue-Maned_Hawk@21:1/5 to Scott Lurndal on Tue Jan 23 22:28:03 2024
    Scott Lurndal wrote:

    Malcolm McLean <malcolm.arthur.mclean@gmail.com> writes:
    On 22/01/2024 23:36, Lawrence D'Oliveiro wrote:
    On Mon, 22 Jan 2024 23:25:22 -0000 (UTC), Blue-Maned_Hawk wrote:

    I've only ever done chained strcat calls, e.g.
    strcat(strcat(strcat(something, "blah"), "whatever"), "nonsense").

    You’ve got to be kidding.

    That was why strcat retutns a pointer. It was how it was meant to be
    used. But it never caught on.

    For rather good reason. Too easy to overrun the size of the buffer.

    Huge security hole.

    strncat



    --
    Blue-Maned_Hawk│shortens to Hawk│/ blu.mɛin.dʰak/ │he/him/his/himself/Mr.
    blue-maned_hawk.srht.site
    In this case, some interesting physical effects would destroy it.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to All on Tue Jan 23 23:31:57 2024
    On Tue, 23 Jan 2024 22:28:03 -0000 (UTC), Blue-Maned_Hawk wrote:

    strncat

    If you can figure out how to use it properly.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Lawrence D'Oliveiro on Wed Jan 24 09:40:34 2024
    On 23/01/2024 22:48, Lawrence D'Oliveiro wrote:
    On Tue, 23 Jan 2024 15:27:25 GMT, Scott Lurndal wrote:

    Huge security hole.

    That was the point I was trying to make, but it seemed to go over somebody’s head ...

    If that "somebody" was me, then the it did not go over my head - it went
    far to the side of it.

    Chained strcat has no more and no less of a risk of buffer overflows
    than any other way of combining strings without checking their size and ensuring you have an appropriately sized destination buffer. There is
    no more and no less danger in doing "strcat(strcat(strcat(..." than in
    doing "strcat" at all - either you have ensured your buffers and sizes
    are safe and that all your pointers really are terminated strings, or
    you have a risk of overrun.

    And a risk of overrun is not in any way synonymous with "huge security
    hole". It is a risk of failure. How big that risk is depends on the likelihood of the strings being too big for the target buffer, which is
    totally impossible to estimate without real code - "huge" is no more appropriate than "tiny". Similarly, the consequences of an overrun are impossible to judge without much more information - thus "security hole"
    is no more appropriate than guessing any other kind of failure. "Huge
    security hole" and "tiny risk of pixel colour glitches" are equally
    valid, due to a total lack of knowledge of the rest of the program.

    So when someone has used "strcat" in a Usenet post, I would assume that real-world code would take appropriate care to be sure the arguments
    point to valid strings, and the destinations have enough space. It's
    not unlikely that the real code actually uses "strncat" to reduce the
    risk of failures.

    And I assumed that you, too, know all this and considered it a side
    issue, orthogonal to the actual interesting point about nested strcat
    calls. That is, concatenating strings with nested strcat calls scales
    as O(n²), while alternatives such as "sprintf" or nested "stpcpy" (POSIX
    but not standard C) scales O(n).

    But you might not have known that gcc will convert a set of nested
    "strcat" calls into a call to "strlen" followed by calls to "stpcpy".

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Lawrence D'Oliveiro on Wed Jan 24 09:52:59 2024
    On 24/01/2024 00:31, Lawrence D'Oliveiro wrote:
    On Tue, 23 Jan 2024 22:28:03 -0000 (UTC), Blue-Maned_Hawk wrote:

    strncat

    If you can figure out how to use it properly.

    strncat is nasty, as "n" is the maximum number of characters to copy,
    not the size of the destination buffer. strncpy is nasty as it wastes
    effort needlessly writing null characters once the string is copied, and
    does not guarantee a terminating null.

    The common standard C str* functions are, IMHO, extraordinarily poorly specified, and could easily have been very much easier to use correctly
    and efficiently.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to David Brown on Wed Jan 24 13:53:54 2024
    On 24.01.2024 09:40, David Brown wrote:

    [...] That is, concatenating strings with nested strcat calls scales
    as O(n²), while alternatives such as "sprintf" or nested "stpcpy" (POSIX
    but not standard C) scales O(n).

    First I mistook "stpcpy" for a typo, but hey, it's there. - Nice, and
    good to know it's there. Thanks.

    But you might not have known that gcc will convert a set of nested
    "strcat" calls into a call to "strlen" followed by calls to "stpcpy".

    Interesting.

    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Janis Papanagnou on Wed Jan 24 14:56:46 2024
    On 24/01/2024 13:53, Janis Papanagnou wrote:
    On 24.01.2024 09:40, David Brown wrote:

    [...] That is, concatenating strings with nested strcat calls scales
    as O(n²), while alternatives such as "sprintf" or nested "stpcpy" (POSIX
    but not standard C) scales O(n).

    First I mistook "stpcpy" for a typo, but hey, it's there. - Nice, and
    good to know it's there. Thanks.

    Note that it exists in POSIX (and therefore in most C libraries), but it
    is not standard C.


    But you might not have known that gcc will convert a set of nested
    "strcat" calls into a call to "strlen" followed by calls to "stpcpy".

    Interesting.


    gcc (and clang, and no doubt other compilers) can optimise a number of
    standard library calls. This is, IME, very useful.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to David Brown on Wed Jan 24 19:50:27 2024
    On 2024-01-24, David Brown <david.brown@hesbynett.no> wrote:
    On 24/01/2024 00:31, Lawrence D'Oliveiro wrote:
    On Tue, 23 Jan 2024 22:28:03 -0000 (UTC), Blue-Maned_Hawk wrote:

    strncat

    If you can figure out how to use it properly.

    strncat is nasty, as "n" is the maximum number of characters to copy,
    not the size of the destination buffer. strncpy is nasty as it wastes
    effort needlessly writing null characters once the string is copied, and
    does not guarantee a terminating null.

    I always assumed strncpy was intended for the following kind of
    situation: placing a string into a non-null-terminated array intended
    for data communication or storage.

    struct rec {
    char first_name[15]; // null padded, not terminated
    char last_name[15]; // likewise
    // ...
    };

    Internally, you work with these as null terminated strings,
    copying them out to [16] buffers.

    These get written to disk, so the data has to be cleanly
    padded with nulls, even if a single null is enough to
    terminate short fields. You don't want random garbage
    from memory written out; it could be a security problem.

    strncpy helps to prepare the fields:

    char fn[16]; // null terminated
    /* ... */
    strncpy(rec->first_name, fn, 15); // correct in all cases

    as well as in the other direction

    strncpy(fn, rec->first_name, 15); // could use strcpy

    I'm using hard-coded constants for simplicity.

    If fn was initialized to zeros, and always used correctly to
    store a null-terminated string, there is nothing to do;
    a null is guaranteed to always be present at fn[15].
    Otherwise we need fn[15] = 0.

    So I think that's the nice use case for strncpy; unfortunately, over
    most of its life, it got clumsily used for other use cases.


    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Kaz Kylheku on Wed Jan 24 19:54:35 2024
    Kaz Kylheku <433-929-6894@kylheku.com> writes:
    On 2024-01-24, David Brown <david.brown@hesbynett.no> wrote:
    On 24/01/2024 00:31, Lawrence D'Oliveiro wrote:
    On Tue, 23 Jan 2024 22:28:03 -0000 (UTC), Blue-Maned_Hawk wrote:

    strncat

    If you can figure out how to use it properly.

    strncat is nasty, as "n" is the maximum number of characters to copy,
    not the size of the destination buffer. strncpy is nasty as it wastes
    effort needlessly writing null characters once the string is copied, and
    does not guarantee a terminating null.

    I always assumed strncpy was intended for the following kind of
    situation: placing a string into a non-null-terminated array intended
    for data communication or storage.

    struct rec {
    char first_name[15]; // null padded, not terminated
    char last_name[15]; // likewise
    // ...
    };

    Internally, you work with these as null terminated strings,
    copying them out to [16] buffers.

    These get written to disk, so the data has to be cleanly
    padded with nulls, even if a single null is enough to
    terminate short fields. You don't want random garbage
    from memory written out; it could be a security problem.

    strncpy helps to prepare the fields:

    char fn[16]; // null terminated
    /* ... */
    strncpy(rec->first_name, fn, 15); // correct in all cases

    snprintf(fn, sizeof(fn), "%s", rec->first_name) is also correct in
    all cases (and can tell you if it would have overflowed, if you're
    interested).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to David Brown on Thu Jan 25 00:12:57 2024
    On Wed, 24 Jan 2024 09:52:59 +0100, David Brown wrote:

    The common standard C str* functions are, IMHO, extraordinarily poorly specified, and could easily have been very much easier to use correctly
    and efficiently.

    True. Which is why it is better to look a little beyond them <https://manpages.debian.org/7/string_copying.en.html>.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to David Brown on Thu Jan 25 00:11:14 2024
    On Wed, 24 Jan 2024 09:40:34 +0100, David Brown wrote:

    Chained strcat ...

    Not that you chained it, but that you used it at all.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to Kaz Kylheku on Wed Jan 24 23:26:46 2024
    On 1/24/24 14:50, Kaz Kylheku wrote:
    ...
    I always assumed strncpy was intended for the following kind of
    situation: placing a string into a non-null-terminated array intended
    for data communication or storage.

    struct rec {
    char first_name[15]; // null padded, not terminated
    char last_name[15]; // likewise
    // ...
    };

    Internally, you work with these as null terminated strings,
    copying them out to [16] buffers.

    These get written to disk, so the data has to be cleanly
    padded with nulls, even if a single null is enough to
    terminate short fields. You don't want random garbage
    from memory written out; it could be a security problem.

    That is precisely the situation that led to me using strncpy().
    Apparently, the need to pad the space with nulls is the part that most
    people find odd. In addition to the security issues you mention, there's
    also a problem with comparing test output files from different runs - if
    they don't get nulled out, the unused parts get garbage in them, and
    routines for comparing the files will find different garbage in two
    files that should be identical.
    The other aspect of the situation I was working with that people found
    odd was that I was sometimes copying a string that might be too long for
    the destination, and it was OK if I had to truncate it to fit. That was
    due to the fact that the relevant string served essentially as a
    comment, rather than being a critical part of the output file.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Lawrence D'Oliveiro on Thu Jan 25 09:22:16 2024
    On 25/01/2024 01:11, Lawrence D'Oliveiro wrote:
    On Wed, 24 Jan 2024 09:40:34 +0100, David Brown wrote:

    Chained strcat ...

    Not that you chained it, but that you used it at all.

    If you think people are missing your points, perhaps you should try
    expressing them rather than berating people for not reading your mind.

    "strcat" is a perfectly good and useful library function. Like every
    function, without exception, you need to know what it does and that it
    is appropriate and correct to use it in your code.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Kaz Kylheku on Thu Jan 25 09:50:35 2024
    On 24/01/2024 20:50, Kaz Kylheku wrote:
    On 2024-01-24, David Brown <david.brown@hesbynett.no> wrote:
    On 24/01/2024 00:31, Lawrence D'Oliveiro wrote:
    On Tue, 23 Jan 2024 22:28:03 -0000 (UTC), Blue-Maned_Hawk wrote:

    strncat

    If you can figure out how to use it properly.

    strncat is nasty, as "n" is the maximum number of characters to copy,
    not the size of the destination buffer. strncpy is nasty as it wastes
    effort needlessly writing null characters once the string is copied, and
    does not guarantee a terminating null.

    I always assumed strncpy was intended for the following kind of
    situation: placing a string into a non-null-terminated array intended
    for data communication or storage.

    struct rec {
    char first_name[15]; // null padded, not terminated
    char last_name[15]; // likewise
    // ...
    };

    Internally, you work with these as null terminated strings,
    copying them out to [16] buffers.

    (I'd consider using 16 char arrays and saying that they /are/ null
    terminated. (Of course you don't trust that when you read records from
    files - you either check it, or force it, before using the record.)
    Much of your copying can then be very efficient inlined memcpy()'s - if
    you even need to copy data into and out of buffers. I understand this
    is about good use-cases for strncpy, rather than about ideal
    implementations of record storage. But if good uses of strncpy could be replaced by better code that does not use it, it weakens the
    justification for strncpy being defined the way it is.)


    These get written to disk, so the data has to be cleanly
    padded with nulls, even if a single null is enough to
    terminate short fields. You don't want random garbage
    from memory written out; it could be a security problem.

    Certainly strncpy would be good for such purposes. It is quite common, however, to use memset to first zero out such structs - that is simple, efficient (usually more efficient than strncpy would be), and also
    clears any padding.

    But I am not saying that strncpy, as it is defined, has no uses - I am
    saying that it would be more useful if it had been defined to copy a
    string with a hard limit on the number of characters to avoid any risk
    of overrun, without copying or padding unnecessarily, and with a
    guaranteed zero termination:

    char * string_copy(char * restrict dest,
    const char * restrict src, size_t n)
    {
    while (n--) {
    char c = *src++;
    *dest++ = c;
    if (!c) return dest - 1;
    }
    *(dest - 1) = '\0';
    return dest - 1;
    }

    (This also returns a pointer to the terminating null, which I see as
    more useful than a copy of "dest".)


    strncpy helps to prepare the fields:

    char fn[16]; // null terminated
    /* ... */
    strncpy(rec->first_name, fn, 15); // correct in all cases

    as well as in the other direction

    strncpy(fn, rec->first_name, 15); // could use strcpy

    "strcpy" might be a risk if you have read the record from a file and
    can't be sure it is null terminated. "strncpy" avoids that risk, but inefficiently and unnecessarily does extra padding. I would say it is
    better here to use memcpy(), then "fn[15] = '\0';".


    I'm using hard-coded constants for simplicity.

    Sure.


    If fn was initialized to zeros, and always used correctly to
    store a null-terminated string, there is nothing to do;
    a null is guaranteed to always be present at fn[15].
    Otherwise we need fn[15] = 0.

    So I think that's the nice use case for strncpy; unfortunately, over
    most of its life, it got clumsily used for other use cases.


    I certainly do think strncpy, as it is defined, has its uses. And in
    many situations it is a better choice than strcpy, even if it is less efficient. But I think it was a wasted opportunity - I think "strncpy"
    should have been defined the way I defined "string_copy" above (though
    for consistency it should return its first argument - and a matching
    stpncpy function could be made that returns an end pointer).

    Then a separate "strpad(char * s, size_t n)" function could be made that
    takes a string pointer and ensures that everything beyond the
    terminating '\0' is cleared to zero up to a total of n characters. (If
    there is no zero in those n characters, the data is left unchanged.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Keith Thompson on Thu Jan 25 10:31:29 2024
    On 24/01/2024 21:14, Keith Thompson wrote:

    Agreed. The name "strncpy" suggests that it's just "strcpy" with bounds checking, but it isn't. "strncat", on the other hand, pretty much *is* "strcat" with bounds checking. (And calling strncat() after setting the initial character of the target to '\0' actually does what some people
    assume strncpy should do.)

    I wrote about this a few years ago:

    https://github.com/Keith-S-Thompson/the-flat-trantor-society/blob/master/002-strncpy.md


    You could add a paragraph about how strncat also does not do what people
    might expect it to do. The "n" is the maximum number of characters to
    copy, not including the terminating zero. The most obvious user
    expectation would be that "n" is the size of the destination buffer, and
    the second most obvious expectation would be that it is the maximum
    number of characters copied /including/ the terminating zero.

    So IMHO, strncat does /roughly/ what you expect when you have set the
    first character of the target to 0 (once you remember to subtract 1 from
    the size), but is otherwise surprising in its behaviour. It should have
    been better.

    Oh, and there should have been a "strnlen" too. It would do the same as "memchr(s, '\0', n)", but have a better name. It's in POSIX and should
    be in the C standards.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Lawrence D'Oliveiro on Thu Jan 25 13:26:40 2024
    On 25/01/2024 01:12, Lawrence D'Oliveiro wrote:
    On Wed, 24 Jan 2024 09:52:59 +0100, David Brown wrote:

    The common standard C str* functions are, IMHO, extraordinarily poorly
    specified, and could easily have been very much easier to use correctly
    and efficiently.

    True. Which is why it is better to look a little beyond them <https://manpages.debian.org/7/string_copying.en.html>.

    Yes.

    Unfortunately, most of these are not standard C library functions. Some
    are common extensions in many libraries, others are, AFIAK, not so common.

    Still, thanks for that reference. I'll have a look at them and see if
    they are potentially useful to me and available with the C libraries I
    usually use.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Malcolm McLean on Thu Jan 25 18:22:51 2024
    On 2024-01-25, Malcolm McLean <malcolm.arthur.mclean@gmail.com> wrote:
    On 24/01/2024 08:40, David Brown wrote:

    So when someone has used "strcat" in a Usenet post, I would assume that
    real-world code would take appropriate care to be sure the arguments
    point to valid strings, and the destinations have enough space.  It's
    not unlikely that the real code actually uses "strncat" to reduce the
    risk of failures.

    By doing that you are replacing undefined behaviour with defined incorrect behaviour. Now undefined behaviour will often be correct behaviour, in the context that the program as whole is bugged. The result "segmentation fault" is less wrong than "the answeer to the life, universe and everything is 4" (we added an extra e to "answer" and so ran out of buffer).

    You're only saying that a documented extension which traps undefined
    behavior is better than a silent incorrect result.

    The crude memory violation checks resulting in a segmentation fault are
    not even remotely close to a reliable trap for undefined behavior.

    If you think that the situation when some datum does not fit into a
    buffer is a grave situation, such that the program must not continue
    executing, then you should use a copying function that issues a
    diagnostic and aborts in that situation (like an assert failure).

    I work on embedded firmware where that is the case. For string copying,
    we have have functions that will abort if truncation would take place,
    that return an indication that truncation took place, allowing the
    caller to decide what to do, and ones that silently truncate.

    Under no circumstances do you want to just overflow the buffer and hope
    that a segfault will catch it.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to Malcolm McLean on Thu Jan 25 13:36:37 2024
    On 2024-01-25, Malcolm McLean <malcolm.arthur.mclean@gmail.com> wrote:
    On 24/01/2024 08:40, David Brown wrote:

    So when someone has used "strcat" in a Usenet post, I would assume that
    real-world code would take appropriate care to be sure the arguments
    point to valid strings, and the destinations have enough space.  It's
    not unlikely that the real code actually uses "strncat" to reduce the
    risk of failures.

    By doing that you are replacing undefined behaviour with defined incorrect behaviour. Now undefined behaviour will often be correct behaviour, in the context that the program as whole is bugged. The result "segmentation fault" is less wrong than "the answeer to the life, universe and everything is 4" (we added an extra e to "answer" and so ran out of buffer).

    That depends entirely upon what the correct behavior was supposed to be.
    In the contexts where I used strncat(), the correct behavior specified
    by the requirements for the program I was writing when the input string
    was too big to fit in the output field was to put as much of the input
    string as possible into the output field, including the start of the
    string, even if that meant not having a terminating null character, but
    that it must not go past the end of the field, even if that meant
    chopping off the end of the string.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Malcolm McLean on Thu Jan 25 20:37:55 2024
    On 25/01/2024 18:46, Malcolm McLean wrote:
    On 24/01/2024 08:40, David Brown wrote:

    So when someone has used "strcat" in a Usenet post, I would assume
    that real-world code would take appropriate care to be sure the
    arguments point to valid strings, and the destinations have enough
    space.  It's not unlikely that the real code actually uses "strncat"
    to reduce the risk of failures.

    By doing that you are replacing undefined behaviour with defined incorrect behaviour. Now undefined behaviour will often be correct behaviour, in the context that the program as whole is bugged. The result "segmentation
    fault"
    is less wrong than "the answeer to the life, universe and everything is 4" (we added an extra e to "answer" and so ran out of buffer).
    Not always and sometimes wrong results are better than no results, for example in video game. Better send 4 baddies rather than 42 than shut
    down the game.


    That is all totally dependent on the situation, and the requirements.

    A buffer overrun is always wrong. It might not have major consequences,
    and those consequences might even be predictable (like a segmentation
    fault), though it is highly unlikely.

    Cutting a string short might also be an error - in which case, if it is
    a possibility, you should add checking somewhere. (It's typically quite
    easy to do here - if you want to limit your strings to 20 characters,
    including the null terminator, make your buffer 21 characters, clear it,
    and use that for your limits for strncat, strncpy or snprintf as
    appropriate. Then when you are done, check index 19 of your buffer - if
    it is not a null character, your string has been truncated.)

    Or cutting a string short might be perfectly correct and acceptable
    behaviour.

    Any generalisation here will be wrong much of the time.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to David Brown on Thu Jan 25 19:59:48 2024
    On Thu, 25 Jan 2024 09:22:16 +0100, David Brown wrote:

    "strcat" is a perfectly good and useful library function.

    Can’t think of any situation where it is worth using.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kenny McCormack@21:1/5 to ldo@nz.invalid on Thu Jan 25 20:29:55 2024
    In article <uouejj$2ei7i$1@dont-email.me>,
    Lawrence D'Oliveiro <ldo@nz.invalid> wrote:
    On Thu, 25 Jan 2024 09:22:16 +0100, David Brown wrote:

    "strcat" is a perfectly good and useful library function.

    Can't think of any situation where it is worth using.

    Well, that's clearly your problem, not ours.

    As the kids say, it's a "you problem".

    --
    The randomly chosen signature file that would have appeared here is more than 4 lines long. As such, it violates one or more Usenet RFCs. In order to remain in compliance with said RFCs, the actual sig can be found at the following URL:
    http://user.xmission.com/~gazelle/Sigs/ModernXtian

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Kenny McCormack on Thu Jan 25 22:01:04 2024
    On Thu, 25 Jan 2024 20:29:55 -0000 (UTC), Kenny McCormack wrote:

    In article <uouejj$2ei7i$1@dont-email.me>, Lawrence D'Oliveiro <ldo@nz.invalid> wrote:
    On Thu, 25 Jan 2024 09:22:16 +0100, David Brown wrote:

    "strcat" is a perfectly good and useful library function.

    Can't think of any situation where it is worth using.

    Well, that's clearly your problem, not ours.

    “You may say that I’m a dreamer;
    But I’m not the only one.”

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Kenny McCormack on Thu Jan 25 21:55:18 2024
    On 2024-01-25, Kenny McCormack <gazelle@shell.xmission.com> wrote:
    In article <uouejj$2ei7i$1@dont-email.me>,
    Lawrence D'Oliveiro <ldo@nz.invalid> wrote:
    On Thu, 25 Jan 2024 09:22:16 +0100, David Brown wrote:

    "strcat" is a perfectly good and useful library function.

    Can't think of any situation where it is worth using.

    Well, that's clearly your problem, not ours.

    As the kids say, it's a "you problem".

    In the entire TXR project:

    $ git grep strcat
    stream.c: strcat(num_buf, ".0");

    What's that? This is when the format function, in certain circumstances,
    is printing a floating-point number that has no fractional part,
    and the representation has to be machine-readable to reproduce the
    same object (Lisp "print-read consistency").

    This strcat is justified because num_buf is 512 characters. We know
    in this line of code that that it holds a printed floating-point value
    in decimal, and we know that double's exponent caps out at E+308. There
    cannot be an IEEE 64 bit floating-point double with anywhere near 512
    digits to the left of the decimal point on all the platforms.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Blue-Maned_Hawk@21:1/5 to David Brown on Thu Jan 25 22:46:06 2024
    David Brown wrote:

    On 24/01/2024 00:31, Lawrence D'Oliveiro wrote:
    On Tue, 23 Jan 2024 22:28:03 -0000 (UTC), Blue-Maned_Hawk wrote:

    strncat

    If you can figure out how to use it properly.

    strncat is nasty, as "n" is the maximum number of characters to copy,
    not the size of the destination buffer.

    sizeof outbuf - strlen(outbuf)



    --
    Blue-Maned_Hawk│shortens to Hawk│/ blu.mɛin.dʰak/ │he/him/his/himself/Mr.
    blue-maned_hawk.srht.site

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Malcolm McLean on Fri Jan 26 00:15:32 2024
    Malcolm McLean <malcolm.arthur.mclean@gmail.com> writes:
    On 25/01/2024 19:40, Keith Thompson wrote:
    Malcolm McLean <malcolm.arthur.mclean@gmail.com> writes:
    On 24/01/2024 08:40, David Brown wrote:
    So when someone has used "strcat" in a Usenet post, I would assume
    that real-world code would take appropriate care to be sure the
    arguments point to valid strings, and the destinations have enough
    space.  It's not unlikely that the real code actually uses "strncat"
    to reduce the risk of failures.

    By doing that you are replacing undefined behaviour with defined incorrect >>> behaviour.

    What "incorrect behaviour" are you talking about?

    David's premise is that the arguments point to valid strings and the
    destinations have enough space. Given that premise, strcat() works.

    char s[10];
    strcat(s, "hello");
    puts(s);

    What incorrect behavior do you see in the above code?

    Of course ensuring that the arguments are valid and the destination is
    big enough is sometimes easier said than done, but that's not the point.

    The proposal is to replace strcat with strncat() to improve behaviour
    when the bufffer overflows. So the premise is that the program is bugged
    and the overflow is unintentional, and there is no ideal behaviour.
    By calling strncat you force the program to truncate the string to fit
    the buffer. Which in reality might often be the best thing to do,
    because many strings are intended for human reading, and a human reader
    can guess what has happened and supply the missing characters. But the >results are always wrong, they are defined to be wrong.
    With undefined behaviour they are undefined (by C). But the two most
    likely results are either than the buffer will in fact have room and the >program will works as intended, though it won't be correct, or that the
    UB will be defined by the platform as to give a message that there has
    been a memory access error.

    Actually, it's highly _unlikely_ that an implementation would "give a
    message that there has been a memory access error", unless the buffer access crossed a page boundary and hit a not-present page. The majority
    of cases, strcat would just corrupt whatever followed the buffer,
    which often would be on the stack, where said corruption could lead
    to exploits even leaving aside the UB whenever the corrupt data
    is used.

    strcat should not be used. strncat isn't a suitable substitute.

    snprintf does work quite well as a substitute for strcat.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Malcolm McLean on Fri Jan 26 00:25:21 2024
    On 2024-01-25, Malcolm McLean <malcolm.arthur.mclean@gmail.com> wrote:
    The proposal is to replace strcat with strncat() to improve behaviour
    when the bufffer overflows. So the premise is that the program is bugged
    and the overflow is unintentional, and there is no ideal behaviour.
    By calling strncat you force the program to truncate the string to fit
    the buffer. Which in reality might often be the best thing to do,
    because many strings are intended for human reading, and a human reader
    can guess what has happened and supply the missing characters. But the results are always wrong, they are defined to be wrong.

    Yes; you have to pick one behavior for the function and ... USE
    SOMETHING ELSE for the other behaviors. Wow, eh?

    (Or have ioctl-like codes to select a behavior in one function,
    which is really like multipel functions in one.)

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Malcolm McLean on Fri Jan 26 16:12:40 2024
    On 26/01/2024 00:36, Malcolm McLean wrote:
    On 25/01/2024 19:40, Keith Thompson wrote:
    Malcolm McLean <malcolm.arthur.mclean@gmail.com> writes:
    On 24/01/2024 08:40, David Brown wrote:
    So when someone has used "strcat" in a Usenet post, I would assume
    that real-world code would take appropriate care to be sure the
    arguments point to valid strings, and the destinations have enough
    space.  It's not unlikely that the real code actually uses "strncat"
    to reduce the risk of failures.

    By doing that you are replacing undefined behaviour with defined
    incorrect
    behaviour.

    What "incorrect behaviour" are you talking about?

    David's premise is that the arguments point to valid strings and the
    destinations have enough space.  Given that premise, strcat() works.

         char s[10];
         strcat(s, "hello");
         puts(s);

    What incorrect behavior do you see in the above code?

    Of course ensuring that the arguments are valid and the destination is
    big enough is sometimes easier said than done, but that's not the point.

    The proposal is to replace strcat with strncat() to improve behaviour
    when the bufffer overflows.

    Yes.

    So the premise is that the program is bugged
    and the overflow is unintentional,

    Yes.

    and there is no ideal behaviour.

    Incorrect.

    You started out okay, but then drew an unwarranted conclusion from your premises.

    Buffer overflow means the original "strcat" program had a bug if it was expected to deal correctly with longer strings - longer strings caused
    UB, and that is never correct execution.

    Let's say the program is trying to concatenate two strings into a 10
    character buffer. There are, roughly, three was to specify this :

    1. It should give the concatenation for any two strings whose total
    length does not exceed 9 characters.

    2. It should give the concatenation for any two strings.

    3. It should up to the first 9 characters of the hypothetical unlimited
    length concatenation of any two strings.

    If the specification is 1, then the "strcat" version is fine - it is
    ideal behaviour. Garbage in, garbage out - programs are only required
    to be used according to their specifications.

    If the specification is 2, then it is impossible to write a program with
    the required behaviour.

    If the specification is 3, then strncat will give the ideal behaviour.


    The "ideal behaviour" is dependent on the specification for the program,
    and that has not been given here. Without the specification or
    requirements, it makes no sense to talk about whether there is or is not
    an "ideal behaviour", whether it is possible or not, or whether any
    given program fulfils it.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Keith Thompson on Fri Jan 26 16:22:37 2024
    On 25/01/2024 19:59, Keith Thompson wrote:
    David Brown <david.brown@hesbynett.no> writes:
    [...]
    I certainly do think strncpy, as it is defined, has its uses. And in
    many situations it is a better choice than strcpy, even if it is less
    efficient. But I think it was a wasted opportunity - I think
    "strncpy" should have been defined the way I defined "string_copy"
    above (though for consistency it should return its first argument -
    and a matching stpncpy function could be made that returns an end
    pointer).

    Then a separate "strpad(char * s, size_t n)" function could be made
    that takes a string pointer and ensures that everything beyond the
    terminating '\0' is cleared to zero up to a total of n characters.
    (If there is no zero in those n characters, the data is left
    unchanged.)

    To summarize, the problem with strncpy() is not its behavior, but its
    name. The name suggests that it's something that it isn't, a
    bound-checked version of strcpy(). It *is* useful for certain niche
    cases. For example, I think early Unix filesystems stored file names in
    a 14-byte field padded with zero or more null characters; that may have
    been *the* primary use case for strncpy().

    If I had a time machine, I might rename "strncpy()" to, say, "strpad()"
    (not the same as the "strpad() you suggest above) and define a new "strncpy()" that behaves like setting the first character of the target
    to '\0' and then calling strncat().


    I would go for a longer name - perhaps strncpy_padded(). Other than
    that, I agree with you.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to All on Fri Jan 26 16:28:48 2024
    On 25/01/2024 23:46, Blue-Maned_Hawk wrote:
    David Brown wrote:

    On 24/01/2024 00:31, Lawrence D'Oliveiro wrote:
    On Tue, 23 Jan 2024 22:28:03 -0000 (UTC), Blue-Maned_Hawk wrote:

    strncat

    If you can figure out how to use it properly.

    strncat is nasty, as "n" is the maximum number of characters to copy,
    not the size of the destination buffer.

    sizeof outbuf - strlen(outbuf)


    sizeof outbuf - strlen(outbuf) - 1

    strncat adds a \0 to the end, after copying up to n characters.

    And you have to check this to make sure it is not less than zero -
    remembering that all the types here are "size_t" and thus unsigned -
    before deciding if you can call strncat at all.

    As I said, nasty.


    And for a naïve use, you are scanning through the string currently
    "outbuf" to find its current length, then calling strncat on it which
    will re-scan the same string again to find the end point for starting
    the copy. And it will uselessly return the pointer to "outbuf", so that
    if you want to find the length of the combined string (perhaps for concatenating another string), you have to re-scan the whole thing again.

    Nasty and inefficient.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Scott Lurndal on Fri Jan 26 16:20:53 2024
    On 26/01/2024 01:15, Scott Lurndal wrote:

    strcat should not be used. strncat isn't a suitable substitute.


    Either can be used, if you know exactly what they do and they are
    suitable for the task.

    snprintf does work quite well as a substitute for strcat.


    It is a completely useless substitute in some situations - it has
    massively greater overhead, rendering it unusable in situations where efficiency is important.

    There is no single "right" answer here, nor are there "always wrong"
    answers. "stcat should not be used" is as wrong a generalisation as
    "strcat is always fine". Understand the functions, and use them
    appropriately - just like any other programming task.

    I am slightly surprised to be writing this in reply to a post by you -
    you are not exactly a newbie to programming, or to understanding that
    sometimes efficiency is critical, or to understanding that it's safe to
    use a function if and only if you have ensured it is safe to use that
    function.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Malcolm McLean on Sat Jan 27 15:44:22 2024
    On 27/01/2024 06:37, Malcolm McLean wrote:
    On 26/01/2024 15:12, David Brown wrote:
    On 26/01/2024 00:36, Malcolm McLean wrote:
    On 25/01/2024 19:40, Keith Thompson wrote:
    Malcolm McLean <malcolm.arthur.mclean@gmail.com> writes:
    On 24/01/2024 08:40, David Brown wrote:
    So when someone has used "strcat" in a Usenet post, I would assume >>>>>> that real-world code would take appropriate care to be sure the
    arguments point to valid strings, and the destinations have enough >>>>>> space.  It's not unlikely that the real code actually uses "strncat" >>>>>> to reduce the risk of failures.

    By doing that you are replacing undefined behaviour with defined
    incorrect
    behaviour.

    What "incorrect behaviour" are you talking about?

    David's premise is that the arguments point to valid strings and the
    destinations have enough space.  Given that premise, strcat() works.

         char s[10];
         strcat(s, "hello");
         puts(s);

    What incorrect behavior do you see in the above code?

    Of course ensuring that the arguments are valid and the destination is >>>> big enough is sometimes easier said than done, but that's not the
    point.

    The proposal is to replace strcat with strncat() to improve behaviour
    when the bufffer overflows.

    Yes.

    So the premise is that the program is bugged and the overflow is
    unintentional,

    Yes.

    and there is no ideal behaviour.

    Incorrect.

    You started out okay, but then drew an unwarranted conclusion from
    your premises.

    Buffer overflow means the original "strcat" program had a bug if it
    was expected to deal correctly with longer strings - longer strings
    caused UB, and that is never correct execution.

    Let's say the program is trying to concatenate two strings into a 10
    character buffer.  There are, roughly, three was to specify this :

    1. It should give the concatenation for any two strings whose total
    length does not exceed 9 characters.

    2. It should give the concatenation for any two strings.

    3. It should up to the first 9 characters of the hypothetical
    unlimited length concatenation of any two strings.

    If the specification is 1, then the "strcat" version is fine - it is
    ideal behaviour.  Garbage in, garbage out - programs are only required
    to be used according to their specifications.

    If the specification is 2, then it is impossible to write a program
    with the required behaviour.

    If the specification is 3, then strncat will give the ideal behaviour.


    The "ideal behaviour" is dependent on the specification for the
    program, and that has not been given here.  Without the specification
    or requirements, it makes no sense to talk about whether there is or
    is not an "ideal behaviour", whether it is possible or not, or whether
    any given program fulfils it.


    No. The program is trying to concatentate "strcat " and "strncat" into a
    ten byte buffer, and the specification is that the buffer shall hold
    "strcat strncat".

    Says who?

    Why do /you/ get to decide details of what the program is supposed to
    do? You can't just pick a specification after the fact as an attempt to justify your claims that the program is buggy!

    But since that is more than ten bytes that is
    impossible, the specification cannot be met and therefore the program is bugged.

    No. I the specification is impossible to meet, then the specification
    is at fault, not the program.


    Whether a program with strcat, or a program with strncat, is correct or
    not depends on the specifications for the program. The specifications
    were never given. So all we can do is look at what might or might not
    be correct for various plausible choices of specifications. We cannot
    conclude that a particular implementation is buggy, nor can we conclude
    that it is correct.

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