• Re: encapsulating directory operations

    From Paul Edwards@mutazilah@gmail.com to comp.lang.c on Thu Jun 5 19:28:53 2025
    From Newsgroup: comp.lang.c

    "David Brown" <david.brown@hesbynett.no> wrote in message news:101rdli$1cb3a$2@dont-email.me...
    On 04/06/2025 20:58, Paul Edwards wrote:
    "David Brown" <david.brown@hesbynett.no> wrote in message news:101poqm$t350$1@dont-email.me...
    On 04/06/2025 11:23, Paul Edwards wrote:

    And I know what you're thinking - all the data is in EBCDIC.
    There are no other EBCDIC systems I could possibly jump to.
    We would need an 80386 EBCDIC version of Win32 in order
    for this to be remotely possible - which doesn't exist, and likely
    never will exist.

    For it to exist it would need some sort of pseudo-bios concept
    that allowed charset conversion. And no such thing exists as far
    as I am aware!

    You don't need an EBCDIC operating system, or "pseudo-bios" (whatever
    /that/ might be) to use data using EBCDIC character encoding. It is no
    different from working with any other character encoding - ASCII,
    UTF-8,
    different 8-bit code pages, or whatever. If the data is just passing
    through your code, read it in and pass it out without a care. If you
    need to convert it or mix it with some other encoding, work with a
    common encoding - UTF-8 is normally the right choice.

    If I have existing C code that does:

    fopen("test1.dat", "rb");
    fread into buf
    if (memcmp(buf + 5, "XYZ", 3) == 0)

    and test1.dat is in EBCDIC, the above program on the 80386
    has been compiled with EBCDIC strings, so it works, and then
    now you do:

    printf("It matches!\n");

    where do you expect those characters in the printf string - all
    currently EBCDIC - to be translated to ASCII on a modern
    Windows 10/11 system?


    In /your/ code!

    /You/ are responsible for writing the code that handles the data, and
    which gets the encoding right. If you want to handle data in an odd encoding, write code to handle it. That's what everyone else does when dealing with data in different encodings.

    With my application programmer's hat on, I have already
    written the code, it is already C90-compliant, and no, I
    don't want to change my code.

    And how do you expect Windows 10/11 to find "test1.dat" -
    all EBCDIC on its current ASCII filesystem?

    Convert the character encoding for the string.

    People do this all the time. They write code that uses UTF-8, and have
    to deal with Windows crappy UCS2 / partial UTF-16 / wchar_t mess. Or
    they have code that supports different code pages because they started
    it in the pre-Unicode days and don't live in a little American-only
    ASCII bubble.

    C does not make this stuff particularly convenient, though it has
    improved a little since C90 - other languages have vastly superior
    string and encoding handling. But that does not mean you can't do it,
    or should not do it.

    Maybe if you actually wanted to contribute something useful to the C
    world - something that other people might find useful - you could put
    your efforts into writing a library that has functions for converting encodings back and forth with UTF-8 as the base. Include support for
    the dozen different EDBDIC versions.

    Or do you really think that if someone sent me a file to read that was
    in EDBDIC encoding, I'd be happy to install an EDBDIC "pseudo-bios" and EDBDIC version of Windows so that I could read it?

    It's not for you to send a file.

    It's for an enterprise - suddenly forced off the mainframe -
    to carry on, on a different machine, with no data conversion,
    and no code changes, because they took the precaution of
    writing in C90.

    And all running at native speed on the new x64 processor.

    BFN. Paul.


    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Paul Edwards@mutazilah@gmail.com to comp.lang.c on Thu Jun 5 19:31:38 2025
    From Newsgroup: comp.lang.c

    "Kaz Kylheku" <643-408-1753@kylheku.com> wrote in message news:20250604142536.604@kylheku.com...
    On 2025-06-04, Paul Edwards <mutazilah@gmail.com> wrote:
    "Kaz Kylheku" <643-408-1753@kylheku.com> wrote in message news:20250604121550.286@kylheku.com...
    On 2025-06-04, Paul Edwards <mutazilah@gmail.com> wrote:
    If I have existing C code that does:

    fopen("test1.dat", "rb");
    fread into buf
    if (memcmp(buf + 5, "XYZ", 3) == 0)

    and test1.dat is in EBCDIC, the above program on the 80386
    has been compiled with EBCDIC strings, so it works, and then
    now you do:

    printf("It matches!\n");

    where do you expect those characters in the printf string - all
    currently EBCDIC - to be translated to ASCII on a modern
    Windows 10/11 system?

    I think that in this case you would ideally want the EBCDIC-enabled
    compiler to have extensions for working with both character sets.

    For isntance E"foo" would specify a string object that is
    encoded in EBCDIC in the execution environment, whereas
    "foo" is ASCII.

    You could develop a textual preprocesor which implements
    this transformation: tokenizes the C well enough to recognize
    string literals preceded by E, and translates them to
    string literals without E, whose content is EBCDIC bytes.

    It wouldn't have to be absoultely perfect; only to work
    correctly on your small amount of private code.

    Even assuming the above was technically possible - what's
    wrong with just having a pseudo-bios and OS so that
    everything is totally clean?

    Nothing; it's just that the OS doesn't match the requirement given in
    some above quoted text I was responding to that this is on "modern
    Windows 10/11 system" which requires the program output to be
    "translated to ASCII".

    The OS - PDOS-generic - does (or can) "match the requirement".

    The underlying Windows 10/11 system is converted into
    nothing more than a glorified BIOS, ie pseudo-bios, which
    PDOS-generic can then run under.

    No emulation. Native x64 speed. EBCDIC.

    BFN. Paul.


    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Paul Edwards@mutazilah@gmail.com to comp.lang.c on Thu Jun 5 20:25:37 2025
    From Newsgroup: comp.lang.c

    "David Brown" <david.brown@hesbynett.no> wrote in message news:101rdli$1cb3a$2@dont-email.me...

    Maybe if you actually wanted to contribute something useful to the C
    world - something that other people might find useful

    I'm not trying to win Miss America.

    I'm trying to make C90 - or a slight variation - that e.g.
    would include directory operations, hence the title,
    actually "work as designed".

    None of this nonsense about "real world programs
    can't be written in C90".

    Instead, if someone complains about IBM's monopoly, I
    just want to be able to respond "well maybe you should
    have written your app in C90. If you had, you could have
    moved your entire application, completely unchanged,
    with a simple recompile".

    Previously people could have said "It's not a simple
    matter of recompilation - you need to migrate the
    data from EBCDIC to ASCII too".

    And even further back than that, they would have said
    something along the lines of "IBM files are record-based
    and cannot be migrated".

    I never really understood the comment about being
    record-based. Took a long time to find out they were
    talking about the Loch Ness Monster.

    And the "-m31" (instead of -m32) option of gcc was
    another Loch Ness Monster.

    It took a long time to drain Loch Ness once and for all.

    And bring IBM mainframes in from the cold.

    BFN. Paul.


    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From antispam@antispam@fricas.org (Waldek Hebisch) to comp.lang.c on Fri Jun 6 03:26:40 2025
    From Newsgroup: comp.lang.c

    David Brown <david.brown@hesbynett.no> wrote:

    Maybe if you actually wanted to contribute something useful to the C
    world - something that other people might find useful - you could put
    your efforts into writing a library that has functions for converting encodings back and forth with UTF-8 as the base. Include support for
    the dozen different EDBDIC versions.

    AFAICS librecode already exists and can do this.
    --
    Waldek Hebisch
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Bonita Montero@Bonita.Montero@gmail.com to comp.lang.c on Fri Jun 6 10:29:12 2025
    From Newsgroup: comp.lang.c

    Am 03.06.2025 um 20:25 schrieb Scott Lurndal:
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 02.06.2025 um 17:24 schrieb Scott Lurndal:
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 01.06.2025 um 09:43 schrieb Lawrence D'Oliveiro:

    On Sun, 1 Jun 2025 07:58:54 +0200, Bonita Montero wrote:

    Sth. like this:

    for( directory_entry const &de : recursive_directory_iterator( "\\", >>>>>> directory_options::follow_directory_symlink ) )
    cout << de.path() << endl;

    You need the dirfd functions to avoid certain potential security holes >>>>> on operations with symlinks.

    Which security holes ?


    The fchownat() function shall be equivalent to the chown() and lchown()
    functions except in the case where path specifies a relative path. In >>> this case the file to be changed is determined relative to the directory
    associated with the file descriptor fd instead of the current working >>> directory. If the access mode of the open file description associated >>> with the file descriptor is not O_SEARCH, the function shall check
    whether directory searches are permitted using the current permissions >>> of the directory underlying the file descriptor. If the access mode is >>> O_SEARCH, the function shall not perform the check.

    And why is this relevant for directory_iterator or
    recursive_directory_iterator ?

    Why are you asking this question on comp.lang.c?

    Because you can have it easier than with opendir() / readdir().

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From scott@scott@slp53.sl.home (Scott Lurndal) to comp.lang.c on Fri Jun 6 14:10:07 2025
    From Newsgroup: comp.lang.c

    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 03.06.2025 um 20:25 schrieb Scott Lurndal:

    The fchownat() function shall be equivalent to the chown() and lchown()
    functions except in the case where path specifies a relative path. In >>>> this case the file to be changed is determined relative to the directory
    associated with the file descriptor fd instead of the current working >>>> directory. If the access mode of the open file description associated >>>> with the file descriptor is not O_SEARCH, the function shall check >>>> whether directory searches are permitted using the current permissions
    of the directory underlying the file descriptor. If the access mode is
    O_SEARCH, the function shall not perform the check.

    And why is this relevant for directory_iterator or
    recursive_directory_iterator ?

    Why are you asking this question on comp.lang.c?

    Because you can have it easier than with opendir() / readdir().

    Personally, I'd use nftw to iterate over a path.

    https://pubs.opengroup.org/onlinepubs/9799919799/functions/nftw.html

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Bonita Montero@Bonita.Montero@gmail.com to comp.lang.c on Fri Jun 6 19:10:13 2025
    From Newsgroup: comp.lang.c

    Am 06.06.2025 um 16:10 schrieb Scott Lurndal:
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 03.06.2025 um 20:25 schrieb Scott Lurndal:

    The fchownat() function shall be equivalent to the chown() and lchown()
    functions except in the case where path specifies a relative path. In
    this case the file to be changed is determined relative to the directory
    associated with the file descriptor fd instead of the current working
    directory. If the access mode of the open file description associated
    with the file descriptor is not O_SEARCH, the function shall check >>>>> whether directory searches are permitted using the current permissions
    of the directory underlying the file descriptor. If the access mode is
    O_SEARCH, the function shall not perform the check.

    And why is this relevant for directory_iterator or
    recursive_directory_iterator ?

    Why are you asking this question on comp.lang.c?

    Because you can have it easier than with opendir() / readdir().

    Personally, I'd use nftw to iterate over a path.

    https://pubs.opengroup.org/onlinepubs/9799919799/functions/nftw.html

    That would be much nicer with a lambda which can take the context
    of the calling function.

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From scott@scott@slp53.sl.home (Scott Lurndal) to comp.lang.c on Fri Jun 6 17:24:50 2025
    From Newsgroup: comp.lang.c

    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 06.06.2025 um 16:10 schrieb Scott Lurndal:
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 03.06.2025 um 20:25 schrieb Scott Lurndal:

    The fchownat() function shall be equivalent to the chown() and lchown()
    functions except in the case where path specifies a relative path. In
    this case the file to be changed is determined relative to the directory
    associated with the file descriptor fd instead of the current working
    directory. If the access mode of the open file description associated
    with the file descriptor is not O_SEARCH, the function shall check >>>>>> whether directory searches are permitted using the current permissions
    of the directory underlying the file descriptor. If the access mode is
    O_SEARCH, the function shall not perform the check.

    And why is this relevant for directory_iterator or
    recursive_directory_iterator ?

    Why are you asking this question on comp.lang.c?

    Because you can have it easier than with opendir() / readdir().

    Personally, I'd use nftw to iterate over a path.

    https://pubs.opengroup.org/onlinepubs/9799919799/functions/nftw.html

    That would be much nicer with a lambda which can take the context
    of the calling function.


    That's a matter of opinion. In any case, nftw is a C function
    and C doesn't support lamda functions. nftw can be used
    in any C++ code, it is not restricted to C++17 or higher,
    which makes it far more portable than std::filesystem::directory_iterator
    (not to mention much easier to type :-).
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Bonita Montero@Bonita.Montero@gmail.com to comp.lang.c on Fri Jun 6 19:26:51 2025
    From Newsgroup: comp.lang.c

    Am 06.06.2025 um 19:24 schrieb Scott Lurndal:
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 06.06.2025 um 16:10 schrieb Scott Lurndal:
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 03.06.2025 um 20:25 schrieb Scott Lurndal:

    The fchownat() function shall be equivalent to the chown() and lchown()
    functions except in the case where path specifies a relative path. In
    this case the file to be changed is determined relative to the directory
    associated with the file descriptor fd instead of the current working
    directory. If the access mode of the open file description associated
    with the file descriptor is not O_SEARCH, the function shall check
    whether directory searches are permitted using the current permissions
    of the directory underlying the file descriptor. If the access mode is
    O_SEARCH, the function shall not perform the check.

    And why is this relevant for directory_iterator or
    recursive_directory_iterator ?

    Why are you asking this question on comp.lang.c?

    Because you can have it easier than with opendir() / readdir().

    Personally, I'd use nftw to iterate over a path.

    https://pubs.opengroup.org/onlinepubs/9799919799/functions/nftw.html

    That would be much nicer with a lambda which can take the context
    of the calling function.


    That's a matter of opinion.

    It results in much less code. Just like a lambda passed
    to std::sort() which takes context of the calling function.

    In any case, nftw is a C function and C doesn't support lamda functions.

    Lambdas without capture can be converted to C function-pointers.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From wij@wyniijj5@gmail.com to comp.lang.c on Sat Jun 7 04:04:24 2025
    From Newsgroup: comp.lang.c

    On Fri, 2025-06-06 at 19:26 +0200, Bonita Montero wrote:
    Am 06.06.2025 um 19:24 schrieb Scott Lurndal:
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 06.06.2025 um 16:10 schrieb Scott Lurndal:
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 03.06.2025 um 20:25 schrieb Scott Lurndal:

           The fchownat() function shall be equivalent to the chown() and lchown()
           functions except in the case where path specifies a relative path. In
           this case the file to be changed is determined relative to the directory
           associated with the file descriptor fd instead of the current working
           directory. If the access mode of the open file description associated
           with the file descriptor is not O_SEARCH, the function shall check
           whether directory searches are permitted using the current permissions
           of the directory underlying the file descriptor. If the access mode is
           O_SEARCH, the function shall not perform the check.

    And why is this relevant for directory_iterator or recursive_directory_iterator ?

    Why are you asking this question on comp.lang.c?

    Because you can have it easier than with opendir() / readdir().

    Personally, I'd use nftw to iterate over a path.

    https://pubs.opengroup.org/onlinepubs/9799919799/functions/nftw.html

    That would be much nicer with a lambda which can take the context
    of the calling function.


    That's a matter of opinion.

    It results in much less code. 
    That is illusion, particularly in this case (about directory).
    Most serious programs will 'invent' their own functions, even their own
    'sugar syntax'... result would be better than 'directory_iterator'.
    In general, no need for such things as 'directory_iterator' in C-library.
    Just like a lambda passed
    to std::sort() which takes context of the calling function.

    In any case, nftw is a C function and C doesn't support lamda functions.

    Lambdas without capture can be converted to C function-pointers.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Tim Rentsch@tr.17687@z991.linuxsc.com to comp.lang.c on Fri Jun 6 17:16:09 2025
    From Newsgroup: comp.lang.c

    Kaz Kylheku <643-408-1753@kylheku.com> writes:

    On 2025-05-28, Michael S <already5chosen@yahoo.com> wrote:

    Got it. Stability occurs when the standards is fenced from
    changes by presence of the next edition.

    Each technical corrigendum effectively yields a new edition.

    The previous standard without that corrigendum is forever stable,
    as any immutable object.

    There are two reasons why these comments are off base.

    The first is the word "edition" is wrong. All of the ISO documents
    related to C99, whether the original one or a later one associated
    with a TC, all say "this second edition...". And similarly for
    other versions of the language.

    The second is that the discussion is not about what is covered by
    ISO labels but about C90, C99, C11, etc. Each of these names is
    about one edition of the language, no matter how many separate ISO
    documents are involved, and that's what the conversation is
    concerned with. The documents might be immutable, but the documents
    are not what is under discussion, which is different versions (in
    other words editions, in the official terminology of the ISO C
    standards) of the C language.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Tim Rentsch@tr.17687@z991.linuxsc.com to comp.lang.c on Fri Jun 6 17:26:21 2025
    From Newsgroup: comp.lang.c

    Michael S <already5chosen@yahoo.com> writes:

    On Tue, 27 May 2025 16:16:50 -0700
    Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:

    Michael S <already5chosen@yahoo.com> writes:

    On Tue, 27 May 2025 16:23:22 +0200
    David Brown <david.brown@hesbynett.no> wrote:

    On 26/05/2025 07:19, Peter 'Shaggy' Haywood wrote:

    Groovy hepcat Tim Rentsch was jivin' in comp.lang.c on Fri, 23 May
    2025 10:43 pm. It's a cool scene! Dig it.

    C99 is just as stable as C90, and has been for well over a
    decade.

    Methinks Tim is having trouble with his arithmetic. Either
    that or he doesn't know what year it is now. :)
    C99 was ratified in 1999, over two and a half decades ago.

    C11 is just as stable as C90, and has been for just slightly
    less than a decade.

    And C11 was ratified in 2011, no? That was almost a decade
    and a half ago.

    Tim was, I believe, taking into account the time it took for common
    implementations of C compilers and libraries to have complete and
    generally bug-free support for the standards, and for these
    implementations to become common. C99 was published in 1999, but
    it took quite a while before most people programming in C could
    happily use C99 without worrying about the tool support being
    "experimental" or not as mature as C90 support.

    I believe that your belief is wrong.
    It is much more likely that Tim took into account defect reports.
    Here is the list of C11 defect reports with the last dated 2016:
    https://open-std.org/jtc1/sc22/wg14/www/docs/summary.htm

    I did not find similar list for C99. However believing Tim I would
    guess that the last change in C99 document was made ~15 years ago.

    You are partly right. Besides defect reports, there are TCs. And
    there is always the possibility of future TCs, future defect
    reports, or future changes for any ISO C standard while it is
    still current.

    To be as stable as C90, a C standard would need to be immune to
    the possibility of such future changes.

    I take C99 to have reached this level of stability in 2011, when
    it was superseded by C11. I take C11 to have reached this level
    of stability in 2017, when it was superseded by C17.

    Got it. Stability occurs when the standards is fenced from
    changes by presence of the next edition.
    Stability by obsolescence.

    Right except the word obsolescence is not appropriate. The release
    of C99 doesn't make C90 either obsolete or obsolescent. It is
    possible that a given earlier edition of C will become either
    obsolete or obsolescent, but it isn't the release of a subsequent
    edition that does that. Stability happens when a subsequent edition
    is ratified, regardless of when or whether an earlier edition ever
    becomes either obsolete or obsolescent. No edition of C is now
    obsolete, nor do I expect any of them will be during our lifetimes.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Bonita Montero@Bonita.Montero@gmail.com to comp.lang.c on Sat Jun 7 06:24:53 2025
    From Newsgroup: comp.lang.c

    Am 06.06.2025 um 22:04 schrieb wij:
    On Fri, 2025-06-06 at 19:26 +0200, Bonita Montero wrote:
    Am 06.06.2025 um 19:24 schrieb Scott Lurndal:
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 06.06.2025 um 16:10 schrieb Scott Lurndal:
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 03.06.2025 um 20:25 schrieb Scott Lurndal:

           The fchownat() function shall be equivalent to the chown() and lchown()
           functions except in the case where path specifies a relative path. In
           this case the file to be changed is determined relative to the directory
           associated with the file descriptor fd instead of the current working
           directory. If the access mode of the open file description associated
           with the file descriptor is not O_SEARCH, the function shall check
           whether directory searches are permitted using the current permissions
           of the directory underlying the file descriptor. If the access mode is
           O_SEARCH, the function shall not perform the check. >>>>>>>>
    And why is this relevant for directory_iterator or
    recursive_directory_iterator ?

    Why are you asking this question on comp.lang.c?

    Because you can have it easier than with opendir() / readdir().

    Personally, I'd use nftw to iterate over a path.

    https://pubs.opengroup.org/onlinepubs/9799919799/functions/nftw.html

    That would be much nicer with a lambda which can take the context
    of the calling function.


    That's a matter of opinion.

    It results in much less code.

    That is illusion, particularly in this case (about directory).

    I've shown the code. In C++17 iterating through a single directory and
    printing it is two lines of code.

    Most serious programs will 'invent' their own functions, even their own 'sugar syntax'...

    Not to iterate through a directory in C++.

    'sugar syntax'... result would be better than 'directory_iterator'.

    Why ?

    In general, no need for such things as 'directory_iterator' in C-library.


    In C you're forced to write ten times the code if you include error
    handling.

    Just like a lambda passed
    to std::sort() which takes context of the calling function.

    In any case, nftw is a C function and C doesn't support lamda functions.

    Lambdas without capture can be converted to C function-pointers.


    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From wij@wyniijj5@gmail.com to comp.lang.c on Sat Jun 7 19:58:01 2025
    From Newsgroup: comp.lang.c

    On Sat, 2025-06-07 at 06:24 +0200, Bonita Montero wrote:
    Am 06.06.2025 um 22:04 schrieb wij:
    On Fri, 2025-06-06 at 19:26 +0200, Bonita Montero wrote:
    Am 06.06.2025 um 19:24 schrieb Scott Lurndal:
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 06.06.2025 um 16:10 schrieb Scott Lurndal:
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 03.06.2025 um 20:25 schrieb Scott Lurndal:

            The fchownat() function shall be equivalent to the chown() and lchown()
            functions except in the case where path specifies a relative path. In
            this case the file to be changed is determined relative to the directory
            associated with the file descriptor fd instead of the current working
            directory. If the access mode of the open file description associated
            with the file descriptor is not O_SEARCH, the function shall check
            whether directory searches are permitted using the current permissions
            of the directory underlying the file descriptor. If the access mode is
            O_SEARCH, the function shall not perform the check.

    And why is this relevant for directory_iterator or recursive_directory_iterator ?

    Why are you asking this question on comp.lang.c?

    Because you can have it easier than with opendir() / readdir().

    Personally, I'd use nftw to iterate over a path.

    https://pubs.opengroup.org/onlinepubs/9799919799/functions/nftw.html

    That would be much nicer with a lambda which can take the context
    of the calling function.


    That's a matter of opinion.

    It results in much less code.

    That is illusion, particularly in this case (about directory).

    I've shown the code. In C++17 iterating through a single directory and printing it is two lines of code.
    C has to deal with hardware, that is all the basic (C++ and other languages are fundamentally the same).
    There are 'tons' of details to consider, e.g. in multi-user/tasking condition.
    Most serious programs will 'invent' their own functions, even their own 'sugar syntax'...

    Not to iterate through a directory in C++.

    'sugar syntax'... result would be better than 'directory_iterator'.

    Why ?
    There are many kinds of 'sub-languages' that can do any specific job better.
    As said, C is the basic. Actually, IMO, C++ could be better than C, but C++ invented 10 times more possibility of doing things wrong (because most are duplications).
    In general, no need for such things as 'directory_iterator' in C-library.


    In C you're forced to write ten times the code if you include error
    handling.
    C++ is no better, probably worse. Firstly, the term 'exception' (and
    'vector' probably) is disabled in human mind to think about exception.
    C++ had been trying to HIDE error (exception) from its beginning, very wrong and very unsuccessful.
    Snipet from https://sourceforge.net/projects/cscall/files/MisFiles/ClassGuidelines.txt/download
    -------- Returning error and the error checking
    Error (branching point information) not handled is always a hiden 'UB' waiting
    to happen. The guideline is: ALWAYS CHECK THE ERROR. This guideline extends to
    API, therefore, "__attribute__((warn_unused_result))" should be added to the
    returning Errno. As usual, exception exists.
    From function usage point of view: The general concern of error checking may
    be the complexity and readibility of the source codes. In this guidelines'
    view, without the checking and handling of the error, the evaluation
    (readibility, beauty,..) of the codes is one-sided judgement, because error
    checking and handling is part of the complete program or (function). The API
    can always move the 'unwelcome' one to somewhere, but the machine sees
    everything.
    The minimum check-and-throw pattern is better than ignoration, such coding
    (throw) is equivalent to a blended cheap-and-better assert.
    Practice proves that error checking actually reduces software development and
    maintenance time, significantly.
    Notice the term 'branching point', errors are merely branching points.
    For a progam to be shorter/simpler as desired, how many branching points are handled? What is 'convenience' based on, ignorance?
    Just like a lambda passed
    to std::sort() which takes context of the calling function.

    In any case, nftw is a C function and C doesn't support lamda functions.

    Lambdas without capture can be converted to C function-pointers.

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Bonita Montero@Bonita.Montero@gmail.com to comp.lang.c on Sat Jun 7 18:31:35 2025
    From Newsgroup: comp.lang.c

    Am 07.06.2025 um 13:58 schrieb wij:

    C++ is no better, probably worse. Firstly, the term 'exception' (and
    'vector' probably) is disabled in human mind to think about exception.

    I deal with exceptions every day and I've no problems with that. C++
    exceptions aren't that clean like Java exceptions, but still make much
    less work than C return code handling.

    C++ had been trying to HIDE error (exception) from its beginning, very wrong and very unsuccessful.

    Exceptions are the cleanest way of error handling. In most cases you
    deal with the error in a higher function and not in the leaf of the
    call graph; therefore exceptions are perfect.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From wij@wyniijj5@gmail.com to comp.lang.c on Sun Jun 8 02:43:25 2025
    From Newsgroup: comp.lang.c

    On Sat, 2025-06-07 at 18:31 +0200, Bonita Montero wrote:
    Am 07.06.2025 um 13:58 schrieb wij:

    C++ is no better, probably worse. Firstly, the term 'exception' (and 'vector' probably) is disabled in human mind to think about exception.

    I deal with exceptions every day and I've no problems with that. C++ exceptions aren't that clean like Java exceptions, but still make much
    less work than C return code handling.
    The error from opendir(..):
    EACCES Permission denied.
    EBADF fd is not a valid file descriptor opened for reading.
    EMFILE The per-process limit on the number of open file descriptors has
    been reached.
    ENFILE The system-wide limit on the total number of open files has been
    reached.
    ENOENT Directory does not exist, or name is an empty string.
    ENOMEM Insufficient memory to complete the operation.
    ENOTDIR name is not a directory.
    ...
    Those are just common errors, there are actually more, and more from subsequent read/write/.... File operations (particularily directory) are actually very nasty.
    I think your 'less work' was from simplication or idealization, lots are simplified and idealized.
    We have no problem is because our average programs don't need to handle those individual errors, but C's major task is to build OS, not average applications.
    C++ had been trying to HIDE error (exception) from its beginning, very wrong
    and very unsuccessful.

    Exceptions are the cleanest way of error handling. 
    Conditional. As said 'simplified'.
    In most cases you
    deal with the error in a higher function and not in the leaf of the
    call graph; therefore exceptions are perfect.
    If you can handle errors that way, C can also do it in much simpler way.
    The basic problem of throwing error is losing error context, it is similar
    to set a global errno (or object) and cause the program to stack unwind.
    C++ has many artificial fancy things and encourages such programming style. Good or bad? Both, but I would say mostly bad.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Bonita Montero@Bonita.Montero@gmail.com to comp.lang.c on Sat Jun 7 21:22:49 2025
    From Newsgroup: comp.lang.c

    Am 07.06.2025 um 20:43 schrieb wij:

    The error from opendir(..):

    EACCES Permission denied.
    EBADF fd is not a valid file descriptor opened for reading.
    EMFILE The per-process limit on the number of open file descriptors has
    been reached.
    ENFILE The system-wide limit on the total number of open files has been
    reached.
    ENOENT Directory does not exist, or name is an empty string.
    ENOMEM Insufficient memory to complete the operation.
    ENOTDIR name is not a directory.
    ...

    With C++ that's all encapsulated in filesystem_error which is derived
    from system_error. But you can omit permission denied errors with directory_options::skip_permission_denied. That makes sense mostly
    with recursive_directory_iterator. If you want to to that in C you've
    at least ten times the code.

    I think your 'less work' was from simplication or idealization, lots are simplified and idealized.

    No, show me your C code to iterate through a directory and I show you
    the same code with up to a fifth of C++-code. When you iterate
    recur-sively that's a tenth of the code.

    If you can handle errors that way, C can also do it in much simpler way.

    Absolutely not.

    The basic problem of throwing error is losing error context, it is similar
    to set a global errno (or object) and cause the program to stack unwind.

    The context doesn't matter. A bad_alloc doesn't need context, a
    system_error also not. And that's most of the exceptions the C++
    runtime throws (some are derived from system_error).

    C++ has many artificial fancy things and encourages such programming style. Good or bad? Both, but I would say mostly bad.

    C is bad, because you've to do simple things over and over with multiple
    times the code because there are no language-facilities to abstract your coding.

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From wij@wyniijj5@gmail.com to comp.lang.c on Sun Jun 8 03:56:47 2025
    From Newsgroup: comp.lang.c

    On Sat, 2025-06-07 at 21:22 +0200, Bonita Montero wrote:
    Am 07.06.2025 um 20:43 schrieb wij:

    The error from opendir(..):

            EACCES Permission denied.
            EBADF  fd is not a valid file descriptor opened for reading.
            EMFILE The  per-process limit on the number of open file descriptors has
                   been reached.
            ENFILE The system-wide limit on the total number of open files has  been
                   reached.
            ENOENT Directory does not exist, or name is an empty string.         ENOMEM Insufficient memory to complete the operation.         ENOTDIR name is not a directory.
            ...

    With C++ that's all encapsulated in filesystem_error which is derived
    from system_error. But you can omit permission denied errors with directory_options::skip_permission_denied. That makes sense mostly
    with recursive_directory_iterator. 
    I see std::filesystem as evidence that C++ finally admits the deficiency of
    its advance error handling system (std::exception). But the result is worse than C.
    I am not familiar with C++ std lib, I cannot say much.
    If you want to to that in C you've
    at least ten times the code.

    I think your 'less work' was from simplication or idealization, lots are simplified and idealized.

    No, show me your C code to iterate through a directory and I show you
    the same code with up to a fifth of C++-code. When you iterate
    recur-sively that's a tenth of the code.

    If you can handle errors that way, C can also do it in much simpler way.

    Absolutely not.

    The basic problem of throwing error is losing error context, it is similar to set a global errno (or object) and cause the program to stack unwind.

    The context doesn't matter. A bad_alloc doesn't need context, a
    system_error also not. And that's most of the exceptions the C++
    runtime throws (some are derived from system_error).

    C++ has many artificial fancy things and encourages such programming style. Good or bad? Both, but I would say mostly bad.

    C is bad, because you've to do simple things over and over with multiple times the code because there are no language-facilities to abstract your coding.
    Most of the above replies are false owing from narrow view. I'll save my explanations.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.c on Sat Jun 7 23:12:23 2025
    From Newsgroup: comp.lang.c

    On 07.06.2025 21:22, Bonita Montero wrote:
    Am 07.06.2025 um 20:43 schrieb wij:

    The basic problem of throwing error is losing error context, [...]

    Nonsense.

    The context doesn't matter. A bad_alloc doesn't need context, a
    system_error also not. And that's most of the exceptions the C++
    runtime throws (some are derived from system_error).

    Context, in the general case, matters. It's been decades that I
    used C++, but back then in the 1990's we did pass error context
    with the thrown error object. That's an inherent part of C++'s
    exception handling. If you use standard error classes without
    context that's of course possible, but nothing prevents you to
    define yet another error class derived from some existing error
    class with additional information. You are actually free to do
    what suits you and your projects best; use rudimentary handling,
    or have a sophisticated error framework, or anything in between.

    Janis

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Bonita Montero@Bonita.Montero@gmail.com to comp.lang.c on Sat Jun 7 23:57:12 2025
    From Newsgroup: comp.lang.c

    Am 07.06.2025 um 21:56 schrieb wij:

    I see std::filesystem as evidence that C++ finally admits the deficiency of its advance error handling system (std::exception). But the result is worse than C.
    I am not familiar with C++ std lib, I cannot say much.

    LOL



    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Bonita Montero@Bonita.Montero@gmail.com to comp.lang.c on Sun Jun 8 10:06:58 2025
    From Newsgroup: comp.lang.c

    Am 07.06.2025 um 21:56 schrieb wij:

    I see std::filesystem as evidence that C++ finally admits the deficiency of its advance error handling system (std::exception). But the result is worse
    than C.

    That's just a mere assertion without any facts. In fact, exception
    handling makes error handling possible with a fraction of the code
    length, because most parts of the code don't need to handle errors,
    whereas in C they do. In C every call level has to deal with erorrs,
    whereas in C++ only one level at the upper edge has to catch the
    errors.

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Muttley@Muttley@DastardlyHQ.org to comp.lang.c on Sun Jun 8 08:55:52 2025
    From Newsgroup: comp.lang.c

    On Sun, 8 Jun 2025 10:06:58 +0200
    Bonita Montero <Bonita.Montero@gmail.com> wibbled:
    Am 07.06.2025 um 21:56 schrieb wij:

    I see std::filesystem as evidence that C++ finally admits the deficiency of >> its advance error handling system (std::exception). But the result is worse
    than C.

    That's just a mere assertion without any facts. In fact, exception
    handling makes error handling possible with a fraction of the code
    length, because most parts of the code don't need to handle errors,
    whereas in C they do. In C every call level has to deal with erorrs,
    whereas in C++ only one level at the upper edge has to catch the
    errors.

    You can of course use setjmp & longjmp in C but depending on how many levels
    up you jump they could be more trouble than they're worth. I think I've only ever used them once.

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From wij@wyniijj5@gmail.com to comp.lang.c on Sun Jun 8 22:52:41 2025
    From Newsgroup: comp.lang.c

    On Sun, 2025-06-08 at 10:06 +0200, Bonita Montero wrote:
    Am 07.06.2025 um 21:56 schrieb wij:

    I see std::filesystem as evidence that C++ finally admits the deficiency of its advance error handling system (std::exception). But the result is worse
    than C.

    That's just a mere assertion without any facts.
    I know a bit of the development of std::filesystem. The view of mere 'standard' disregards fact and uses more the 'assertion' criticized.
    In fact, exception
    handling makes error handling possible with a fraction of the code
    length, because most parts of the code don't need to handle errors,
    "dont' need" is illusion, errors are always there, mostly ignored and encouraged
    to ignore by simplification.
    whereas in C they do. In C every call level has to deal with erorrs,
    whereas in C++ only one level at the upper edge has to catch the
    errors.
    C has not hard coded what 'exception' should be. E.g. C can also set an error object and let interested code to handle it in many ways, what's left is impl. issues.
    But, I think the 'throw' mechanism (not std::exception) is good, like many others. 'throw' is more like a soft assert failure, which is no error handling. --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Bonita Montero@Bonita.Montero@gmail.com to comp.lang.c on Sun Jun 8 17:02:08 2025
    From Newsgroup: comp.lang.c

    Am 08.06.2025 um 10:55 schrieb Muttley@DastardlyHQ.org:

    You can of course use setjmp & longjmp in C but depending on how many levels up you jump they could be more trouble than they're worth. I think I've only ever used them once.

    That's makes a lot of work and it's really ugly. And you need global
    jump_buf-s for that.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Bonita Montero@Bonita.Montero@gmail.com to comp.lang.c on Sun Jun 8 17:06:44 2025
    From Newsgroup: comp.lang.c

    Am 08.06.2025 um 16:52 schrieb wij:

    I know a bit of the development of std::filesystem. The view of mere 'standard'
    disregards fact and uses more the 'assertion' criticized.

    Another statement without arguments.

    "dont' need" is illusion, errors are always there, mostly ignored and encouraged
    to ignore by simplification.

    If the code is written to be exception-safe, i.e. it uses
    RAII throughout, then this is easily possible.

    C has not hard coded what 'exception' should be. E.g. C can also set an error object and let interested code to handle it in many ways, what's left is impl.
    issues.

    Are you serious? The fact that the exception type is transported along
    with the exception itself makes things really convenient. This way, the
    stack can be unrolled until the correct exception handler is found.

    But, I think the 'throw' mechanism (not std::exception) is good, like many others. 'throw' is more like a soft assert failure, which is no error handling.

    Totally different - asserts are handled at debug-time.
    Based on this statement, you didn't understand exceptions correctly.

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Muttley@Muttley@DastardlyHQ.org to comp.lang.c on Sun Jun 8 15:08:16 2025
    From Newsgroup: comp.lang.c

    On Sun, 8 Jun 2025 17:02:08 +0200
    Bonita Montero <Bonita.Montero@gmail.com> wibbled:
    Am 08.06.2025 um 10:55 schrieb Muttley@DastardlyHQ.org:

    You can of course use setjmp & longjmp in C but depending on how many levels >> up you jump they could be more trouble than they're worth. I think I've only >> ever used them once.

    That's makes a lot of work and it's really ugly. And you need global >jump_buf-s for that.

    Its no different to C++ exceptions except obviously no destructors are called so there's no chance to do a tidy up at each stack level. Also jumps arn't limited to lower to higher stack frames jumps, they can jump about all over
    the place. Whether thats useful or not I can't say. I've never needed it.

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Bonita Montero@Bonita.Montero@gmail.com to comp.lang.c on Sun Jun 8 17:11:54 2025
    From Newsgroup: comp.lang.c

    Am 07.06.2025 um 23:12 schrieb Janis Papanagnou:

    Context, in the general case, matters. ...

    If you need the context then you catch the exception near where
    it is thrown; but that's not usual, meaning in most cases you
    don't need that context. F.e. when a bad_alloc is thown it dosn't
    matter which allocation failed, it's just enough to know that a
    memory-collapse happened.

    It's been decades that I used C++, but back then in the 1990's
    we did pass error context with the thrown error object.

    You can easily define your own exception object with more context
    but for 95% of all exceptions bad_alloc or system_error fit.

    That's an inherent part of C++'s exception handling. If you use
    standard error classes without context that's of course possible,
    but nothing prevents you to define yet another error class derived
    from some existing error class with additional information. You
    are actually free to do what suits you and your projects best;
    use rudimentary handling, or have a sophisticated error framework,
    or anything in between.
    Most exceptions you throw or catch are bad_allocs or system_errors.
    With that you don't need any context. For the system_error it might
    make sense to catch them nearer to the call level where the error
    occured. Usually that happens with I/O-errors. But that still
    doesn't need the context of the individual I/O-operation.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Bonita Montero@Bonita.Montero@gmail.com to comp.lang.c on Sun Jun 8 17:18:03 2025
    From Newsgroup: comp.lang.c

    Am 08.06.2025 um 17:08 schrieb Muttley@DastardlyHQ.org:

    Its no different to C++ exceptions except obviously no destructors
    are called so there's no chance to do a tidy up at each stack level.

    It's much more complicated than exceptions because the functions where
    the error raises and where the error is "caught" need shared global
    variables. C++ doesn't need that, just throw and catch and everything
    is glued to the runtime.
    And the problem with your solution is: All functions between the setjmp-
    and the longjmp-calls don't have any cleanup, i.e. they don't handle
    back their opened resources to the runtime or the kernel. With RAII in combination with exceptions that's for free.
    Do you really think setjmp() / longjmp() is a competitive solution ?
    You're naive.

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From wij@wyniijj5@gmail.com to comp.lang.c on Mon Jun 9 00:28:34 2025
    From Newsgroup: comp.lang.c

    On Sun, 2025-06-08 at 17:06 +0200, Bonita Montero wrote:
    Am 08.06.2025 um 16:52 schrieb wij:

    I know a bit of the development of std::filesystem. The view of mere 'standard'
    disregards fact and uses more the 'assertion' criticized.

    Another statement without arguments.
    My primary interest of programming is on the theory. From your presented wording, I don't think you can conduct a logical discussion (it seems you continue to ask for logical proof).
    "dont' need" is illusion, errors are always there, mostly ignored and encouraged
    to ignore by simplification.

    If the code is written to be exception-safe, i.e. it uses
    RAII throughout, then this is easily possible.

    C has not hard coded what 'exception' should be. E.g. C can also set an error
    object and let interested code to handle it in many ways, what's left is impl.
    issues.

    Are you serious? The fact that the exception type is transported along
    with the exception itself makes things really convenient. This way, the
    stack can be unrolled until the correct exception handler is found.

    But, I think the 'throw' mechanism (not std::exception) is good, like many others. 'throw' is more like a soft assert failure, which is no error handling.

    Totally different - asserts are handled at debug-time.
    Based on this statement, you didn't understand exceptions correctly.
    It seems your whole idea (and 'fact') is based on C++'s propaganda.
    Most of the related discussion have happened in the past I am lazy to repeat. Just look at the fact, C++ is half-dying and accelerating (IMO, not because
    of bad but of trillions ways doing it wrong).
    You are repeating past errors and think your understanding and coding is orthodox enough to be factually correct.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From scott@scott@slp53.sl.home (Scott Lurndal) to comp.lang.c on Sun Jun 8 16:55:50 2025
    From Newsgroup: comp.lang.c

    Muttley@DastardlyHQ.org writes:
    On Sun, 8 Jun 2025 17:02:08 +0200
    Bonita Montero <Bonita.Montero@gmail.com> wibbled:
    Am 08.06.2025 um 10:55 schrieb Muttley@DastardlyHQ.org:

    You can of course use setjmp & longjmp in C but depending on how many levels
    up you jump they could be more trouble than they're worth. I think I've only
    ever used them once.

    That's makes a lot of work and it's really ugly. And you need global >>jump_buf-s for that.

    Its no different to C++ exceptions except obviously no destructors are called >so there's no chance to do a tidy up at each stack level. Also jumps arn't >limited to lower to higher stack frames jumps, they can jump about all over >the place. Whether thats useful or not I can't say. I've never needed it.


    I've used sigsetjmp/siglongjmp in C++ code, very successfully. As an experiment a few
    years ago, I replaced it with C++ exceptions and took a 20% performance
    hit in the application. Needless to say the experiment was a failure.

    Note that the code in question[*] is performance sensitive and doesn't do dynamic resource allocations (or use STL at all), thus the fact that
    C++ unwinding will execute destructors isn't relevent to this application.

    The primary cause of the performance hit was the extra code generated
    to handle stack unwinding.

    [*] Which models a CPU, longjmp is used when an modeled CPU exception
    (memory fault, arithmetic fault, interrupt, etc) is detected
    to return to the instruction dispatch loop.

    In general, I tend to concur with wij - I prefer to handle run-of-the-mill errors when they're detected.

    For example:

    c_file_card_unit::c_file_card_unit(ulong channel, ulong unit, const char *name,
    c_logger *lp, c_card_dlp *dlp, bool punch,
    bool hollerith, const char *binpath)
    : c_card_unit(channel, unit, lp, dlp, punch)
    {
    int flags = punch?O_RDWR|O_APPEND:O_RDONLY;
    int diag;
    uint8 header[CARD_SIZE_COLUMNS];

    f_file = NULL;
    f_inputhopper = 0ul;
    f_binfd = -1;
    snprintf(f_binary_path, sizeof(f_binary_path), "%s", binpath);

    diag = stat(name, &cu_stat);
    if (diag == -1) {
    if ((errno == ENOENT) && punch) {
    flags |= O_CREAT|O_EXCL;
    }
    }

    cu_fd = open(name, flags, 0600);
    if (cu_fd == -1) {
    fprintf(stdout, "%4.4lu/%2.2lu Unable to open '%s': %s\n",
    cu_channel, cu_unit, name, strerror(errno));
    return;
    }

    diag = fcntl(cu_fd, F_SETFD, FD_CLOEXEC);
    if (diag == -1) {
    lp->log("%4.4lu/%2.2lu Unable to set FD_CLOEXEC on '%s': %s\n",
    cu_channel, cu_unit, name, strerror(errno));
    diag = close(cu_fd);
    cu_fd = -1;
    return;
    }

    ...
    }

    bool
    c_file_card_unit::is_ready(void)
    {
    return (cu_fd != -1) && ((f_file != NULL) && !feof(f_file));
    }

    So, if is_ready() returns false after the constructor runs,
    the creator of the object knows the creation failed.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From scott@scott@slp53.sl.home (Scott Lurndal) to comp.lang.c on Sun Jun 8 16:58:24 2025
    From Newsgroup: comp.lang.c

    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 07.06.2025 um 23:12 schrieb Janis Papanagnou:

    Context, in the general case, matters. ...

    If you need the context then you catch the exception near where
    it is thrown; but that's not usual, meaning in most cases you
    don't need that context. F.e. when a bad_alloc is thown it dosn't
    matter which allocation failed, it's just enough to know that a >memory-collapse happened.

    Actually it very much matters where the allocation failed, if
    one wishes to recover from it. It seems your concept of error
    handling is simply reporting it (hopefully with sufficient
    context information for the user to understand what needs to
    be fixed) and calling exit().
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Kaz Kylheku@643-408-1753@kylheku.com to comp.lang.c on Sun Jun 8 17:45:47 2025
    From Newsgroup: comp.lang.c

    On 2025-06-08, Scott Lurndal <scott@slp53.sl.home> wrote:
    Muttley@DastardlyHQ.org writes:
    On Sun, 8 Jun 2025 17:02:08 +0200
    Bonita Montero <Bonita.Montero@gmail.com> wibbled:
    Am 08.06.2025 um 10:55 schrieb Muttley@DastardlyHQ.org:

    You can of course use setjmp & longjmp in C but depending on how many levels
    up you jump they could be more trouble than they're worth. I think I've only
    ever used them once.

    That's makes a lot of work and it's really ugly. And you need global >>>jump_buf-s for that.

    Its no different to C++ exceptions except obviously no destructors are called >>so there's no chance to do a tidy up at each stack level. Also jumps arn't >>limited to lower to higher stack frames jumps, they can jump about all over >>the place. Whether thats useful or not I can't say. I've never needed it.


    I've used sigsetjmp/siglongjmp in C++ code, very successfully. As an experiment a few
    years ago, I replaced it with C++ exceptions and took a 20% performance
    hit in the application. Needless to say the experiment was a failure.

    setjmp and longjmp have a clearly defined implementation model
    (obviously not in ISO C, but so in implementation practice).

    Implementations of setjmp do not stray far from the paradigm of just
    saving a bunch of registers in an array, which are then 'blindly"
    restored.

    This model gives you certain performance characteristics that you can
    rely on. Both saving the state and doing the longjmp are fairly cheap.

    In C++, the tradeoffs are going to be a crapshoot: whether it is
    expensive to set up a try block, but cheap to throw, or vice versa
    and that sort of thing.

    A C++ throw typically has to search for the exit point to which control
    will be transferred. (Maybe not in some statically analyzable cases,
    like throw and catch being all in the same function.)

    In general, I tend to concur with wij - I prefer to handle run-of-the-mill errors when they're detected.

    But errors can't always be handled where they are detected. That's why,
    in the first place, you are receiving the error from the function that
    failed! That function couldn't handle it, so it tossed it upward to you.

    The alternative to exceptions is to detect errors at multiple levels
    and pass up whatever you can't handle.


    For example:

    c_file_card_unit::c_file_card_unit(ulong channel, ulong unit, const char *name,
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

    OK, this is a constructor.

    So, if is_ready() returns false after the constructor runs,
    the creator of the object knows the creation failed.

    Right, just like the caller of open() knows that creation
    failed, because -1 was returned.

    The error was only partially handled by c_file_card_unit in so far
    is that it ended up in a documented state, which now something
    else has to handle.
    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c on Sun Jun 8 11:48:45 2025
    From Newsgroup: comp.lang.c

    On 6/8/2025 9:58 AM, Scott Lurndal wrote:
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 07.06.2025 um 23:12 schrieb Janis Papanagnou:

    Context, in the general case, matters. ...

    If you need the context then you catch the exception near where
    it is thrown; but that's not usual, meaning in most cases you
    don't need that context. F.e. when a bad_alloc is thown it dosn't
    matter which allocation failed, it's just enough to know that a
    memory-collapse happened.

    Actually it very much matters where the allocation failed, if
    one wishes to recover from it.

    Exactly. If an allocation failed in some of my older server code, 20
    years ago for sure. Well, it would make the server go into a sort of
    "panic mode" and dump cache, dump timed out connections, ect, then it
    would try the allocation again. If that failed, it would go into shit
    hit the fan mode... ;^)


    Another fun aspect is in panic mode, instead of dumping timedout
    connections that are deemed worthy, I send them a special packet. It
    says, disconnect from me and connect to my friend server, here is the
    address and port.



    It seems your concept of error
    handling is simply reporting it (hopefully with sufficient
    context information for the user to understand what needs to
    be fixed) and calling exit().

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Bonita Montero@Bonita.Montero@gmail.com to comp.lang.c on Sun Jun 8 22:05:59 2025
    From Newsgroup: comp.lang.c

    Am 08.06.2025 um 18:28 schrieb wij:

    My primary interest of programming is on the theory. ...

    Then don't talk about practical issues.

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Bonita Montero@Bonita.Montero@gmail.com to comp.lang.c on Sun Jun 8 22:07:13 2025
    From Newsgroup: comp.lang.c

    Am 08.06.2025 um 18:55 schrieb Scott Lurndal:

    I've used sigsetjmp/siglongjmp in C++ code, very successfully. As an experiment a few
    years ago, I replaced it with C++ exceptions and took a 20% performance
    hit in the application. Needless to say the experiment was a failure.

    Exceptions aren't for the regular case but for exceptions. I.e. if you
    have an I/O error or there's not enough memory. The time to handle this
    cases doesn't matter.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Bonita Montero@Bonita.Montero@gmail.com to comp.lang.c on Sun Jun 8 22:09:07 2025
    From Newsgroup: comp.lang.c

    Am 08.06.2025 um 18:58 schrieb Scott Lurndal:

    Actually it very much matters where the allocation failed, if
    one wishes to recover from it.

    This very rarely makes sense. It's almost always the case that if
    an operation fails, it doesn't matter what allocation was behind
    it.

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From wij@wyniijj5@gmail.com to comp.lang.c on Mon Jun 9 06:51:32 2025
    From Newsgroup: comp.lang.c

    On Sun, 2025-06-08 at 22:05 +0200, Bonita Montero wrote:
    Am 08.06.2025 um 18:28 schrieb wij:

    My primary interest of programming is on the theory. ...

    Then don't talk about practical issues.
    Where did I talk about theory?
    It is you who talked like you want a theoretic proof.
    (I have no problem with that except I don't think you will understand).
    And now, you cut the context off to indicate I only interested in theory (imagination).
    Your several posts indicate that your fact is actually propaganda and reject other
    facts (practice).
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From wij@wyniijj5@gmail.com to comp.lang.c on Mon Jun 9 07:13:34 2025
    From Newsgroup: comp.lang.c

    On Sun, 2025-06-08 at 16:55 +0000, Scott Lurndal wrote:

    For example:

    c_file_card_unit::c_file_card_unit(ulong channel, ulong unit, const char *name,
                                       c_logger *lp, c_card_dlp *dlp, bool punch,
                                       bool hollerith, const char *binpath)
        : c_card_unit(channel, unit, lp, dlp, punch)
    {
        int flags = punch?O_RDWR|O_APPEND:O_RDONLY;
        int diag;
        uint8 header[CARD_SIZE_COLUMNS];

        f_file        = NULL;
        f_inputhopper = 0ul;
        f_binfd       = -1;
        snprintf(f_binary_path, sizeof(f_binary_path), "%s", binpath);

        diag = stat(name, &cu_stat);
        if (diag == -1) {
            if ((errno == ENOENT) && punch) {
                flags |= O_CREAT|O_EXCL;
            }
        }

        cu_fd = open(name, flags, 0600);
        if (cu_fd == -1) {
            fprintf(stdout, "%4.4lu/%2.2lu Unable to open '%s': %s\n",                 cu_channel, cu_unit, name, strerror(errno));         return;
        }

        diag = fcntl(cu_fd, F_SETFD, FD_CLOEXEC);
        if (diag == -1) {
            lp->log("%4.4lu/%2.2lu Unable to set FD_CLOEXEC on '%s': %s\n",
                          cu_channel, cu_unit, name, strerror(errno));
            diag = close(cu_fd);
            cu_fd = -1;
            return;
        }

    ...
    }

    I would like just share my comment of the code (just skim the code).

    Assume c_file_card_unit(..) is a normal function:
    1. It should be obvious throwing is no good than returning error.
    2. Error is issued in severl places, post condition is a concern.
    3. Error handling itself (code and propagation) has to (or better be) error free.
    4. Cancellation point/Async-signal safe issues.

    bool
    c_file_card_unit::is_ready(void)
    {
        return (cu_fd != -1) && ((f_file != NULL) && !feof(f_file));
    }

    So, if is_ready() returns false after the constructor runs,
    the creator of the object knows the creation failed.

    If the code in C++ constructor, returning error encountered is no good, 'contract' is broken.
    A ctor like this should be considered too complicated but fine in non-serious code.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Lawrence D'Oliveiro@ldo@nz.invalid to comp.lang.c on Mon Jun 9 07:59:05 2025
    From Newsgroup: comp.lang.c

    On Sun, 8 Jun 2025 10:06:58 +0200, Bonita Montero wrote:

    In C every call level has to deal with erorrs, whereas in C++ only
    one level at the upper edge has to catch the errors.

    Not necessarily that simple. It might be easier if C++ had
    try/finally (like Python does), but it doesn’t.

    Here’s a patch I submitted to the Blender project some years ago to
    fix up their directory-scan code to gracefully recover from errors
    without either memory leaks or double-frees. This code is in C. It’s
    all a matter of structured programming.

    <https://projects.blender.org/blender/blender/src/commit/edf4855a38e3ee24874431f7c6321f04cb6d1b2f>
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Muttley@Muttley@DastardlyHQ.org to comp.lang.c on Mon Jun 9 09:20:30 2025
    From Newsgroup: comp.lang.c

    On Sun, 8 Jun 2025 17:18:03 +0200
    Bonita Montero <Bonita.Montero@gmail.com> wibbled:
    Am 08.06.2025 um 17:08 schrieb Muttley@DastardlyHQ.org:

    Its no different to C++ exceptions except obviously no destructors
    are called so there's no chance to do a tidy up at each stack level.

    It's much more complicated than exceptions because the functions where
    the error raises and where the error is "caught" need shared global >variables. C++ doesn't need that, just throw and catch and everything
    is glued to the runtime.
    And the problem with your solution is: All functions between the setjmp-
    and the longjmp-calls don't have any cleanup, i.e. they don't handle
    back their opened resources to the runtime or the kernel. With RAII in >combination with exceptions that's for free.
    Do you really think setjmp() / longjmp() is a competitive solution ?
    You're naive.

    Did you actually read what I wrote? Obviously in C++ you'd use exceptions
    but in C without any kind of built in exception mechanism they're better than nothing if you want to jump out of a deep stack.

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Muttley@Muttley@DastardlyHQ.org to comp.lang.c on Mon Jun 9 10:21:42 2025
    From Newsgroup: comp.lang.c

    On Sun, 08 Jun 2025 16:55:50 GMT
    scott@slp53.sl.home (Scott Lurndal) wibbled:
    In general, I tend to concur with wij - I prefer to handle run-of-the-mill >errors when they're detected.

    Same here. I think with exceptions the clue is in the name. They should really only be used for exceptional circumstances, not as a general minor error handling mechanism.


    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Bonita Montero@Bonita.Montero@gmail.com to comp.lang.c on Mon Jun 9 12:58:07 2025
    From Newsgroup: comp.lang.c

    Am 09.06.2025 um 11:20 schrieb Muttley@DastardlyHQ.org:

    Did you actually read what I wrote? Obviously in C++ you'd use exceptions
    but in C without any kind of built in exception mechanism they're better
    than nothing if you want to jump out of a deep stack.
    I ask myself if setjmp() / longjmp() with manual unwinding then is
    cheaper than to just have error-returns.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Bonita Montero@Bonita.Montero@gmail.com to comp.lang.c on Mon Jun 9 13:01:37 2025
    From Newsgroup: comp.lang.c

    Am 09.06.2025 um 12:21 schrieb Muttley@DastardlyHQ.org:

    Same here. I think with exceptions the clue is in the name. They should really
    only be used for exceptional circumstances, not as a general minor error handling mechanism.

    In Java they're also used for things that are more likely. But although
    Java is significantly slower than C++, exceptions are a magnitude faster
    than in C++.
    That's while with current table-driven exception handling does need to
    find the handler for the callers return address. As shared libraries
    might be loaded and unloaded asynchronously you've to ask the kernel
    for the image of every return address, and that's slow.

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Bonita Montero@Bonita.Montero@gmail.com to comp.lang.c on Mon Jun 9 13:04:45 2025
    From Newsgroup: comp.lang.c

    Am 09.06.2025 um 09:59 schrieb Lawrence D'Oliveiro:

    Not necessarily that simple. It might be easier if C++ had
    try/finally (like Python does), but it doesn’t.

    The initial thought with C++ exceptions is that all the unrolling
    happens with RAII-classe so that you don't need finally. But some-
    times you won't write a class for each resource you hold. For this
    cases a helper-class like experimental::scope_exit is very comfor-
    table. Unfortunately that's still not in the standard so that I
    had to write it on my own:

    #pragma once
    #include <utility>
    #include <concepts>
    #include "nui.h"

    template<std::invocable Fn>
    struct defer final
    {
    defer( Fn &&fn ) :
    m_enabled( true ),
    m_fn( std::forward<Fn>( fn ) )
    {
    }
    defer( defer const & ) = delete;
    void operator =( defer const & ) = delete;
    ~defer()
    {
    if( m_enabled ) [[likely]]
    m_fn();
    }
    void operator ()()
    {
    if( !m_enabled ) [[unlikely]]
    return;
    m_fn();
    m_enabled = false;
    }
    void disable()
    {
    m_enabled = false;
    }
    private:
    bool m_enabled;
    NO_UNIQUE_ADDRESS Fn m_fn;
    };

    template<std::invocable ... Fns>
    inline void disable( defer<Fns> &... defs )
    {
    (defs.disable(), ...);
    }

    template<std::invocable Fn, std::invocable FnNext = Fn>
    struct xdefer final
    {
    xdefer( Fn &&fn, xdefer<FnNext> *next = nullptr ) :
    m_enabled( true ),
    m_next( next ),
    m_fn( std::forward<Fn>( fn ) )
    {
    }
    xdefer( xdefer const & ) = delete;
    void operator =( xdefer const & ) = delete;
    ~xdefer()
    {
    bool enabled = m_enabled;
    if( m_next ) [[likely]]
    m_next->m_enabled = enabled;
    if( enabled ) [[likely]]
    m_fn();
    }
    void operator ()()
    {
    if( !m_enabled ) [[unlikely]]
    return;
    m_fn();
    m_enabled = false;
    if( !m_next ) [[unlikely]]
    return;
    m_next->m_enabled = true;
    (*m_next)();
    }
    void disable()
    {
    m_enabled = false;
    }
    private:
    template<std::invocable Fn1, std::invocable Fn2>
    friend struct xdefer;
    bool m_enabled;
    xdefer<FnNext> *m_next;
    NO_UNIQUE_ADDRESS Fn m_fn;
    };
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From scott@scott@slp53.sl.home (Scott Lurndal) to comp.lang.c on Mon Jun 9 14:01:25 2025
    From Newsgroup: comp.lang.c

    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 08.06.2025 um 18:58 schrieb Scott Lurndal:

    Actually it very much matters where the allocation failed, if
    one wishes to recover from it.

    This very rarely makes sense. It's almost always the case that if
    an operation fails, it doesn't matter what allocation was behind
    it.

    Have you ever written real-world production code? Like an operating
    system, where allocation failures should -never- result in an
    inability to recover.

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Bonita Montero@Bonita.Montero@gmail.com to comp.lang.c on Mon Jun 9 17:24:28 2025
    From Newsgroup: comp.lang.c

    Am 09.06.2025 um 16:01 schrieb Scott Lurndal:

    Have you ever written real-world production code? Like an operating system, where allocation failures should -never- result in an
    inability to recover.

    If you need an allocation to proceed and it fails you can't recover.


    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From scott@scott@slp53.sl.home (Scott Lurndal) to comp.lang.c on Mon Jun 9 15:53:19 2025
    From Newsgroup: comp.lang.c

    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 09.06.2025 um 16:01 schrieb Scott Lurndal:

    Have you ever written real-world production code? Like an operating
    system, where allocation failures should -never- result in an
    inability to recover.

    If you need an allocation to proceed and it fails you can't recover.

    That's your problem caused by poor design and implementation. Exacerbated
    by the propensity for you to use C++ features that require dynamic
    allocation where other forms of data structures are more suitable.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Bonita Montero@Bonita.Montero@gmail.com to comp.lang.c on Mon Jun 9 19:45:32 2025
    From Newsgroup: comp.lang.c

    Am 09.06.2025 um 17:53 schrieb Scott Lurndal:
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 09.06.2025 um 16:01 schrieb Scott Lurndal:

    Have you ever written real-world production code? Like an operating
    system, where allocation failures should -never- result in an
    inability to recover.

    If you need an allocation to proceed and it fails you can't recover.

    That's your problem caused by poor design and implementation.

    That's how 100% of all programs that deal with bad_alloc are designed.

    Exacerbated by the propensity for you to use C++ features that require dynamic allocation where other forms of data structures are more suitable.

    When dynamic allocation is needed it is needed.


    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Richard Heathfield@rjh@cpax.org.uk to comp.lang.c on Mon Jun 9 19:22:03 2025
    From Newsgroup: comp.lang.c

    On 09/06/2025 16:53, Scott Lurndal wrote:
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 09.06.2025 um 16:01 schrieb Scott Lurndal:

    Have you ever written real-world production code? Like an operating
    system, where allocation failures should -never- result in an
    inability to recover.

    If you need an allocation to proceed and it fails you can't recover.

    That's your problem caused by poor design and implementation.

    To be fair, that's how the schools teach them to cut code: if you
    drop a piton, jump off the mountain.
    --
    Richard Heathfield
    Email: rjh at cpax dot org dot uk
    "Usenet is a strange place" - dmr 29 July 1999
    Sig line 4 vacant - apply within

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From scott@scott@slp53.sl.home (Scott Lurndal) to comp.lang.c on Mon Jun 9 18:33:38 2025
    From Newsgroup: comp.lang.c

    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 09.06.2025 um 17:53 schrieb Scott Lurndal:
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 09.06.2025 um 16:01 schrieb Scott Lurndal:

    Have you ever written real-world production code? Like an operating >>>> system, where allocation failures should -never- result in an
    inability to recover.

    If you need an allocation to proceed and it fails you can't recover.

    That's your problem caused by poor design and implementation.

    That's how 100% of all programs that deal with bad_alloc are designed.

    Exacerbated by the propensity for you to use C++ features that require
    dynamic allocation where other forms of data structures are more suitable.

    When dynamic allocation is needed it is needed.

    And there are many ways to handle it that don't include throwing
    bad_alloc when the system is unable to provide additional address
    space, memory or backing store.

    Allocating major data structures at application start (perhaps using a
    pool allocator) and crafting your algorithms such that they
    don't require infinite memory is a good start.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Bonita Montero@Bonita.Montero@gmail.com to comp.lang.c on Mon Jun 9 20:39:17 2025
    From Newsgroup: comp.lang.c

    Am 09.06.2025 um 20:33 schrieb Scott Lurndal:

    And there are many ways to handle it that don't include throwing
    bad_alloc when the system is unable to provide additional address
    space, memory or backing store.

    These ways are all less comfortable than bad_alloc.

    Allocating major data structures at application start (perhaps using
    a pool allocator) and crafting your algorithms such that they
    don't require infinite memory is a good start.

    With modern allocators like mimalloc allocation is that fast that
    you mostly can't compete with your own pools.

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Tim Rentsch@tr.17687@z991.linuxsc.com to comp.lang.c on Mon Jun 9 18:59:27 2025
    From Newsgroup: comp.lang.c

    scott@slp53.sl.home (Scott Lurndal) writes:

    Bonita Montero <Bonita.Montero@gmail.com> writes:

    Am 08.06.2025 um 18:58 schrieb Scott Lurndal:

    Actually it very much matters where the allocation failed, if
    one wishes to recover from it.

    This very rarely makes sense. It's almost always the case that if
    an operation fails, it doesn't matter what allocation was behind
    it.

    Have you ever written real-world production code? Like an operating system, where allocation failures should -never- result in an
    inability to recover.

    You're talking to someone who can't understand the difference
    between comp.lang.c and comp.lang.c++. What do you expect?
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Muttley@Muttley@DastardlyHQ.org to comp.lang.c on Tue Jun 10 07:21:32 2025
    From Newsgroup: comp.lang.c

    On Mon, 09 Jun 2025 18:33:38 GMT
    scott@slp53.sl.home (Scott Lurndal) wibbled:
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 09.06.2025 um 17:53 schrieb Scott Lurndal:
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 09.06.2025 um 16:01 schrieb Scott Lurndal:

    Have you ever written real-world production code? Like an operating >>>>> system, where allocation failures should -never- result in an
    inability to recover.

    If you need an allocation to proceed and it fails you can't recover.

    That's your problem caused by poor design and implementation.

    That's how 100% of all programs that deal with bad_alloc are designed.

    Exacerbated by the propensity for you to use C++ features that require
    dynamic allocation where other forms of data structures are more suitable. >>
    When dynamic allocation is needed it is needed.

    And there are many ways to handle it that don't include throwing
    bad_alloc when the system is unable to provide additional address
    space, memory or backing store.

    Allocating major data structures at application start (perhaps using a
    pool allocator) and crafting your algorithms such that they
    don't require infinite memory is a good start.

    Ugh. Then you end up like the Java JVM - grabbing boatloads of memory that causes huge startup delays and can often cause the machine to do lots of swapping and/or slow everything else down to treacle.

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Bonita Montero@Bonita.Montero@gmail.com to comp.lang.c on Tue Jun 10 09:51:12 2025
    From Newsgroup: comp.lang.c

    Am 10.06.2025 um 03:59 schrieb Tim Rentsch:

    You're talking to someone who can't understand the difference
    between comp.lang.c and comp.lang.c++. What do you expect?

    Comparisons between C and any other lanugage are on-topic here. ;-)
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Tim Rentsch@tr.17687@z991.linuxsc.com to comp.lang.c on Tue Jun 10 03:21:52 2025
    From Newsgroup: comp.lang.c

    Kaz Kylheku <643-408-1753@kylheku.com> writes:

    On 2025-06-08, Scott Lurndal <scott@slp53.sl.home> wrote:

    Muttley@DastardlyHQ.org writes:

    On Sun, 8 Jun 2025 17:02:08 +0200
    Bonita Montero <Bonita.Montero@gmail.com> wibbled:

    Am 08.06.2025 um 10:55 schrieb Muttley@DastardlyHQ.org:

    You can of course use setjmp & longjmp in C but depending on how
    many levels up you jump they could be more trouble than they're
    worth. I think I've only ever used them once.

    That's makes a lot of work and it's really ugly. And you need
    global jump_buf-s for that.

    Nonsense.

    [...]

    setjmp and longjmp have a clearly defined implementation model
    (obviously not in ISO C, but so in implementation practice).

    What I think you mean is that setjmp/longjmp have a natural and
    straightforward implementation strategy that works on many
    hardware platforms and in many C implementations. That strategy
    is not ubiquitous, and TTBOMK is not defined anywhere except
    implicitly by the code that implements it. Described perhaps,
    but that isn't the same as a definition.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Bonita Montero@Bonita.Montero@gmail.com to comp.lang.c on Tue Jun 10 14:07:40 2025
    From Newsgroup: comp.lang.c

    Am 03.06.2025 um 02:37 schrieb Lawrence D'Oliveiro:
    On Mon, 2 Jun 2025 09:35:24 +0200, Bonita Montero wrote:

    Am 01.06.2025 um 09:43 schrieb Lawrence D'Oliveiro:

    On Sun, 1 Jun 2025 07:58:54 +0200, Bonita Montero wrote:

    Sth. like this:

    for( directory_entry const &de : recursive_directory_iterator( "\\", >>>> directory_options::follow_directory_symlink ) )
    cout << de.path() << endl;

    You need the dirfd functions to avoid certain potential security
    holes on operations with symlinks.

    Which security holes ?

    TOCTOU.

    That's unavoidable with directory-operations. The file might have been
    changed or deleted as you process a directory entry. Unfortunately there
    are no really transactional filesystems, although they would be only
    a bit slower.

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From scott@scott@slp53.sl.home (Scott Lurndal) to comp.lang.c on Tue Jun 10 13:19:24 2025
    From Newsgroup: comp.lang.c

    Muttley@DastardlyHQ.org writes:
    On Mon, 09 Jun 2025 18:33:38 GMT
    scott@slp53.sl.home (Scott Lurndal) wibbled:
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 09.06.2025 um 17:53 schrieb Scott Lurndal:
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 09.06.2025 um 16:01 schrieb Scott Lurndal:

    Have you ever written real-world production code? Like an operating >>>>>> system, where allocation failures should -never- result in an
    inability to recover.

    If you need an allocation to proceed and it fails you can't recover.

    That's your problem caused by poor design and implementation.

    That's how 100% of all programs that deal with bad_alloc are designed.

    Exacerbated by the propensity for you to use C++ features that require >>>> dynamic allocation where other forms of data structures are more suitable. >>>
    When dynamic allocation is needed it is needed.

    And there are many ways to handle it that don't include throwing
    bad_alloc when the system is unable to provide additional address
    space, memory or backing store.

    Allocating major data structures at application start (perhaps using a
    pool allocator) and crafting your algorithms such that they
    don't require infinite memory is a good start.

    Ugh. Then you end up like the Java JVM - grabbing boatloads of memory that >causes huge startup delays and can often cause the machine to do lots of >swapping and/or slow everything else down to treacle.

    That's a problem with host not being suitable for java, if that
    is the behavior you are seeing. I've not seen that in production
    java-based applications that are competently developed.

    For C/C++, one generally allocates page-aligned regions with mmap, eschewing granular allocation methods such as new/delete/malloc.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Tim Rentsch@tr.17687@z991.linuxsc.com to comp.lang.c on Tue Jun 10 07:18:50 2025
    From Newsgroup: comp.lang.c

    scott@slp53.sl.home (Scott Lurndal) writes:

    [...]

    And all the existing C compilers in the entire planet support
    the C90 dialect[*], if so instructed. Where is the problem?

    It is common to use the word "dialect" when talking about
    different editions of the C standard, but actually it isn't
    right. The word "dialect" comes from linguistics, and it
    has a particular meaning that does not apply in this case.
    My understanding of the terminology used in linguistics is
    that C90, C99, C11, and so forth, should be referred to
    as "varieties" of the C language, in much the same way
    that American English and British English and Indian
    English are all different varieties (rather than dialects)
    of English.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From scott@scott@slp53.sl.home (Scott Lurndal) to comp.lang.c on Tue Jun 10 14:36:04 2025
    From Newsgroup: comp.lang.c

    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 03.06.2025 um 02:37 schrieb Lawrence D'Oliveiro:
    On Mon, 2 Jun 2025 09:35:24 +0200, Bonita Montero wrote:

    Am 01.06.2025 um 09:43 schrieb Lawrence D'Oliveiro:

    On Sun, 1 Jun 2025 07:58:54 +0200, Bonita Montero wrote:

    Sth. like this:

    for( directory_entry const &de : recursive_directory_iterator( "\\", >>>>> directory_options::follow_directory_symlink ) )
    cout << de.path() << endl;

    You need the dirfd functions to avoid certain potential security
    holes on operations with symlinks.

    Which security holes ?

    TOCTOU.

    That's unavoidable with directory-operations.

    Incorrect. The entire purpose of the POSIX *at functions are to close
    those security holes.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Bonita Montero@Bonita.Montero@gmail.com to comp.lang.c on Tue Jun 10 16:40:28 2025
    From Newsgroup: comp.lang.c

    Am 10.06.2025 um 16:36 schrieb Scott Lurndal:

    That's unavoidable with directory-operations.

    Incorrect. The entire purpose of the POSIX *at functions are to close
    those security holes.

    You confirm me and say I'm incorrect incorrect - in the same sentence ?

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Muttley@Muttley@DastardlyHQ.org to comp.lang.c on Tue Jun 10 14:46:28 2025
    From Newsgroup: comp.lang.c

    On Tue, 10 Jun 2025 13:19:24 GMT
    scott@slp53.sl.home (Scott Lurndal) wibbled:
    Muttley@DastardlyHQ.org writes:
    Ugh. Then you end up like the Java JVM - grabbing boatloads of memory that >>causes huge startup delays and can often cause the machine to do lots of >>swapping and/or slow everything else down to treacle.

    That's a problem with host not being suitable for java, if that
    is the behavior you are seeing. I've not seen that in production
    java-based applications that are competently developed.

    For C/C++, one generally allocates page-aligned regions with mmap, eschewing >granular allocation methods such as new/delete/malloc.

    No, one generally doesn't. Why on earth would anyone bother with their own
    low level memory allocation based on pages unless they were writing something like a database? Plus I'm sure most modern implementations of malloc() are sophisticated enough not to just allocate random blocks of memory but have their own pool behind the scenes which they manage.

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Tim Rentsch@tr.17687@z991.linuxsc.com to comp.lang.c on Tue Jun 10 08:52:50 2025
    From Newsgroup: comp.lang.c

    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:

    Tim Rentsch <tr.17687@z991.linuxsc.com> writes:

    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:

    Richard Harnden <richard.harnden@gmail.invalid> writes:

    On 22/05/2025 23:32, Keith Thompson wrote:

    [...]

    In one of your library's headers:
    extern const char ESCAPE;
    In the corresponding *.c file:
    const char ESCAPE = ('z' - 'a' == 25 ? '\x1b' : '\x27');
    Change the name if you prefer.

    Wouldn't that be a reserved identifier?

    Yes, it would. Good catch.

    (Identifiers starting with E followed by either a digit or an
    uppercase letter are reserved; they could be defined as macros
    in <errno.h>.)

    They are reserved only as macros, and only if <errno.h> has
    been #include'd.

    For this particular use, it's easy to make the definition work,
    simply by adding

    #undef ESCAPE

    before the declaration in the header file, and before the
    definition in the source file (assuming of course that if
    there are any #include <errno.h> they precede the #undef's).

    It would be even easier to pick a different name.

    The point of my comment was to help explain the rules about what
    macro names are reserved and under what circumstances, not to
    suggest a way to avoid conflicts.

    A better way to avoid conflicts with E* macros is to take functions
    where errno is needed, as for example signal(), and not call them
    directly but rather wrap each one in a function, with the wrapping
    functions put in (one or more) separate translation unit(s). Those
    translation units, and only those translation units, are the ones
    where a #include <errno.h> is done. Some details are needed to keep
    the separation complete, but I think those aren't too hard to work
    out, so if someone has trouble please ask. This way most of the
    program can use names beginning with E that might otherwise be
    reserved, without any fear of conflicts. There is a bit of source
    code overhead, but that is paid only once, across all projects that
    use this approach. Also there are some other benefits, related to
    libraries used that are not part of ISO C, such as Posix, which
    again should be readily apparent to anyone used to working in large
    projects that use such libraries.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Kaz Kylheku@643-408-1753@kylheku.com to comp.lang.c on Tue Jun 10 16:52:11 2025
    From Newsgroup: comp.lang.c

    On 2025-06-10, Scott Lurndal <scott@slp53.sl.home> wrote:
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 03.06.2025 um 02:37 schrieb Lawrence D'Oliveiro:
    On Mon, 2 Jun 2025 09:35:24 +0200, Bonita Montero wrote:

    Am 01.06.2025 um 09:43 schrieb Lawrence D'Oliveiro:

    On Sun, 1 Jun 2025 07:58:54 +0200, Bonita Montero wrote:

    Sth. like this:

    for( directory_entry const &de : recursive_directory_iterator( "\\", >>>>>> directory_options::follow_directory_symlink ) )
    cout << de.path() << endl;

    You need the dirfd functions to avoid certain potential security
    holes on operations with symlinks.

    Which security holes ?

    TOCTOU.

    That's unavoidable with directory-operations.

    Incorrect. The entire purpose of the POSIX *at functions are to close
    those security holes.

    Simply by using a path coming from an untrusted input, you have
    a securty problem that no POSIX function addresses.

    I seem to recall that the "at" functions are mainly motivated by multithreading.

    A process has a single working directory, inherited from a time before
    threads. The "at" function can be used to defensively code file system
    access in a thread, even if there is code in the process which uses
    chdir.
    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c on Tue Jun 10 12:08:40 2025
    From Newsgroup: comp.lang.c

    On 6/10/2025 6:19 AM, Scott Lurndal wrote:
    Muttley@DastardlyHQ.org writes:
    On Mon, 09 Jun 2025 18:33:38 GMT
    scott@slp53.sl.home (Scott Lurndal) wibbled:
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 09.06.2025 um 17:53 schrieb Scott Lurndal:
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 09.06.2025 um 16:01 schrieb Scott Lurndal:

    Have you ever written real-world production code? Like an operating >>>>>>> system, where allocation failures should -never- result in an
    inability to recover.

    If you need an allocation to proceed and it fails you can't recover. >>>>>
    That's your problem caused by poor design and implementation.

    That's how 100% of all programs that deal with bad_alloc are designed. >>>>
    Exacerbated by the propensity for you to use C++ features that require >>>>> dynamic allocation where other forms of data structures are more suitable.

    When dynamic allocation is needed it is needed.

    And there are many ways to handle it that don't include throwing
    bad_alloc when the system is unable to provide additional address
    space, memory or backing store.

    Allocating major data structures at application start (perhaps using a
    pool allocator) and crafting your algorithms such that they
    don't require infinite memory is a good start.

    Ugh. Then you end up like the Java JVM - grabbing boatloads of memory that >> causes huge startup delays and can often cause the machine to do lots of
    swapping and/or slow everything else down to treacle.

    That's a problem with host not being suitable for java, if that
    is the behavior you are seeing. I've not seen that in production
    java-based applications that are competently developed.

    For C/C++, one generally allocates page-aligned regions with mmap, eschewing granular allocation methods such as new/delete/malloc.

    Humm... However, one can choose to carve out allocations from the
    region? Actually, this basically reminds me of my older region allocator
    code. So, this is basically dynamic within our defined region, so to
    speak. However, back in the day, I would try to recover from dynamic
    malloc failures... Just for testing the robustness of my system. Fwiw,
    here is my old region code:

    https://groups.google.com/g/comp.lang.c/c/7oaJFWKVCTw/m/sSWYU9BUS_QJ

    If it fails to allocate, it simply means the region is exhausted at that
    time. Then my code can fall back to dumping connections, freeing cache,
    and try the allocation again? Fair enough? It was a long time ago, and
    when I was conducting said experiments. Fwiw, here is a direct link to
    my old region code that is, well, fun to me at least... :^)

    https://pastebin.com/raw/f37a23918

    Does it still work for anybody? ;^o
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From antispam@antispam@fricas.org (Waldek Hebisch) to comp.lang.c on Tue Jun 10 20:22:29 2025
    From Newsgroup: comp.lang.c

    Scott Lurndal <scott@slp53.sl.home> wrote:
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 09.06.2025 um 17:53 schrieb Scott Lurndal:
    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 09.06.2025 um 16:01 schrieb Scott Lurndal:

    Have you ever written real-world production code? Like an operating >>>>> system, where allocation failures should -never- result in an
    inability to recover.

    If you need an allocation to proceed and it fails you can't recover.

    That's your problem caused by poor design and implementation.

    That's how 100% of all programs that deal with bad_alloc are designed.

    Exacerbated by the propensity for you to use C++ features that require
    dynamic allocation where other forms of data structures are more suitable. >>
    When dynamic allocation is needed it is needed.

    And there are many ways to handle it that don't include throwing
    bad_alloc when the system is unable to provide additional address
    space, memory or backing store.

    Allocating major data structures at application start (perhaps using a
    pool allocator) and crafting your algorithms such that they
    don't require infinite memory is a good start.

    If you can allocate memory before looking at data, then you really
    do not need dynamic allocation. And there are cases when you can
    do with something simpler than general dynamic allocation.

    But AFAICS there are cases which need general dynamic allocation.
    And there are cases which strictly speaking do not need general
    dynamic allocation, but dynamic allocation looks better in
    practice. Namely, one can estimate amount of memory that is
    sufficient to do the job, but this estimate is typically
    significantly bigger than amount of memory which would be
    dynamically allocated. In other words, users prefer
    "stochastic" version, that is program which usually is
    efficient, but with low probability may fail or require more
    resources to a "deterministic" program that always
    require more resources.

    There are now in use programs that solve high complexity
    problems. On many practical problems they are quite
    efficient, but by the nature of problem they need
    inpracticaly large resources on some instances. For
    such problem the best one can hope is to localize the
    trouble, that is fail the computation if it requires
    to much, but keep independent things running. And even
    such localized failure may be tricky (that is avoiding
    trouble in unrelated places).
    --
    Waldek Hebisch
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Tue Jun 10 13:35:19 2025
    From Newsgroup: comp.lang.c

    Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
    Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
    Richard Harnden <richard.harnden@gmail.invalid> writes:
    On 22/05/2025 23:32, Keith Thompson wrote:
    [...]

    In one of your library's headers:
    extern const char ESCAPE;
    In the corresponding *.c file:
    const char ESCAPE = ('z' - 'a' == 25 ? '\x1b' : '\x27');
    Change the name if you prefer.

    Wouldn't that be a reserved identifier?

    Yes, it would. Good catch.

    (Identifiers starting with E followed by either a digit or an
    uppercase letter are reserved; they could be defined as macros
    in <errno.h>.)

    They are reserved only as macros, and only if <errno.h> has
    been #include'd.

    For this particular use, it's easy to make the definition work,
    simply by adding

    #undef ESCAPE

    before the declaration in the header file, and before the
    definition in the source file (assuming of course that if
    there are any #include <errno.h> they precede the #undef's).

    It would be even easier to pick a different name.

    The point of my comment was to help explain the rules about what
    macro names are reserved and under what circumstances, not to
    suggest a way to avoid conflicts.

    And the point of my comment was to suggest a way to avoid conflicts.

    A better way to avoid conflicts with E* macros is to take functions
    where errno is needed, as for example signal(), and not call them
    directly but rather wrap each one in a function, with the wrapping
    functions put in (one or more) separate translation unit(s). Those translation units, and only those translation units, are the ones
    where a #include <errno.h> is done. Some details are needed to keep
    the separation complete, but I think those aren't too hard to work
    out, so if someone has trouble please ask. This way most of the
    program can use names beginning with E that might otherwise be
    reserved, without any fear of conflicts. There is a bit of source
    code overhead, but that is paid only once, across all projects that
    use this approach. Also there are some other benefits, related to
    libraries used that are not part of ISO C, such as Posix, which
    again should be readily apparent to anyone used to working in large
    projects that use such libraries.

    That doesn't strike me as better. You're suggesting a substantial reorganization to avoid some simple name conflicts (identifiers
    starting with 'E'). I find it much easier -- and yes, better --
    to avoid defining identifiers starting with 'E'. (Actually only
    identifiers starting with 'E' followed by either a digit or an
    uppercase letter are reserved. It's simpler to avoid all identifiers
    starting with 'E', but you can safely use something like "Escape"
    if you like.)

    If there are other reasons for such an organization, that's fine.

    (My personal policy is to assume a more expansive set of reserved
    identifiers, because it means I only have to remember a simpler
    set of rules. For example, I find it easier to avoid defining any
    identifiers starting with '_' than to account for the rules for which
    _* identifiers are reserved for which purposes in which contexts.
    Of course I'll look up and apply the actual rules when I need to.)

    I find the reservation of potential errno macro names annoying.
    Using and reserving identifiers starting with "E_", for example,
    would have been less intrusive. But we're stuck with it.
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Kaz Kylheku@643-408-1753@kylheku.com to comp.lang.c on Tue Jun 10 21:34:23 2025
    From Newsgroup: comp.lang.c

    On 2025-06-10, Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    I find the reservation of potential errno macro names annoying.

    If the standard contained /no/ statements about what a given header
    file may reserve, then /any/ identifier whatsoever would be a potential
    clash.

    The errno reservation is kind of good because implementors often extend
    the set of errno constants. POSIX has a lot more of them than ISO C, and
    there are some vendor-specific ones.

    Anyway, you can safely ignore the reservation theatre, and just
    deal with clashes that happen, when they happen. (If you're lucky,
    that could just be never).

    Anyway, ISO C, POSIX and vendors have historically introduced new
    identifiers in spaces that were not previously declared as reserved.
    If you're ever hit by that, you will feel like a completel sucker if
    you've religiously adhered to namespaces from your end.
    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Tue Jun 10 15:09:44 2025
    From Newsgroup: comp.lang.c

    Kaz Kylheku <643-408-1753@kylheku.com> writes:
    On 2025-06-10, Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    I find the reservation of potential errno macro names annoying.

    If the standard contained /no/ statements about what a given header
    file may reserve, then /any/ identifier whatsoever would be a potential clash.

    The errno reservation is kind of good because implementors often extend
    the set of errno constants. POSIX has a lot more of them than ISO C, and there are some vendor-specific ones.

    Anyway, you can safely ignore the reservation theatre, and just
    deal with clashes that happen, when they happen. (If you're lucky,
    that could just be never).

    You can do that, but a new clash could happen when your code is
    compiled on a system that defines an errno macro that you haven't
    seen before.

    Anyway, ISO C, POSIX and vendors have historically introduced new
    identifiers in spaces that were not previously declared as reserved.
    If you're ever hit by that, you will feel like a completel sucker if
    you've religiously adhered to namespaces from your end.

    Yes, that can happen, but no, I won't feel like a complete sucker.

    If I define my own strfoo() function and a new edition of the standard
    defines strfoo() in <string.h>, the clash is my fault,and I could have
    avoided it by defining str_foo().

    Nothing can prevent all possible name clashes, but I like to follow the
    rules in the standard that let me prevent some of them.
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Lawrence D'Oliveiro@ldo@nz.invalid to comp.lang.c on Tue Jun 10 23:33:08 2025
    From Newsgroup: comp.lang.c

    On Tue, 10 Jun 2025 14:07:40 +0200, Bonita Montero wrote:

    Am 03.06.2025 um 02:37 schrieb Lawrence D'Oliveiro:

    On Mon, 2 Jun 2025 09:35:24 +0200, Bonita Montero wrote:

    Am 01.06.2025 um 09:43 schrieb Lawrence D'Oliveiro:

    On Sun, 1 Jun 2025 07:58:54 +0200, Bonita Montero wrote:

    Sth. like this:

    for( directory_entry const &de : recursive_directory_iterator( "\
    \",
    directory_options::follow_directory_symlink ) )
    cout << de.path() << endl;

    You need the dirfd functions to avoid certain potential security
    holes on operations with symlinks.

    Which security holes ?

    TOCTOU.

    That's unavoidable with directory-operations.

    No it isn’t. That’s why we have the fd-based calls in recent POSIX, and in Linux. That plugs the holes, and makes it possible to implement privileged file-access software like Samba securely.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Kaz Kylheku@643-408-1753@kylheku.com to comp.lang.c on Wed Jun 11 01:16:11 2025
    From Newsgroup: comp.lang.c

    On 2025-06-10, Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    Kaz Kylheku <643-408-1753@kylheku.com> writes:
    On 2025-06-10, Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    I find the reservation of potential errno macro names annoying.

    If the standard contained /no/ statements about what a given header
    file may reserve, then /any/ identifier whatsoever would be a potential
    clash.

    The errno reservation is kind of good because implementors often extend
    the set of errno constants. POSIX has a lot more of them than ISO C, and
    there are some vendor-specific ones.

    Anyway, you can safely ignore the reservation theatre, and just
    deal with clashes that happen, when they happen. (If you're lucky,
    that could just be never).

    You can do that, but a new clash could happen when your code is
    compiled on a system that defines an errno macro that you haven't
    seen before.

    A new CRASH could happen too, and any number of things.

    This clash would be literally one of the first roadblocks, if not the
    first one, that we would see when just exploring the possibility whether
    our code might not run on that system.

    At that point we could be months away from declaring that a supported
    platform.


    Anyway, ISO C, POSIX and vendors have historically introduced new
    identifiers in spaces that were not previously declared as reserved.
    If you're ever hit by that, you will feel like a completel sucker if
    you've religiously adhered to namespaces from your end.

    Yes, that can happen, but no, I won't feel like a complete sucker.

    If I define my own strfoo() function and a new edition of the standard defines strfoo() in <string.h>, the clash is my fault,and I could have avoided it by defining str_foo().

    But, my point is, that maybe you could have called it kidneybeans() and
    still have a clash.

    In fact, if you are writing new string functions that are the same sort
    of stuff like the standard ones, you should use the str prefix,
    for consistency.

    If your code is influential enough, they might be standardized one
    day---and then they are ready with the proper naming, so early adopters
    of your functions won't have to change anything; just in their build
    system drop the third party code and switch to what their library now
    provides.
    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Keith Thompson@Keith.S.Thompson+u@gmail.com to comp.lang.c on Tue Jun 10 19:11:09 2025
    From Newsgroup: comp.lang.c

    Kaz Kylheku <643-408-1753@kylheku.com> writes:
    On 2025-06-10, Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    Kaz Kylheku <643-408-1753@kylheku.com> writes:
    On 2025-06-10, Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    I find the reservation of potential errno macro names annoying.

    If the standard contained /no/ statements about what a given header
    file may reserve, then /any/ identifier whatsoever would be a potential
    clash.

    The errno reservation is kind of good because implementors often extend
    the set of errno constants. POSIX has a lot more of them than ISO C, and >>> there are some vendor-specific ones.

    Anyway, you can safely ignore the reservation theatre, and just
    deal with clashes that happen, when they happen. (If you're lucky,
    that could just be never).

    You can do that, but a new clash could happen when your code is
    compiled on a system that defines an errno macro that you haven't
    seen before.

    A new CRASH could happen too, and any number of things.

    Sure, but a compilation failure is more likely.

    This clash would be literally one of the first roadblocks, if not the
    first one, that we would see when just exploring the possibility whether
    our code might not run on that system.

    At that point we could be months away from declaring that a supported platform.

    Sure, but why not skip that first roadblock?

    Anyway, ISO C, POSIX and vendors have historically introduced new
    identifiers in spaces that were not previously declared as reserved.
    If you're ever hit by that, you will feel like a completel sucker if
    you've religiously adhered to namespaces from your end.

    Yes, that can happen, but no, I won't feel like a complete sucker.

    If I define my own strfoo() function and a new edition of the standard
    defines strfoo() in <string.h>, the clash is my fault,and I could have
    avoided it by defining str_foo().

    But, my point is, that maybe you could have called it kidneybeans() and
    still have a clash.

    Certainly, I've already acknowledged that.

    But surely an identifier that the standard says is reserved is less
    likely to cause a clash than one that isn't.

    In fact, if you are writing new string functions that are the same sort
    of stuff like the standard ones, you should use the str prefix,
    for consistency.

    If your code is influential enough, they might be standardized one
    day---and then they are ready with the proper naming, so early adopters
    of your functions won't have to change anything; just in their build
    system drop the third party code and switch to what their library now provides.

    I see your point, but ... meh. To me, a name matching "str[a-z]*"
    fairly strongly suggests a function declared in <string.h> in ISO C.
    Of course it's not an absolute rule; see strlcpy(), for example
    (which is now part of POSIX).

    Even a str*() identifier that never clashes with ISO C's <string.h>
    can still clash with POSIX, or glibc, or BSD, or ....

    I would have been happier if POSIX were less intrusive on the
    C standard library, but the way C and POSIX evolved didn't make
    that feasible.
    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Bonita Montero@Bonita.Montero@gmail.com to comp.lang.c on Wed Jun 11 07:07:37 2025
    From Newsgroup: comp.lang.c

    Am 11.06.2025 um 01:33 schrieb Lawrence D'Oliveiro:

    No it isn’t. That’s why we have the fd-based calls in recent POSIX, and in
    Linux. That plugs the holes, and makes it possible to implement privileged file-access software like Samba securely.

    opendir() and readdir() hasn't changed for decades.

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Lawrence D'Oliveiro@ldo@nz.invalid to comp.lang.c on Wed Jun 11 05:34:59 2025
    From Newsgroup: comp.lang.c

    On Wed, 11 Jun 2025 07:07:37 +0200, Bonita Montero wrote:

    Am 11.06.2025 um 01:33 schrieb Lawrence D'Oliveiro:

    No it isn’t. That’s why we have the fd-based calls in recent POSIX, and >> in Linux. That plugs the holes, and makes it possible to implement
    privileged file-access software like Samba securely.

    opendir() and readdir() hasn't changed for decades.

    And they are not enough.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From scott@scott@slp53.sl.home (Scott Lurndal) to comp.lang.c on Wed Jun 11 13:41:11 2025
    From Newsgroup: comp.lang.c

    Bonita Montero <Bonita.Montero@gmail.com> writes:
    Am 11.06.2025 um 01:33 schrieb Lawrence D'Oliveiro:

    No it isn’t. That’s why we have the fd-based calls in recent POSIX, and in
    Linux. That plugs the holes, and makes it possible to implement privileged >> file-access software like Samba securely.

    opendir() and readdir() hasn't changed for decades.


    That turns out to not be true. fdopendir(3) was added in Issue
    7 of the Single Unix Specification (aka POSIX).

    https://pubs.opengroup.org/onlinepubs/9799919799/functions/fdopendir.html

    ftw(3)/nftw(3) are used more often than opendir(3) in real world code.

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Kaz Kylheku@643-408-1753@kylheku.com to comp.lang.c on Wed Jun 11 15:23:16 2025
    From Newsgroup: comp.lang.c

    On 2025-06-11, Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    Kaz Kylheku <643-408-1753@kylheku.com> writes:
    On 2025-06-10, Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    Kaz Kylheku <643-408-1753@kylheku.com> writes:
    On 2025-06-10, Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    I find the reservation of potential errno macro names annoying.

    If the standard contained /no/ statements about what a given header
    file may reserve, then /any/ identifier whatsoever would be a potential >>>> clash.

    The errno reservation is kind of good because implementors often extend >>>> the set of errno constants. POSIX has a lot more of them than ISO C, and >>>> there are some vendor-specific ones.

    Anyway, you can safely ignore the reservation theatre, and just
    deal with clashes that happen, when they happen. (If you're lucky,
    that could just be never).

    You can do that, but a new clash could happen when your code is
    compiled on a system that defines an errno macro that you haven't
    seen before.

    A new CRASH could happen too, and any number of things.

    Sure, but a compilation failure is more likely.

    This clash would be literally one of the first roadblocks, if not the
    first one, that we would see when just exploring the possibility whether
    our code might not run on that system.

    At that point we could be months away from declaring that a supported
    platform.

    Sure, but why not skip that first roadblock?

    Because:

    1. The roadblock is not a sure thing; in fact it is improbable.

    2. The severity/impact is trivial.

    3. There are dowsides to skipping it:

    - tiny amount of effort, which occurs with 100% probability.

    - giving up a desired name.

    Anyway, ISO C, POSIX and vendors have historically introduced new
    identifiers in spaces that were not previously declared as reserved.
    If you're ever hit by that, you will feel like a completel sucker if
    you've religiously adhered to namespaces from your end.

    Yes, that can happen, but no, I won't feel like a complete sucker.

    If I define my own strfoo() function and a new edition of the standard
    defines strfoo() in <string.h>, the clash is my fault,and I could have
    avoided it by defining str_foo().

    But, my point is, that maybe you could have called it kidneybeans() and
    still have a clash.

    Certainly, I've already acknowledged that.

    But surely an identifier that the standard says is reserved is less
    likely to cause a clash than one that isn't.

    The probabilities are not precisely quantified, but low.

    The probability that your #define ELMER_FUDD will clash with something
    out of <errno.h> is vanishingly low, as is the probability that #define BUGS_BUNNY will clash with something in spite of not intruding into
    a reserved namespace.

    We have no way to quantitfy the probabilities, and they are so low that
    even if one could be determined to be 100 times the other, why
    prioritize the concern over that difference over any other naming consideration? (Like that maybe Looney Tunes characters aren't very good

    In fact, if you are writing new string functions that are the same sort
    of stuff like the standard ones, you should use the str prefix,
    for consistency.

    If your code is influential enough, they might be standardized one
    day---and then they are ready with the proper naming, so early adopters
    of your functions won't have to change anything; just in their build
    system drop the third party code and switch to what their library now
    provides.

    I see your point, but ... meh. To me, a name matching "str[a-z]*"
    fairly strongly suggests a function declared in <string.h> in ISO C.
    Of course it's not an absolute rule; see strlcpy(), for example
    (which is now part of POSIX).

    Note that the guys who made strlcpy did (almost) exactly what I say
    above. They deliberately introduced the function into that namespace,
    which made it more nicely 'standardizable". I say almost because they
    are systems implementors working on OpenBSD, and so they are using the namespace as designed. However, the use of those functions spread beyond OpenBSD; application codebases started defining those for themselves.

    Even a str*() identifier that never clashes with ISO C's <string.h>
    can still clash with POSIX, or glibc, or BSD, or ....

    I would have been happier if POSIX were less intrusive on the
    C standard library, but the way C and POSIX evolved didn't make
    that feasible.

    But POSIX has to use *some* kind of names. No matter what you call a
    function, it's going to start with either an underscore, or an upper or
    lower case letter. So if it doesn't intrude into the _* space, it will
    intrude into a*. Or else if not into that one, then b*, ... and so on. Everything is in some space, and could step on something in an
    application.

    Religiously sticking to certain spaces can uglify the API. Imagine
    everything newer than 1990 in POSIX were called posix_<something>.
    Yuck! (The whole pthread_* thing is actually curious that way).

    Namespaces offer a solution. They are not available in C, and in many
    ways are a solution in search of a problem.

    It's amazing how far C development is going, into code bases of millions
    of lines and even orders of magnitude more, without a package system.

    What helps is that there are no clashes between:

    - static symbols in separate translation units.
    - symbols in separate programs
    - unexported symbols among shared libraries

    It's also amazing how just even a small naming convention reduces
    the occurrences of clashes. E.g. four letter prefix of your company's
    acronym, and you're mostly good. Writing code for Broadcom? Just
    call things brcm_*.

    Speaking of POSIX, again, if you use ALL_CAPS variable names in shell
    scripts, Makefiles, or your environment you are in treading in reserved namespace! Yet everyone deos this all the time and the sky does not
    fall.

    Just because POSIX says that it will not define lower-cased environment varaibles doesn't mean everyone should rush to define their own
    variables there.

    The "user" and "vendor" roles can be fluid. You might define a
    lower cased environment variable, thinking you are a user, in order not
    to clash with vendors. Next thing you know, you have users who are
    buying a complete system solution for you, including your app with
    lower-cased environment variables. You're now "vendor" and doing it
    wrong.
    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Kaz Kylheku@643-408-1753@kylheku.com to comp.lang.c on Wed Jun 11 15:32:06 2025
    From Newsgroup: comp.lang.c

    On 2025-06-10, Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    I find the reservation of potential errno macro names annoying.

    Perhaps more annoying is the idea that all _t typedef names are reserved
    by POSIX.

    But, you have to look at it from a different point of view. POSIX
    essentially says that when it introduces types names, they will stick a
    _t suffix on them. I.e. that they will conform with a common practice.
    This is good! We want that!

    That doesn't mean everyone should panic and abandon the same good
    practice.

    Suppose two computer philosophers live on separate islands and are
    thinking of what to name a type. One comes up with "foo" and the
    other with "bar". If they meet and integrate their code, there will
    not be a clash.

    Now suppose that they are conscious of conventions; because they
    are naming a type, they both add "_t".

    That, ipso facto, is not going to *introduce* a clash!

    Everyone adding a common suffix to a class of identifiers will
    not introduce clashes in that class. Likewise, removing a common suffix
    will not introduce a clash.

    Now sure, if organization A uses _t, and B entirely avoids it, their
    type names cannot clash; velvet_underground and velvet_underground_t
    are distinct. But that's a bad reading of the situation.

    I want types in C programs to have _t whether they are coming
    from POSIX, third party vendors, or the local code base.
    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Bonita Montero@Bonita.Montero@gmail.com to comp.lang.c on Wed Jun 11 17:33:21 2025
    From Newsgroup: comp.lang.c

    Am 11.06.2025 um 15:41 schrieb Scott Lurndal:

    That turns out to not be true. fdopendir(3) was added in Issue
    7 of the Single Unix Specification (aka POSIX). https://pubs.opengroup.org/onlinepubs/9799919799/functions/fdopendir.html ftw(3)/nftw(3) are used more often than opendir(3) in real world code.

    That doesn't make a difference for the discussed point.

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Chris M. Thomasson@chris.m.thomasson.1@gmail.com to comp.lang.c on Wed Jun 11 11:57:41 2025
    From Newsgroup: comp.lang.c

    On 6/10/2025 3:09 PM, Keith Thompson wrote:
    Kaz Kylheku <643-408-1753@kylheku.com> writes:
    On 2025-06-10, Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    I find the reservation of potential errno macro names annoying.

    If the standard contained /no/ statements about what a given header
    file may reserve, then /any/ identifier whatsoever would be a potential
    clash.

    The errno reservation is kind of good because implementors often extend
    the set of errno constants. POSIX has a lot more of them than ISO C, and
    there are some vendor-specific ones.

    Anyway, you can safely ignore the reservation theatre, and just
    deal with clashes that happen, when they happen. (If you're lucky,
    that could just be never).

    You can do that, but a new clash could happen when your code is
    compiled on a system that defines an errno macro that you haven't
    seen before.

    Anyway, ISO C, POSIX and vendors have historically introduced new
    identifiers in spaces that were not previously declared as reserved.
    If you're ever hit by that, you will feel like a completel sucker if
    you've religiously adhered to namespaces from your end.

    Yes, that can happen, but no, I won't feel like a complete sucker.

    If I define my own strfoo() function and a new edition of the standard defines strfoo() in <string.h>, the clash is my fault,and I could have avoided it by defining str_foo().

    Nothing can prevent all possible name clashes, but I like to follow the
    rules in the standard that let me prevent some of them.


    Wrt to prepending to artificially create a namespace in C, ct, ala
    ct_*_*_* is fine with me. For your new addition how about a keith_thompson_*_*_* for your namespace? Or even a kt_* ? ;^)
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Lawrence D'Oliveiro@ldo@nz.invalid to comp.lang.c on Thu Jun 12 01:33:18 2025
    From Newsgroup: comp.lang.c

    On Wed, 11 Jun 2025 17:33:21 +0200, Bonita Montero wrote:

    [fdopendir] doesn't make a difference for the discussed point.

    It’s a key part of the defence against symlink-related vulnerabilities.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Tim Rentsch@tr.17687@z991.linuxsc.com to comp.lang.c on Wed Jun 11 20:15:26 2025
    From Newsgroup: comp.lang.c

    Richard Heathfield <rjh@cpax.org.uk> writes:

    On 28/05/2025 13:41, Tim Rentsch wrote:

    Richard Heathfield <rjh@cpax.org.uk> writes:

    On 24/05/2025 06:32, Tim Rentsch wrote:

    Richard Heathfield <rjh@cpax.org.uk> writes:

    On 23/05/2025 13:43, Tim Rentsch wrote:

    Richard Heathfield <rjh@cpax.org.uk> writes:

    On 20/05/2025 10:18, Keith Thompson wrote:

    C90 will never be extended.

    And for that reason it will always be valuable. Stability
    has a value all its own.

    C99 is just as stable as C90, and has been for well over a
    decade.

    Sure, but it's a different stable.

    If it were the same stable, it would be C90.

    C99 isn't C90, therefore it isn't the same stable.

    If you tell me C99 is a rock, I will not doubt you. But the C90
    rock it most certainly isn't.

    Now you're being silly.

    No, sir. If you want to play that game, you can play it with
    yourself. I know that you are perfectly capable of polite
    conversation, so I see no reason to endure the opposite.

    I don't think I'm being impolite.

    Then we are again in disagreement, and it seems obvious that it would
    be foolishly optimistic of me to expect to be able to resolve the
    matter. I'll tell Thunderbird to leave it there.

    Apparently my words were taken with a meaning I didn't intend.
    I'm sorry that this happened.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Tim Rentsch@tr.17687@z991.linuxsc.com to comp.lang.c on Tue Jul 1 10:09:11 2025
    From Newsgroup: comp.lang.c

    Richard Heathfield <rjh@cpax.org.uk> writes:

    On 30/05/2025 10:20, David Brown wrote:

    On 29/05/2025 14:38, Richard Heathfield wrote:

    This really is a very simple point, but perhaps a simple analogy
    will help to clarify it. You don't throw out your 3/4" just
    because you've bought a 19mm. There is room for both in the
    toolbox, and why write 3/4" on your new spanner? It /isn't/ a
    3/4" spanner even though it's very like it, so why pretend
    otherwise?

    Your analogy does not cover C99 vs C90.

    It does if we can construct a program that is legal C90 but not
    legal C99, which is easy enough, or (slightly harder but still not
    that hard) a program that is legal in both dialects but which
    gives different output under C99 than it does for C90.

    $ cat c9099.c; \
    gcc -W -Wall -ansi -pedantic -o c90 c9099.c; \
    gcc -o c99 c9099.c; \
    ./c90; \
    ./c99
    #include <stdio.h>

    int main(void)
    {
    int a = 42;

    int b = a //* comment */ 6;
    ;
    printf("Soln = %d\n", b);

    return 0;
    }
    Soln = 7
    Soln = 42

    It's a straightfoward exercise to write a program that discovers
    whether a given .c file has this potential ambiguity. Of course,
    essentially no actual source code will, but the point is the
    problem is easy to detect, and also correct.

    This observation suggests a related question. Is there a source
    of potential ambiguity between C90 and C99 that is not so easy to
    detect and correct? I can think of one, but it falls into the
    realm of depending on implementation-specific behavior, so could
    just as easily be seen as a portability issue rather than a
    language version issue.

    Obviously it's a contrived example, but then examples pointing out
    the consequences of language differences invariably are.

    Some are. Some are not. In the case of C90 and C99, probably
    all such cases are contrived, but certainly for other language
    pairs there are cases that might arise unintentionally, even for
    similar languages (notably C and C++).
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Jakob Bohm@egenagwemdimtapsar@jbohm.dk to comp.lang.c on Sun Aug 17 21:04:11 2025
    From Newsgroup: comp.lang.c

    On 2025-05-21 12:00, Paul Edwards wrote:
    "Lawrence D'Oliveiro" <ldo@nz.invalid> wrote in message news:100jhor$2lgt3$4@dont-email.me...
    On Wed, 21 May 2025 10:23:27 +1000, Paul Edwards wrote:

    ...

    The C90 standard deferred to MVS - probably still does -
    and says that you can't open a file as "w", then read it as
    "rb" and write (a new file) as "wb", and still access (the
    new file) with "r".

    I was shocked when I saw IBM's C library lose the newlines
    when I did the above, and went to look at the standard to
    show that IBM was violating C90 - but it turns out they
    weren't.

    That sort of means you can't write a "zip" program portably,
    against the theoretical C90 file system. Or you would have
    to have flags to say which files need to be opened as text
    or binary.


    I believe the Info-Zip group's ZIP program overcame this problem, by
    somehow enhancing the same feature that handles the difference between
    line endings on UNIX (LF), CP/M (CRLF) and MacOsClassic (CR), but I
    haven't checked .

    Also, the file system peculiarity is probably the same one I experienced
    when transferring some of my old work from VM/CMS to MS-DOS decades ago.

    I do not agree with IBM's C library, and PDPCLIB does
    not have that behavior, so that constraint could potentially
    be dropped in a C90+ standard.

    BFN. Paul.




    Enjoy

    Jakob
    --
    Jakob Bohm, MSc.Eng., I speak only for myself, not my company
    This public discussion message is non-binding and may contain errors
    All trademarks and other things belong to their owners, if any.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Jakob Bohm@egenagwemdimtapsar@jbohm.dk to comp.lang.c on Sun Aug 17 22:34:30 2025
    From Newsgroup: comp.lang.c

    On 2025-05-22 07:14, Keith Thompson wrote:
    Lawrence D'Oliveiro <ldo@nz.invalid> writes:
    On Thu, 22 May 2025 02:20:36 +0200, Jakob Bohm wrote:
    The later UNIX-like file system NTFS ...

    It was (and is) hard to describe NTFS as “Unix-like”. Yes, it had
    hierarchical directories and long(ish) file names, but not much else.
    Drive letters were inherited (indirectly) from DEC OSes, of all things,
    along with an insistence on having filename extensions, restrictions on
    characters allowed in names etc.

    I consider NTFS UNIX-like because it is built around inodes (called MFT entries) and inode numbers, with no inherent special treatment of MSDOS metacharacters in the fundamental logic, except the few places that
    parse user supplied path strings such as symlink targets or passed
    through path strings from API calls .

    When the CP/M style directory listing operations are done on an NTFS directory, the NTFS code loads the file names and inode number from the directory storage, then checks the inode to fill in details such as
    MS-DOS file attributes and POSIX-style time stamps (using 64 bit time
    since 1600-01-01 00:00:00 GMT), next the user mode API logic converts filenames to the locale character set, discards the unwanted time stamps
    and convert the rest to the API encoding (which may be MS-DOS API locale
    time since 1980 or POSIX time since 1970 or Win32 FILETIME which is same
    as NTFS time).


    I don't believe that NTFS requires filename extensions.
    My understanding is that a file name is stored as a single string
    (with some restrictions).

    Symlinks were not even added until Windows Vista. And you have to have
    special privileges to create them.


    The mechanism for symlinks (NTFS reparse points) was included from the
    start, but exposure of that to user mode was always limited . I'm
    unsure if the crippled POSIX subsystem in the NT 3.10 release included symlinks or only hardlinks .


    Enjoy

    Jakob
    --
    Jakob Bohm, MSc.Eng., I speak only for myself, not my company
    This public discussion message is non-binding and may contain errors
    All trademarks and other things belong to their owners, if any.
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Kaz Kylheku@643-408-1753@kylheku.com to comp.lang.c on Mon Aug 18 00:30:35 2025
    From Newsgroup: comp.lang.c

    On 2025-05-21, Paul Edwards <mutazilah@gmail.com> wrote:
    Do note one more thing.

    The C90 standard deferred to MVS - probably still does -
    and says that you can't open a file as "w", then read it as
    "rb" and write (a new file) as "wb", and still access (the
    new file) with "r".

    You mean:

    - write a text file, close it; then
    - open it as a binary file and copy the bytes to another, new binary file; and - finally, read the new binary file in text mode?

    I don't see how that would be allowed to lose any newlines.

    You made a bitwise copy of the file.

    The worst thing that can happen is this: stdio implementations are not
    required to keep the exact length of a binary file down to a byte.

    Binary files can be rounded up and have padding bytes at the end.

    E.g. you write 37 byte, but the file ends up 256 bytes long.

    If an implementation has this issue, it probably will still represent
    text files in such a way that when you copy a binary file, including
    any gratuitous padding, the text file will come out right.

    A recent draft of ISO C says "A binary stream is an ordered sequence of characters that can transparently record internal data. Data read in
    from a binary stream shall compare equal to the data that were earlier
    written out to that stream, under the same implementation. Such a stream
    may, however, have an implementation- defined number of null characters appended to the end of the stream."

    I have C90 somewhere, I can look that up too, but I suspect it was
    the same.

    I was shocked when I saw IBM's C library lose the newlines
    when I did the above, and went to look at the standard to
    show that IBM was violating C90 - but it turns out they
    weren't.

    Losing the newlines in the above scenario (bit copy made of text
    file as a binary file) makes no sense.

    If it is true, someone went out of their way to fuck up something
    simple. (IBM would never do that, right?)

    If we copy a text file as binary and newlines change it suggests that
    the implementation is falling afoul of the "binary stream is an
    ordered sequence of characters that can transparently record
    internal data".

    That sort of means you can't write a "zip" program portably,
    against the theoretical C90 file system.

    Or you would have
    to have flags to say which files need to be opened as text
    or binary.

    This is probably a good idea anyway; you don't want to be compressing
    the proprietary-format binary images of text files, if they are to
    decompress correctly on another system.
    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From James Kuyper@jameskuyper@alumni.caltech.edu to comp.lang.c on Tue Aug 19 18:09:48 2025
    From Newsgroup: comp.lang.c

    On 2025-08-17 20:30, Kaz Kylheku wrote:
    On 2025-05-21, Paul Edwards <mutazilah@gmail.com> wrote:
    Do note one more thing.

    The C90 standard deferred to MVS - probably still does -
    and says that you can't open a file as "w", then read it as
    "rb" and write (a new file) as "wb", and still access (the
    new file) with "r".

    You mean:

    - write a text file, close it; then
    - open it as a binary file and copy the bytes to another, new binary file; and
    - finally, read the new binary file in text mode?

    I don't see how that would be allowed to lose any newlines.

    You made a bitwise copy of the file.
    ...
    A recent draft of ISO C says "A binary stream is an ordered sequence of characters that can transparently record internal data. Data read in
    from a binary stream shall compare equal to the data that were earlier written out to that stream, under the same implementation. Such a stream
    may, however, have an implementation- defined number of null characters appended to the end of the stream."

    I have C90 somewhere, I can look that up too, but I suspect it was
    the same.

    It was.

    This can interact with text mode, if the representation of new-lines in
    text mode involves null characters. I know of two different ways that
    have actually been used where this could be a problem. One method uses
    null characters to represent a single new-line. The other method stores
    lines in fixed-length blocks, with the end of a line indicated by
    padding to the end of the block with null characters. Either way, the
    padding bytes that may be added in binary mode could be interpreted as
    extra newlines in text mode. That does not match Paul's problem:

    I was shocked when I saw IBM's C library lose the newlines
    when I did the above, and went to look at the standard to
    show that IBM was violating C90 - but it turns out they
    weren't.

    It would help if Paul would identify the clauses from C90 that he
    interpreted as permitting such behavior.
    --- Synchronet 3.21a-Linux NewsLink 1.2