back to article Today's bugs have BRANDS? Be still my bleeding heart [logo]

In view of the ongoing security-holed far-too-open source situation, I have decided to convene an emergency meeting of ERCOCC, the El Reg Committee Of Competent Coders, to review what has occurred and how we should go forward. No time for chitter-chattering. Settle down everybody, and juice up the PowerPoint projector please …

COMMENTS

This topic is closed for new posts.
  1. AndrueC Silver badge
    Flame

    Christ on a crutch. Are there still C++ programmers out there that don't know about RAII? Presumably they've not heard of smart pointers so either their code is leaky or they are spending too much time and effort running resource monitoring tools.

    1. Anonymous Coward
      Anonymous Coward

      RAII is nice, but it's not available in C++ and that's the whole problem.

      1. AndrueC Silver badge
        FAIL

        RAII is nice, but it's not available in C++ and that's the whole problem.

        You couldn't be more wrong. RAII originated in C++. I think you meant that it's not available in C ;)

        1. Paul Crawford Silver badge

          I think gcc supports a variant on the idea, but then you get in to serious portability issues for a library that should be cross-platform and compilable on systems of widely varying age.

          1. AndrueC Silver badge
            Thumb Up

            then you get in to serious portability issues

            Good point. About the only reason to still be using C would be for backward/cross compatibility.

            1. jake Silver badge

              @ AndrueC

              "About the only reason to still be using C would be for backward/cross compatibility"

              So pretty much anything resembling kernel code? And silly little things like browsers, routers, servers, telephones, home entertainment systems, and etc?

              Good old K&R C is still doing the grunt-work, and will be for the foreseeable future.

              1. Phil Endecott

                Re: @ AndrueC

                > Good old K&R C is still doing the grunt-work

                Blimey, no. I haven't seen K&R C for decades. I suspect most C programmers don't even know the syntax. Where are you seeing it?

                1. Anonymous Coward
                  Anonymous Coward

                  Re: @ AndrueC

                  Where are you seeing it?

                  Every time he looks under the hood of his own servers

                  1. Stoneshop

                    Re: @ AndrueC

                    Every time he looks under the hood of his own servers

                    Doubt it. Those would be full of decatrons and mercury delay lines, leaving no room for either Kernighan or Ritchie, let alone both of them together.

                2. jake Silver badge

                  @ Phil Endecott (was: Re: @ AndrueC)

                  "Where are you seeing it?"

                  Damn near everywhere close to the hardware.

                  If you haven't seen K&R C "for decades", you are not a programmer that I would be interested in hiring.

                  1. Anonymous Coward
                    Anonymous Coward

                    Re: @ Phil Endecott (was: @ AndrueC)

                    "If you haven't seen K&R C "for decades", you are not a programmer that I would be interested in hiring."

                    That depends on what we're describing here. If you code in the language described in the 1st edition of 'The C Programming Language', you've missed six separate revisions of the language, and are speaking a dead dialect that might or might not compile as is on modern compilers (haven't tried it, couldn't say). At least if we're talking the language as described in the 2nd edition, we're in ANSI C territory.

                    1. Anonymous Coward
                      Anonymous Coward

                      Re: @ Phil Endecott (was: @ AndrueC)

                      "and are speaking a dead dialect that might or might not compile as is on modern compilers (haven't tried it, couldn't say)."

                      I found that sufficiently interesting to install a little C89 compliant compiler (TCC on Windows) and compile some examples. Everything is fine up until you use functions you've failed to declare.

                      That seems to be an A-OK thing to do in "The C Programming Language 1st Edition" - not so much with an ANSI C compiler. I'm sure this is the tip of the iceberg.

                      So, if we mean 1st edition C when we say "K&R C", that's a bad thing; I wouldn't hire you if you meant that jake ;-) If we mean C89, and its treatment in the 2nd edition, that's fine, send me a CV.

                      1. jake Silver badge

                        @ Andrew Fernie (was: Re: @ Phil Endecott (was: @ AndrueC))

                        Modern GCC still compiles K&R properly. Early and later ANSI, too.

                        The issue you are (trying to) address is programmer savvy.

                        Kiddies don't know how the hardware works anymore. That's the real story.

                        1. Anonymous Coward
                          Anonymous Coward

                          Re: @ Andrew Fernie (was: @ Phil Endecott (was: @ AndrueC))

                          "Modern GCC still compiles K&R properly. Early and later ANSI, too.

                          The issue you are (trying to) address is programmer savvy.

                          Kiddies don't know how the hardware works anymore. That's the real story."

                          That's interesting re: gcc - the '-traditional' switch or similar I'm guessing? The point that rather remains though, is why do you feel that knowing how to program in a dialect of C that nobody uses constitutes programmer savvy, rather than enthusiasm and nostalgia? Genuine question.

                          1. jake Silver badge

                            Re: @ Andrew Fernie (was: @ Phil Endecott (was: @ AndrueC))

                            "The point that rather remains though, is why do you feel that knowing how to program in a dialect of C that nobody uses constitutes programmer savvy, rather than enthusiasm and nostalgia? Genuine question."

                            "Nobody uses K&R"? If that is a serious question, you are an interface user, not a computer user. There is a difference. Apple uses K&R in their implementation of BSD+mach based iOS (as an example). Genuine answer.

                            1. Anonymous Coward
                              Anonymous Coward

                              Re: @ Andrew Fernie (was: @ Phil Endecott (was: @ AndrueC))

                              ""Nobody uses K&R"? If that is a serious question, you are an interface user, not a computer user. There is a difference. Apple uses K&R in their implementation of BSD+mach based iOS (as an example). Genuine answer."

                              Right... you can cite your source, of course?.

                              1. Anonymous Coward
                                Anonymous Coward

                                @ jake

                                ---------------------

                                EXT - DAY:

                                A lone tumbleweed rolls across a featureless expanse.

                                ---------------------

                                I'll take that as a 'no'.

                                1. jake Silver badge

                                  @ Andrew Fernie (was: Re: @ jake)

                                  ::shakes head:: Kids these days. Some of us have work to do, and aren't connected 24/7.

                                  The core of the non-(C+assembler)-Mach bit of Darwin is C. K&R C. The guts are BSD, after all. Yes, userland is C++, but glitter can be written in anything.

                                  1. Anonymous Coward
                                    Anonymous Coward

                                    Re: @ Andrew Fernie (was: @ jake)

                                    "::shakes head:: Kids these days. Some of us have work to do, and aren't connected 24/7."

                                    Said the man with nearly 6500 posts of essentially the same old shit to his name.

                                    "The core of the non-(C+assembler)-Mach bit of Darwin is C. K&R C. The guts are BSD, after all. Yes, userland is C++, but glitter can be written in anything."

                                    Cite your source that the Mach source is written in K&R (and by this I mean prior to C89) C.

                                    1. jake Silver badge

                                      Re: @ Andrew Fernie (was: @ jake)

                                      You know, dude/tte, you are attached to TehIntraWebTubes. It has ::whisper:: "search engines". DYOFDW. You might actually learn something. But that would be scary if it completely stomped all over your (mis)conceptions, now wouldn't it?

                                      Religions are ugly. In all forms.

                                      1. Anonymous Coward
                                        Anonymous Coward

                                        Re: @ Andrew Fernie (was: @ jake)

                                        "You know, dude/tte, you are attached to TehIntraWebTubes. It has ::whisper:: "search engines". DYOFDW. You might actually learn something. But that would be scary if it completely stomped all over your (mis)conceptions, now wouldn't it?

                                        Religions are ugly. In all forms."

                                        So in summary you've been talking out of your backside, you know it, and you're falling back on your usual line of bullshit, acronyms, condescension and insult. I'm not the one making the spurious claim you are, so cite your source.

                                        1. jake Silver badge

                                          Re: @ Andrew Fernie (was: @ jake)

                                          OK, I'll help you figure it out if you really need the help. Go to Wikipedia.

                                          Search on OSX, iOS, BSD, and Mach. But ignore the Wiki articles.

                                          Rather, follow the included links to off-site pages. Read, and try to understand. If you are capable.

                                          That's as far as I am going to hand-feed you.

                                          If you are trolling, that's probably the first time I've been trolled in about a decade. With the exception of ElReg's resident troll Trevor Pott, of course.

                                          1. Anonymous Coward
                                            Anonymous Coward

                                            Re: @ Andrew Fernie (was: @ jake)

                                            Stop stalling and provide one statement, one simple link. Doesn't exist, does it? In the meantime I'll be kind enough to hand-feed you a little further.

                                            "If you are trolling, that's probably the first time I've been trolled in about a decade. With the exception of ElReg's resident troll Trevor Pott, of course."

                                            Sure, you're being trolled if trolling means being asked to to do one, simple reasonable thing in the context of a claim you made that you are now clearly unwilling to back up. That is to cite your.source or withdraw your claim

                                            1. jake Silver badge

                                              Re: @ Andrew Fernie (was: @ jake)

                                              There is no single URL that provides "proof" in this conversation. There are many URLs that provide proof of concept, if you have any clue about reality.

                                              You are clearly not cognizant. HTH, HAND.

                                              1. Anonymous Coward
                                                Anonymous Coward

                                                Re: @ Andrew Fernie (was: @ jake)

                                                "There is no single URL that provides "proof" in this conversation."

                                                I guess that's the closest I'll get to an admission that you were talking rubbish, so I'll settle for that.

      2. Anonymous Coward
        Anonymous Coward

        If it wasn't, I wouldn't be using it. But I am. Enough said.

  2. Admiral Grace Hopper

    Flense

    There's nothing wrong with the word "flense". Removing blubber from any code is to applauded.

    1. Anonymous Coward
      Anonymous Coward

      Re: Flense

      Congratulations, Ma'am, you beat me to it by an hour.

      As a fan of Moby-Dick, I love the idea of securing the code to the side of the ship, attaching a rope from a derrick to the outside, and unrolling the fat. Perhaps one day if Douglas Adams's version of virtual reality (in which the computerised accounting system involves writing things in virtual ledgers stored in virtual file cabinets) comes to pass, really advanced debuggers will cause seasickness.

    2. tony2heads

      Flense

      Should we just see comments by the likes of Linus Torvalds as the sort of robust language a harpooner on a whaling ship might use?

  3. Evil Auditor Silver badge
    Paris Hilton

    goto fail;

    Forgive me my ignorance for my C++ (and most other programming) skill went titanic a comparably long time ago. Alternatively, it's early morning after a night with far too little sleep. Anyway, why bother about signedHashes when all this function does, as Verity Stob highlighted in red and bold, is goto fail?

    What am I missing here? (That's my personal Paris moment for today.)

    1. AndrueC Silver badge
      Go

      Re: goto fail;

      The problem is that C++ has a far more elegant and foolproof way of doing this. In this case you'd declare a class whose constructor allocated the buffers and whose destructor deallocated them.

      Then you just create an instance of that class on the stack at the start of the function. Job done. C++ guarantees that the destructor of the instantiated class will be called when it goes out of scope (at the terminating '}'). It removes the need for 'fail' block at the end, and means instead of 'goto fail;' you just have a return. Not only is it more elegant but it's also exception safe so unless the CPU fails or the compiler generated defective code you know that the resources will be freed.

      If you're a VB or C# programmer you can think of it like a finally block except that the finally code is in a destructor right next to the constructor which I think is better than having it at the bottom away from where the buffers are allocated. In fact for C# developers it's basically IDisposable only less convoluted.

      Unfortunately none of this wonderful stuff is available in C. Just a shame that language is still being used for modern development :(

      1. AndrueC Silver badge
        Boffin

        Re: goto fail;

        The problem is that C++ has a far more elegant and foolproof way of doing this. In this case you'd declare a class whose constructor allocated the buffers and whose destructor deallocated them.

        Before someone points it out actually these days you'd use std::vector<>. Rolling your own RAII class isn't all that common and is usually for more complex resources like database or OS handles. The STL has mundane stuff covered off.

        1. Kristian Walsh Silver badge

          re: goto fail;

          How I do step-by-step processes with clean deallocation in C++ (with apologies for the stripped indentation)

          bool stepByStepProcess()

          {

          bool overallResult=false;

          do {

          AcquiredResource one;

          if (false==one.checkSomething()) { break; } // will deallocate 'one'

          OtherAcquiredResource two(one, "hello");

          if (false==one.checkSomethingElse(two)) { break; } // will deallocate 'two', then 'one'

          // getting here means everything has worked.

          overallResult = true;

          } while (false);

          return overallResult;

          }

          If Bjarne hadn't said "C++ ABI and name-mangling is a matter for the implementor" back in 1985, I really don't think C would be still around now...

          1. Tom Wood

            Re: re: goto fail;

            I've seen code like this a few times:

            if (false==one.checkSomething())

            and I just think "why?!"

            I understand the logic behind putting the constant on the left, but why explicitly compare with true or false? What's wrong with if (!one.checkSomething())?

            1. Kristian Walsh Silver badge

              Re: re: goto fail;

              Legibility. ! is easily missed when you're staring at a lot of code, especially when it's as a term in a larger boolean expression.

              Look at this as an example:

               if (token||!f())

              I see checking for a false return as catching the "unusual" case, so I make it a little more explicit. I rarely write things like "if (true==...)"

              1. kraut

                Re: re: goto fail;

                Are you worried about running out of space characters?

                if (token || !f())

                is much more readable

                1. Kristian Walsh Silver badge

                  @kraut, Re: re: goto fail;

                  That was a counter-example, to explain why I tend to write "false==" rather than the unary ! operator. It's not an example of code I'd ever write, although sadly it's an example of code I've had to maintain in the past.

                  I agree that the space character is a good thing. I also bracket boolean terms within expressions, for the time when someone decides to change a test like " if ( x<0 && y!=1 )" into "if (x&0x8000000 && y!=1)". The latter has unfortunate, almost always unintended* consequences; consequences that are avoided if the original terms were bracketed, thus: " if ( (x<0) && (y!=1) )".

                  I code the way I drive. Defensively, on the assumption that everyone else is either incapable, or not paying attention. (It's not that I think other people are stupid, it's that I accept that even the best coders make stupid mistakes from time to time). Brackets and spaces and indentation have zero runtime overhead, with huge "maintain-time" performance gains.

                  * intended consequences are worse: any coder who would deliberately exploit the more unintuitive C operator precedences like this in order to save the effort of using brackets would not last long with me.

                  1. Tom Wood

                    Re: @kraut, re: goto fail;

                    Agree completely about braces after every conditional, even if it's just a single line.

                    But I guess I'm just so used to looking for logical operators when looking at conditional statements in other people's code that I much prefer not to have to evaluate odd expressions like if(false == i), or even worse, if(true != i) in my head while I'm reading code. I'd rather the more straightforward if(!i). It's like reducing algebraic expressions into the simplest form back at school.

                    If you really want to throw someone, try using the spelled-out versions if(not i), if(false eq i) etc!

                    (http://en.wikipedia.org/wiki/Iso646.h)

                    1. AndrueC Silver badge

                      Re: @kraut, re: goto fail;

                      if(true != i)

                      Ouch. Yeah I wouldn't write that. I also tend to avoid anything that claims to return a negative (eg; I'd much prefer DatabaseWasOpened() to DatabaseFailedToOpen().

                    2. Kristian Walsh Silver badge

                      @Tom Re: @kraut, re: goto fail;

                      To be a little clearer, I generally only use that syntax when checking function returns. For comparing boolean variables in expressions, I do use the ! operator.

                      The reason I do this only for functions is that it's consistent with the way you'd check for numeric error-codes. (e.g, "if ( eNoErr != thisFunction() )").

                      I find the use a ! when checking function calls is a little too subtle, simply because about 50% of APIs I've ever used use 0 to mean "no error" and the other 50% use it to mean "not successful". (dishonourable mention here to Apple's CoreFoundation that gives no status at all for operations that can actually fail under some circumstances).

                      For performing "logical NOT" on variables inside expressions, of course, I do use !, but only if the variable is actually a boolean. I do not write "!x" when I really mean to ask "has the value of the counter/numeric variable x become zero". It'll all end up as the same CPU instructions, so I think it's better to say "x==0" when I mean "test if zero" and "!x" when I mean "test if not true", if only to help the maintainer later on.

                      Especially as the maintainer may be a future me who has completely forgotten how the code works.

            2. AndrueC Silver badge
              Go

              Re: re: goto fail;

              if (false==one.checkSomething())

              and I just think "why?!"

              It helps prod my brain into thinking a bit harder about something most programmers would gloss over. I reverse the terms and check for true/false on predicates. Yes it looks odd and yes (the intent) is that it makes you look twice both when writing it and reading it.

              I've found I make far fewer mistakes that way. It's one of a number of styling choices I make that help reduce careless mistakes. Always using {}s is another. Coding shouldn't be about minimalism unless you're working in an interpreted language.

          2. This post has been deleted by its author

  4. hammarbtyp

    Note to all C programmers

    to me the biggest crime in bug #1 was the lack of {} around the if statements. I can never understand why some programmers write C like they are doing it on 1970 teletypes and must therefore feel they have to reduce the character count to an absolute minimum.

    This seems to be the trend in a lot of open source projects, with programmers so keen to show their ability to obfuscate the code, that they forget about the poor buggers who will have to maintain it in the future

    1. Paul Crawford Silver badge

      Re: Note to all C programmers

      Yes, one of the issues is simply crappy coding style (as the author put it so well "No bug is shallow if it lives in a bug-camouflaging environment.").

      That is why the likes of MISRA C/C++ guidelines were created, to get programmers doing things in ways that are robust (i.e. common/minor mistakes are easily caught or mitigated) and readable (so bugs have less opportunity to be hidden).

      You can argue C++ has more elegant ways of doing safety/clean-up things, you can also argue that it has lots of interesting ways of adding bloat or doing things inefficiently. But if you know and understand those arguments, you can probably write safe code in either C or C++ anyway.

    2. AndrueC Silver badge

      Re: Note to all C programmers

      to me the biggest crime in bug #1 was the lack of {} around the if statements.

      Certainly the style doesn't help. I'd say most of C/C++ issues come from lazy short-hand coding. In fact it's a problem with other languages where people use copy/paste or don't break code blocks out into self contained 'widgets'.

      It's the biggest drawback to both languages. You can write good, safe code in both but you need to be on the ball and prepared to put the effort in. Most programmers aren't (in the latter case often because of tight deadlines). I love C++ (although these days I'm entirely C#) but I wish C had withered and died by now. The overhead of C++ isn't that bad and surely it's a rare device these days that needs the raw minimalism of C.

      1. hammarbtyp

        Re: Note to all C programmers

        You can commit crimes against software in any language. However the less imperative the language the harder it generally gets to write bad code. On the downside I find that when such issues do occur tracking down the cause of the fault is much harder.

        For example I spent a week tracking down a memory bug because C++ destructors were firing in the wrong order. In C memory allocation/deallocation is far more visible so easier to track through the free order.

      2. Nick Ryan Silver badge

        Re: Note to all C programmers

        The style definitely doesn't help - and I'm certainly not a "friend" to many of the code formatting styles out there which encourage poorly indented and defined conditional blocks.

        It's an absolutely appalling bug to be in place because:

        1) An automatic code formatter applied to the code would have shown the problem with ease in a visual review.

        2) The compiler would have produced a warning that the code block following it is never executed. Modern compilers are helpful like that. Then utter fuckwit developers either turn off the warnings or ignore them as there are so many. Hint for the clueless: the warnings are there for a reason, deal with them.

        3) Testing should have revealed this bug very quickly as the function would not have behaved as expected. To be fair what probably happened was that the code was tested, then the developer hit Ctrl-D while the cursor happened to be on the badly formatted line, duplicating it (Ctrl-D is a common shortcut on many IDEs) probably while pressing Ctrl-S to save the current source file. However again, a commit of the source and the subsequent diff should have revealed this error straight away unless it was introduced as part of a larger block of changes, in which case the unit tests should have been re-run for all cases and the fault identified.

      3. mccp

        Re: Note to all C programmers

        "surely it's a rare device these days that needs the raw minimalism of C."

        I agree with many of the comments that you've made so far, but I'm afraid that this is simply wrong. I am still writing performance critical code for devices that use 64 MHz CPUs with 16 MB of RAM. In the embedded world, that is Maserati-style resources but still the additional memory overhead and performance reduction of C++ rules it out in favour of C. The vast majority of embedded work will be done on slower processors with an order of magnitude less RAM so C will be required for many years to come.

        1. Kristian Walsh Silver badge

          @mccp Re: Note to all C programmers

          Well, there's also Embedded C++ - it removes the performance-hurting parts of the language (dynamic allocation, virtual functions, exceptions...), and leaves you with a "C with better data structures and naming". No virtual functions, no polymorphism, but you've still got constructors and destructors, and RAII still works, and the code will be as fast and as small as C.

          C++11 also has a whole slew of features that make it very useful for low-level, embedded and performance-critical systems; stuff like constructing objects over existing memory (i.e., no allocation overhead), and elimination of class/structure copying allows you to write less code, but keep the performance. "Constant Expressions" are another great idea (that could go into C too), where you get the compiler to calculate invariants and replace them with constants rather than waste your precious CPU cycles doing it; the advantage over a macro or #define is that if the situation changes to require a runtime-calculation of the expression, the code change is as small as the removal of a single keyword.

          1. olimps

            Re: @mccp Note to all C programmers

            Correct me if I'm wrong, but you could construct and destruct objects over existing memory in older C++. You had also support for that in std. And constexprs don't do much either, compilers were able to optimize that already; constexpr is for the programmer to _enforce_ him to write code that can be actually optimized at compile time. If he writes something that can't be optimized, the compiler won't just switch to 'do it at runtime' mode, but will yell.

        2. RobZed

          Re: Note to all C programmers

          "In the embedded world, that is Maserati-style resources but still the additional memory overhead and performance reduction of C++ rules it out in favour of C."

          I'm not so sure, as a 20+ embedded programmer, I've used C++ right down to in a few K of RAM and a dozen or two kilobytes of ROM/Flash. Albeit different memory management, but definately C++ (not C in C++).

          For very tiny platforms (e.g <1K of RAM) then C or assembler, maybe - but you can do the C in a C++ compiler and get some C++ strictness.

        3. AndrueC Silver badge

          Re: Note to all C programmers

          I am still writing performance critical code for devices that use 64 MHz CPUs with 16 MB of RAM. In the embedded world, that is Maserati-style resources but still the additional memory overhead and performance reduction of C++ rules it out in favour of C

          And yet Turbo C++ was initially released for MSDOS and could target 8086 processors. If I could write software in C++ when I barely had 500kB of RAM to use, less than 64kB of stack and pitiful 8086 how come you can't with all that hardware?

        4. kraut

          Re: Note to all C programmers

          <quote>the additional memory overhead and performance reduction of C++ rules it out in favour of C.</quote>

          What memory overhead and performance reduction would that be, pray tell?

          Bjarne went to a lot of trouble to make sure that you don't pay for features unless you use them. So you can get all the benefits of - e.g, - moderate type safety, exceptions and RAII without paying the (small) runtime overhead of virtual functions.

    3. Anonymous Coward
      Anonymous Coward

      Re: Note to all C programmers

      Not just open source. The company I used to work for took on a new programmer who removed every possible bra and ket from his code "because I don't like them". Java that looks like Python is not a good idea. I am no longer around, someone else has to maintain it.

      1. Androgynous Cupboard Silver badge

        Re: Note to all C programmers

        > The company I used to work for took on a new programmer who removed every possible bra and ket from his code

        A problem that could have been fixed with a judicious application of tar and feathers. Braces are for everywhere, not just for holding up your trousers, and no tabs, anywhere, not ever. Everything else I'm flexible on but not those. And if you're about to ask about the tabs, can I politely request you shove your shiny IDE up your arse while the rest of us fire up a shell and get on with some real work.

        1. Pookietoo
          Coat

          Re: A problem that could have been fixed

          I'm familiar with tar, but "man -k feathers" returns no results.

        2. asdf

          Re: Note to all C programmers

          > And if you're about to ask about the tabs

          You simply tell them set their IDE to convert a press of the tab key into x white spaces. There everyone happy.

    4. maffski

      Re: Note to all C programmers

      'to me the biggest crime in bug #1 was the lack of {} around the if statements'

      To me there are two unacceptable issues here - the first, security related code is being given a default return of 'yay, everythings great' - so any errors that cause it to exit unintentionally will always muck up this way - you should set the default the err code to 'Zulus, thousands of em!' and only set to 'yay, everythings great' once you know everything is great - success means success, not 'I haven't noticed anything wrong'.

      The second issue being nobody seems to have ever actually tested this nice important piece of behaviour. Until you know it's right, it's wrong, it just might not have demonstrated it yet.

    5. Roo
      Windows

      Re: Note to all C programmers

      "to me the biggest crime in bug #1 was the lack of {} around the if statements."

      I don't see that as a crime, the compiler & language lawyers are the ultimate arbiters of that particular brand of justice. That said I tend to scatter {}'s everywhere by default too, but to be honest I find that consistent indentation is actually a bigger help, and these days there are a lot of editors out there that can fix indentation automatically, so it doesn't cost you much if any time to get the code licked into shape. ;)

      I am one of those people who likes languages that denote block structure by indentation (e.g.: Python, OCCAM 2), and I came to prefer those languages because control-flow is easier to follow than sketchy if-else blocks in C(++) with a mix of indentation {}'s and no {}'s. :)

    6. Mage Silver badge

      Re: Note to all C programmers

      Also C programmers are in denial, or they would not be C programmers.

      This is totally obvious from the comments and votes.

      Makes me despair about the chance of better SW. Yes, C is flexible. It's also like using nitroglycerine to dig the flowerbed.

    7. olimps

      Re: Note to all C programmers

      Braces around single lines make the code really ugly and sometimes less readable. In this particular case, they should be avoided.

      1. Tom Wood

        Re: Note to all C programmers

        Braces around single lines make the code really ugly and sometimes less readable. In this particular case, they should be avoided.

        No, no and a million times no. If you want to get rid of braces, go and write Python.

  5. Pete 2 Silver badge

    GOTO be GONE?

    > Because nobody uses goto in real code, right

    Actually EVERYBODY uses goto's - they just turn a blind eye to it.

    Look under the safety-blanky of your favourite compiler and you'll see the assembler which is produced is absolutely infested with goto statements.

    I think what is meant is that nobody (again, incorrect) writes GOTO statements in their source code. The problem isn't actually the goto statement: which is so useful there would be no practical software without it. No: the issue is partly mere fashion/snobbery, but mostly the problem of documenting it: the lack of a complimentary, high-level, COMEFROM statement to tell the poor little debuggerer how the program-counter ended up at a certain point in the code.

    Though if you debug your stuff with a logic analyser, or trace/emulator, working out where the GOTO came from is generally quite easy. There's nothing about a GOTO to sneer at or to be scared of.

    1. Paul Crawford Silver badge

      Re: GOTO be GONE?

      There are occasions where a goto might be the most elegant option (e.g. breaking out of multiple nested loops) but the problem I see is when you look at a goto target, just how did I get there?

    2. AndrueC Silver badge
      Meh

      Re: GOTO be GONE?

      but mostly the problem of documenting it

      But in the real world documentation frequently ends up out of date if it exists at all. The problem with documentation is that the compiler doesn't get to see it or in the case of comments doesn't act on it. Documentation in code or out can say what it wants but it means diddly-squat as far as the actual generated code.

      Logic analysers are good but that's still catching the problem after the mistake was made.

      Good coding style and use of proven idioms help avoid the mistake being created in the first place. So yes do all three things but don't dismiss good style as unimportant. It's the first place where mistakes can be addressed and the earlier you address a mistake the cheaper it is to fix. Good style has the potential to prevent mistakes in the first place ;)

      1. Nick Ryan Silver badge

        Re: GOTO be GONE?

        GOTO statement still have some relevance, but in general in higher languages it should be avoided. An algorithm can usually be written in a more structured, clearer manner where a GOTO statement is no longer required.

        I would much prefer to see a GOTO statement than a "BREAK <n>" statement where you have to work through the layers of conditionals and loops to work out how many levels are actually being skipped out of in the parameterised version of the BREAK statement. "COMEFROM" would be clearer :)

        Lower level, of course, you will see the exact functionality of GOTO everywhere because it is a fundamental control structure - JUMP and (conditional) BRANCH operators are key to assembly language processes. It's just that with progress we've abstracted their use away to reduce the number of problems they cause.

    3. Mage Silver badge

      Re: GOTO be GONE?

      You seem to misunderstand why we have high level languages. It's deliberately to hide the CPU assembler / machine instructions. Every CPU I can think of uses Goto. Some like low end PIC only have stack for saving address etc due to Interrupt, they even use Gotos with parameters in an address to reuse code to simulate a function or procedure.

      1. hammarbtyp

        Re: GOTO be GONE?

        To be pedantic all assembler have some thought of Jmp statement which GOTO's, If and loops compile down to.

        However assembler has a load of instruction which I wouldn't expect to see in well written high level code because the purpose of such languages is to hide complexity not to praise it

    4. Robert Forsyth

      Re: GOTO be GONE?

      In Structured Programming (without GOTOs) the idea was that each block had one entry and one exit to make the code easier to debug, etc.

      An idea which came in with Java, is you write the code as if there are no errors, so as not to hide the algorithm with error handling, then catch all the errors/exceptions at the end. The garbage-collection would clean up any unreferenced resources.

      The 'IF' statement causes problems, especially a sequence of if-them-else statements which hides what is going on and the switch-case statement(s) can easily become unwieldy. However the logical and && and logical or || can be a camouflaged if-then-else statement or conditional branch or goto. The and && loosely the then part and the or || the else part. The 'and' && becomes; if the left side is false, return false, if the left side is true, then return the right side. The 'or' || becomes; if the left side is true, return true, else return the right side.

  6. This post has been deleted by its author

  7. jake Silver badge

    Ta, Stob.

    Some of us remember why Dr. Dobbs Journal existed in dead-tree form. Keep doing your thing, my friend. Hopefully at least one kiddie will grok.

    GOTO is an an anathema when it comes to RealWorld[tm] code.

  8. jai

    A new Stob article? oh joy!

    This is officially my favourite Thursday of the month so far :)

  9. Mage Silver badge
    Mushroom

    Once again

    Excellent Ms Stob

    I was shouting in an enraged fashion the other day at the wall :

    Why are the Security Mailing lists full of the same old Array Bounds Violations, lack of input sanitising and cross site scripting vulnerabilities?! (or is it !?)

    Why nearly 30 years after C++ coming to DOS and every other platform from AT&T UNIX are people still using C?

    I know it's too much to expect people to use Modula-2. But everyone has had time to learn how to program in C++ and port it to everything.

    Why is the GUI and Web pages getting prettier but tools to develop Web Sites are like 1970s? Code if anything seems poorer than 1980s.

    Verity should look at the unholy mix for ONE web page the source files mix of Java, Javascript, PHP, SQL, CSS, HTML, Oracle SQL-PL, and BOTH kinds of Adobe's Cold Fusion (Java style and XML style), often examples or fragments of all in the same source file.

    You have to run it on the server and view with several browsers to even discover if it looks like you intended. Whole chunks of "code" may even be missing without warning. You have to "run" it to get ANY checking or diagnostics.

    Don't let me get started on "Frameworks" for PHP etc.

    1. Anonymous Coward
      Anonymous Coward

      Re: Once again

      There was someone earlier this week suggesting that joining together lots of small applications was a better way of scaling than producing proper enterprise software. You've just explained some of the downsides.

      1. Mage Silver badge

        Re: Once again

        Of course designing your giant application as lots of small ones (in separately compiled and tested files) with a clearly defined APIs/Interfaces/layers whatever so they are separately testable "black boxes" that implementers of other parts need know nothing about the innards is a good idea. Actually the ONLY way to do bigger projects with more than two developers.

        But copy & paste of small "apps" to make a big one is probably a bad idea.

    2. Mage Silver badge

      Re: Once again

      What is really frightening is that many programmers don't even understand what I'm talking about. Or see the problem with source that can only be checked by running it. Or that in the source page text it's impossible to see what actually happens without mentally running a browser inside your head.

      Or why Includes that are just text isn't real structure, objects/classes and can create bugs.

    3. heyrick Silver badge

      Re: Once again

      Mmm, I code in C. Didn't really take to C++, but that's probably not a surprise since I also write stuff in ARM assembler.

      I'm not certain that the issue is that C is inherently bad (certainly other things are better) as much as clueless/lazy programmers making the same fundamental mistakes, such as using strcpy() with no idea if the source string is larger than the destination buffer. There exists a call in the standard library - strncpy() so this bug should never happen....yet it does. Rinse and repeat for dozens of other "oh my god, not that again" failures to validate input / allocate memory / reuse pointers / etc.

      Once upon a time good software used to be supplied in a mask programmed ROM. While quirks/bugs still occurred, getting that wrong could break a company. So a lot of testing went into making sure the program was solid and did what it was supposed to do. Now? There is less impetus to get it right the first time. From downloadable patches to FlashROM updates, instead a company can "be the first" to get whatever rubbish they have created out the door. There's no time for proper testing, there's no time for a code review, you gotta be joking if you think we're going to resolve all these dumb compiler warnings, we have a half-working half-assed implementation and management has promised the world that the product will ship on Friday. And so it will. At least...something will ship on Friday...

      1. Michael Wojcik Silver badge

        Re: Once again

        such as using strcpy() with no idea if the source string is larger than the destination buffer. There exists a call in the standard library - strncpy() so this bug should never happen

        Except that strncpy() has fundamentally broken semantics, and cannot be used safely for its intended purpose unless you already know the source string fits in the destination (in which case strcpy is suitable and should perform better) or explicitly terminate the destination after copying (in which case you have an additional step to perform after the call, creating another opportunity for programmer error, and you may be unnecessarily touching a page).

        strncat() has the correct semantics - it always terminates the destination provided the copy length is at least 1 - but to use it as a safer strcpy you have to initialize the first byte of the destination (and to use it as a safer strcat you have to understand the semantics of the length parameter, which many people get wrong).

        Thinking that strncpy is ever the right answer, regardless of what your problem is, is a perfect example of the dangers of C. The C standard library has a number of traps for the unwary. (The %n format specifier for the printf family is another example.) In its defense, at least the C specification is short enough to be read and understood by most C programmers, which certainly can't be said for the ponderous tome that is the C++ standard.

        (Richard Heathfield, longtime regular of comp.lang.c, was of the opinion that the strn* functions were undesirable anyway. In his view, you always need to know whether the source fits in the destination, so you can perform proper error handling; silently truncating the string is just lazy programming. And if you already know both lengths, you might as well use memcpy and save a few cycles.)

        It is possible to write good C code. It is unfortunately very difficult to do so, and many of the people who think they can do not, in fact, understand the language well enough.

  10. Mage Silver badge

    And another thing...

    Much more evil than Macros, or not sanitising input or not checking array bounds or GOTO are Macros.

    Macros are for assembler. They are an absolute evil in a High Level Language, Don't use a Macro, EVER.

    1. Mookster
      Facepalm

      Re: And another thing...

      Utter Bollox... How else do youy get TRUE and FALSE in C?

      Also can be a convenient way for the pre-compiler to do the tedious work of determining which byte you need to write to GPIO. Besides, you can always look at the output of the pre-processor..

      1. Anonymous Coward
        Anonymous Coward

        Re: And another thing...

        Utter Bollox... How else do youy get TRUE and FALSE in C?

        By using a version of C that's less than fifteen years old? That's how long the language has supported a boolean type, making Verity appear a bit foolish when she wrote:

        He didn't even bother to change the return type of his function to bool... oh wait, he's writing in C, so he couldn't. Bummer.

        Also, her slagging off goto statements only highlights her background as a Pascal programmer, since it's an elegant way to do error cleanup in C if used carefully.

        1. kraut

          Re: And another thing...

          <quote>Also, her slagging off goto statements only highlights her background as a Pascal programmer, since it's an elegant way to do error cleanup in C if used carefully.</quote>

          Which is rather the point. It seems we have two choices, then:

          1. Stop using gotos

          2. Shoot all the programmers who aren't careful enough to use them

          I suspect C programmers would quickly become an endangered species if we went for option 2

      2. Primus Secundus Tertius

        Re: And another thing...

        TRUE and FALSE in C?

        Easy-peasy, enumerated types.

        See K & R, 2nd edition, 1988. I hope enumerations have not been removed by a subsequent "advance" in language design.

      3. Mage Silver badge

        Re: And another thing...

        Why are you using C?

        It was designed to make porting UNIX easily. It's not fit for purpose since late 1980s. Been using C++ since 1987.

        Besides you don't need Macro to define True and False.

  11. John Styles

    D & E

    Although it refers to C++ in an earlier state, I do recommend 'The Design and Evolution of C++' by Stroustrup.

    Very few books about programming / software development are any good in my experience (in accord with Sturgeon's Law), and D & E is a timeless classic.

    It makes it clear (more so than the main C++ book) where Stroustrup was coming from with C++, and why it is as it is.

    1. Anonymous Coward
      Anonymous Coward

      Re: D & E

      It makes it clear (more so than the main C++ book) where Stroustrup was coming from with C++, and why it is as it is.

      Along with the "Effective C++" and "Exceptional C++" series of books it also make it clear that C++ should be avoided at all costs. A language implemented by a lunatic who has attracted other lunatics, Alexandrescu in particular, to further his evil.

  12. shub-internet
    Thumb Up

    Re: Once again

    The key phrase here is "But everyone has had time to learn how to program..." and they haven't done so. All the bugs ranted about (and Ms. Stob, I've enjoyed your work since .EXE) are clearly mistakes/crap programming. The language used doesn't matter if one's thinking is clear; recall that the structure/Bohm-Jacopini theorem suggests that only three control structures are actually needed to program. C++ and all the rest are simply personal expressions by someone as to how *they* think this should be accomplished.

  13. David Given

    Bring back Ada

    After Heartbleed I found myself reading up on Ada --- the original safe programming language. And you know what? It's *really nice*, and I say this as an old-school C hacker. It compiles into real machine code, it's suitable for writing both the really low-level stuff (you can specify the exact bit layout of structures, for example) as well as the high-level stuff (generics and object-oriented code support on a par with C++'s); it's got a beautiful concurrency model that makes juggling threads not just safe but *really easy*; it's got robust support for programming by contract --- preconditions, postconditions etc; and on top of all this it's fast: CLBG shows it to be about equivalent to C++ performance-wise. It even has pointers! But the type system makes them impossible to misuse...

    I did a writeup: http://cowlark.com/2014-04-27-ada

    I'm becoming increasingly convinced that there is simply *no excuse* for writing stuff in C (and C++) any more. There's just better ways to do it these days.

    1. Mage Silver badge

      Re: Bring back Ada

      I think Modula-2 is nicer than Ada, but sadly most people don't understand Modules and Co-Routines to implement Objects and Concurrency. But C++ is preferable to C except when people use a C++ compiler to write C. Strustrup didn't want the amount of backward compatibility there is, but AT&T insisted.

      JAL is best for 16F & 18F PIC. Doing them with C or BASIC is plain daft. They don't have the right architecture for pointer rich C or C++ (nor very suitable for Modula-2, Pascal, Ada, Java/C# etc).

      I think we are stuck with C++ and Java (C# is really MS Java), but no excuse for C or C like programming styles. Or BASIC which is a cut down Fortran (I regard properly used VB5 & VB6 as closer to Visual Pascal or Visual Modula-2 than BASIC. VB.net on the other hand is C# pretending to be BASIC, so utterly pointless).

      1. Reliance

        Re: Bring back Ada

        The new Go language is efficient and type-safe, and is excellent for the sort of web software that has been botched in these examples. It's easier to learn than C and has nice concurrency baked in. See golang.org

        Go generates machine code, and is garbage collected, which has only one bad side-effect in practice. You can't call Go code from C or C++ code in a way that you like. The other way around works very well.

        We need a nicer type-safe language for writing critical code modules that can be linked in as libraries (static or dynamic) to existing C/C++ programs. Will ADA do that?

    2. Nick Ryan Silver badge

      Re: Bring back Ada

      The heartbleed bug could be merrily implemented in any language that supports memory access, it was an algorithm error, not a bounds violation of any form.

      Modula-2 might be ok but it was ruined by the inane insistence of the designer that it was going to be a single-pass compilation process. In reality this just doesn't work and you either wind up with horrible kludges to the code or progressively more unwieldy development environments.

      I'm becoming increasingly convinced that there is simply *no excuse* for writing stuff in C (and C++) any more. There's just better ways to do it these days.

      No one language is so superior to all the others that it is usable at all levels, from device driver all the way to up to user script level. As a general rule: the closer you get to the hardware, the lower the level of language that is appropriate for use. Efficiency really matters at the lower level, while wasting thousands of CPU cycles with boilerplate and support code is almost acceptable at the application level, it most definitely is not for an API call that could be called hundreds of thousands of times a second. Like everything there are always trade offs balancing code security and with efficiency.

    3. Michael Wojcik Silver badge

      Re: Bring back Ada

      I'm becoming increasingly convinced that there is simply *no excuse* for writing stuff in C (and C++) any more.

      Of course there's an "excuse". There are even very good reasons to continue using C, C++, or whatever other language enrages the demagogue of your choice.

      First there is the need to maintain existing code. This ought to be blindingly obvious to anyone in the industry; I don't know why I even mention it. There are ample case studies and other research showing that the cost of "rip & replace" conversions of large existing code bases to another language tends to be enormous, and such projects are even more prone to failing than typical large IT efforts.

      Second there are the costs of developer training - both direct financial costs and indirect ones, like opportunity costs (oh, let's just halt all our development for a few months while our staff learns a new language) and resistance from staff (because programmers are not always the most compliant employees). Just replace them with new hires? Where's this pool of Ada experts waiting to come on board? And do they magically already share all the domain knowledge of existing staff? Didn't think so.

      Third, there's no consensus that this wildly expensive radical change will produce any significant improvement. Many people have a pet "better" programming language or method or what have you that they believe will save the world; so far, few have been successful at convincing more than a small group of adherents. And for good reason, since more than one observer has come to agree with Fred Brooks that there is no silver bullet.

  14. John Styles

    Wasn't it John-Paul Sartre who said..

    ...'hell is other people's C++' ?

  15. Dangermouse 1

    Boolean types in C

    C99 has a bool type. Mind you the standard is only 15 years old so I can see why most people still don't use it.

    (That was sarcasm)

  16. Simon Watson

    You All Use GoTo

    Whenever you use break or continue you're really saying goto. Ever used a switch block? That's just compiler candy for a bunch of if blocks separated by goto. if {foo()} else {bar()} is just if {foo() goto;} {bar()}. You all use it, you just don't like to talk about it!

    1. Kristian Walsh Silver badge

      Re: You All Use GoTo

      There's a huge difference between break, continue and else; and goto.

      break is used to exit the current code block, and can only cause the execution to move forward towards the end of the current function. Similarly, continue can't move execution to any point before the enclosing loop was defined, and else can only cause execution to move forward.

      goto can explicitly put the execution pointer anywhere in the current function. That's why it's considered such a dangerous tool.

      In scoped languages, there's another difference - when you issue a 'break;' you are cleanly leaving the current symbol scope (which means that anything created within that scope will be cleanly disposed of). goto doesn't guarantee anything.

      The argument that an if is just a goto is silly, because that's exactly the point of "if". The reason there's an if/else construct at all is to replace the loose cannon of "goto" with something safer and more descriptive (and remember that compilers produce better code when you give them more to work on).

      1. Simon Watson

        Re: You All Use GoTo

        I was being somewhat facetious, maybe I need to use the Joke Alert. Nevertheless when you look at unoptimised assembly output (and often with more simple code even optimised output) they are often indistinguishable.

    2. Michael Wojcik Silver badge

      Re: You All Use GoTo

      Gah. Eternal September is eternal.

  17. Dan 55 Silver badge

    Anyone who doesn't know when to use a goto, why they should always brace ifs even though there's only one statement, or how to return values from functions in C won't know how to use RAII in C++.

    Doesn't make either C or C++ intrinsically bad, one of those programmers would make a mess of it in any language. The question is why are these people being let near encryption libraries?

    1. Kristian Walsh Silver badge

      Open Source is where seasoned professionals made their early mistakes...

      The projects are always short of coders, and meanwhile colleges and universities are always looking for ways of exposing their students to "real" code.

      There's a lot of free software that was written by people who, until they wrote it, knew very little about the subject, or even about programming.

      The worst examples get weeded out by diligent maintainers, but sometimes code slips through, and then hangs around so long that people think it's a model solution. Definitely the "million eyeballs" collaborative development model encourages the fallacy that "old code is robust code" even without any evidence that the code in question has been examined.

      Commercial practices aren't much better, mind, but there's always the threat of lawsuit or being fired to encourage a little more diligence from devs. Plus, by the time you're hired to do a coding job, you've made a lot of your naive mistakes already... on some Open Source project.

      1. Pookietoo
        Linux

        Re: Open Source is where seasoned professionals made their early mistakes...

        It's usually people with commercial closed-source interests who like to spread the lie that open source doesn't have good professional developers.

        1. Kristian Walsh Silver badge

          Re: Open Source is where seasoned professionals made their early mistakes...

          I didn't say that FOSS lacks professionals, or even capable amateurs if you use the literal definition of "professional". Saying that would be a lie. But that's not what I said.

          What I said was that lots of project contributions are made by students; people who almost by definition lack experience. I know this from working in academia, and in academic research. The project maintainers catch a lot of the naive mistakes they make, but anything that slips through can acquire "good" status simply due to age.

          That is a flaw of the "many eyeballs" theory of software quality, but that's not to say that organised QA is in any way incompatible with FOSS. The best projects do lots of formal bugfix and testing, just like the best commercial closed-source. There's no such thing as an Open Source or Closed-Source Software Engineering process; there are only suitable processes and unsuitable ones.

          There's certainly a lot of closed-source software developers who believe the "many eyeballs" fallacy. Particularly on the web where updates are cheap, and the ethos is that "first but wrong" will beat "second and right".

          1. Roo
            Windows

            Re: Open Source is where seasoned professionals made their early mistakes...

            Fair play on those points Kristian, have an upvote for taking the time to clarify. FWIW I pretty much agree with your points. :)

      2. Roo
        Windows

        Re: Open Source is where seasoned professionals made their early mistakes...

        "Commercial practices aren't much better, mind, but there's always the threat of lawsuit or being fired to encourage a little more diligence from devs."

        Personally I have seen absolutely zero evidence of that having any impact on code quality. Besides, Devs often move on before the system is deployed, some even make a point of doing so... :(

        "Plus, by the time you're hired to do a coding job, you've made a lot of your naive mistakes already... on some Open Source project."

        The Open Source/Commercial background isn't a reliable indicator in my experience, and your comment with respect to Open Source amounts to malicious slander in my view.

        ... Besides, one of the poorest developers I have ever come across lept onto the IT bandwagon in the late 90s, had no interest in computing, and as far as I could tell every single bit of programming they had ever done was paid for. In fact they would be the least likely person to do Open Source development because they appeared to be interested in the money and totally disinterested in computing.

        For several weeks that particular individual yelled/shouted and screamed (literally) that gcc was broken when it failed to stop him passing incompatible parameters to a function that he had declared KNR style (ie: without a prototype). After the mistake was pointed out, and fixes made (adding ANSI style prototypes) which took only 5 minutes, they reverted the fixes and went back to their broken code - which they spent another 3 weeks failing to get to work. Cue more shouting/screaming but also an inexplicable reluctance to post a bug report to the gcc team...

  18. Anonymous Coward
    Anonymous Coward

    good read, as ever.

  19. Down not across

    Workmen and their tools

    All this C is crap and should be burned on the stake, C++ is the holy grail, best thing since sliced bread ...is pointless (and IMHO incorrect) and rather irritating.

    Problem is not the language.

    Only bad workmen blame the tools.

    As for programmers, bad ones can (and will) write bad code regardless of the language. If there is a way to do it wrong they will find it. Equally good programmers will be able to write good code regardless of the language.

    None of these bugs were because it they wouldn't have been avoidable in C. Just that the programmers chose (inadvertently most likely) to be bit sloppy.

    1. Ed 13

      Re: Workmen and their tools

      Indeed.

      There are lots of different tool for lots of different jobs. Using the tool in the wrong way or for the wrong job will result in a poor job or failure.

      Bjarne Stroustrup did say ""C makes it easy to shoot yourself in the foot; C++ makes it harder, but when you do it blows your whole leg off" and adds the footnote that this really applies to all powerful languages.

    2. Michael Wojcik Silver badge

      Re: Workmen and their tools

      Only bad workmen blame the tools.

      A quintessential example of the sort of folk wisdom that isn't worth the bits used to encode it.

      Any real expert knows that tools can be suitable for a task or not; can be well-designed and well-made or not; and can be inherently dangerous even when suitable and well-designed. Often the tools are, in fact, to blame.

      This bumper-sticker tripe is trotted out by some sophomore every time programming languages (or anything else relevant to the crafting of software) are discussed.

  20. Jim McCafferty

    Verity is back

    Great to see another article - always the best read of a given month.

    Any chance of upping the frequency a tad? :)

  21. Michael Wojcik Silver badge

    Who looks at OpenSSL source

    It seems to me that the only folks properly motivated to pore over the inner details of SSL are those naughtily looking for a vulnerabilities.

    Actually, if you subscribe to the openssl-users list, you'll see that most of the people poring over the OpenSSL sources, at least, are folks just trying to use the API. I just spent a few months (once again) doing that myself. The OpenSSL docs are rather incomplete and the feature set is enormous, courtesy of the ever-swelling sea of related standards. (Try adding PKCS#12 support to an OpenSSL-based application some time. Or custom certificate-chain processing. Fun!)

This topic is closed for new posts.

Other stories you might like