Non-Local Intro (The GNU C Library)

### 24.1 Introduction to Non-Local Exits ¶

As an example of a situation where a non-local exit can be useful, suppose you have an interactive program that has a “main loop” that prompts for and executes commands. Suppose the “read” command reads input from a file, doing some lexical analysis and parsing of the input while processing it. If a low-level input error is detected, it would be useful to be able to return immediately to the “main loop” instead of having to make each of the lexical analysis, parsing, and processing phases all have to explicitly deal with error situations initially detected by nested calls.

(On the other hand, if each of these phases has to do a substantial amount of cleanup when it exits—such as closing files, deallocating buffers or other data structures, and the like—then it can be more appropriate to do a normal return and have each phase do its own cleanup, because a non-local exit would bypass the intervening phases and their associated cleanup code entirely. Alternatively, you could use a non-local exit but do the cleanup explicitly either before or after returning to the “main loop”.)

In some ways, a non-local exit is similar to using the ‘`return`’ statement to return from a function. But while ‘`return`’ abandons only a single function call, transferring control back to the point at which it was called, a non-local exit can potentially abandon many levels of nested function calls.

You identify return points for non-local exits by calling the function `setjmp`. This function saves information about the execution environment in which the call to `setjmp` appears in an object of type `jmp_buf`. Execution of the program continues normally after the call to `setjmp`, but if an exit is later made to this return point by calling `longjmp` with the corresponding `jmp_buf` object, control is transferred back to the point where `setjmp` was called. The return value from `setjmp` is used to distinguish between an ordinary return and a return made by a call to `longjmp`, so calls to `setjmp` usually appear in an ‘`if`’ statement.

Here is how the example program described above might be set up:

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

    jmp_buf main_loop;

    void
    abort_to_main_loop (int status)
    {
      longjmp (main_loop, status);
    }

    void
    do_command (void)
    {
      char buffer[128];
      if (fgets (buffer, 128, stdin) == NULL)
        abort_to_main_loop (-1);
      else
        exit (EXIT_SUCCESS);
    }

    int
    main (void)
    {
      while (1)
        if (setjmp (main_loop))
          puts ("Back at main loop....");
        else
          do_command ();
    }

The function `abort_to_main_loop` causes an immediate transfer of control back to the main loop of the program, no matter where it is called from.

The flow of control inside the `main` function may appear a little mysterious at first, but it is actually a common idiom with `setjmp`. A normal call to `setjmp` returns zero, so the “else” clause of the conditional is executed. If `abort_to_main_loop` is called somewhere within the execution of `do_command`, then it actually appears as if the same call to `setjmp` in `main` were returning a second time with a value of `-1`.

So, the general pattern for using `setjmp` looks something like:

    if (setjmp (buffer))
      /* Code to clean up after premature return. */
      ...
    else
      /* Code to be executed normally after setting up the return point. */
      ...