A lot of those if-/case-blocks are precisely where I'd put functions :)
If you changed a bunch of those to separate, pure (i.e. side-effect-free) functions it would if nothing else make unit testing a breeze, and then you'd be free to fix bugs in the logic without fear. As it is, if I had a bug in that huge function I'd be really worried about breaking some edge-condition or implied-state 500 lines up etc.
This could be turned into a pure function that takes c and print_state as input, and returns a new print_state which the outer function assigns. That's 12 lines turned into 1.
It's a Mealy state machine, it should be encoded as a state machine!
Why some folks will absolutely insist that a series of manually-written case statements in a single thousand line function is the epitome of style, when there's a pure state machine model inside wishing you would free it from the shackles of if else if else if else....
You can always use return values, and allow the caller to decide how to use them (e.g. caller may mutate other values).
Depending on use and language, this may be "expensive" (e.g. you could be allocating for and then copying some huge data structure only to pass it back where is is simply copied over the top of the input), but this is where discretion comes in and decisions are made on what is appropriate (i.e. is performance critical, or is correctness and maintainability more important?)
Why? That would create lots of small functions that each is called only once from only one place in the code and still are extremely coupled to the invoking switch statement, often having multiple inputs and multiple outputs which then the invoking statement needs to write in the proper places to which the function wouldn't have access.
Breaking out functions makes sense when you get either reuse or decoupling, but in this case you don't get any of these.
Because it would objectively and shockingly obviously make the code easier to understand.
You would be able to see what it does at a high level at a glance and then drill down into the functions and sub-functions to focus on a particular part of it.
With this version, to get the high level overview you have to scroll through multiple pages of code and find the comments that say what each section does.
So the problem is being able to drill down? Most decent IDEs allow you to collapse code blocks (i.e brackets) if that's what you want to do, and the comments accomplish the same thing a function name would. Some editors support region comments that start out collapsed and can be named. I don't see why separate functions would be anyones first instinct to reach for here.
This function is 1830 lines long. It's reasonably well structured I think. Although the #if 0 are maybe not so good.