// Command-line flags // ============================================================================ // tt can be configured by changing the value of three variables: // -> declarations char *code_prefix; /* string with which code lines should start */ char *doc_prefix; /* string with which documentation lines should start */ char *out_prefix; /* string to which the output file name should be appended */ // The default values are the following: -> main.options code_prefix = " "; /* code lines should begin with four spaces */ doc_prefix = ""; /* other lines are documentation lines */ out_prefix = "out/"; /* all output files go in the out/ directory */ // Each variable is controlled by a single-letter command-line flag, which // should then be immediately -- without any space -- followed by the // desired value. For example, -dfinal. would set out_prefix to "final.". // This convention allows for a very simple parsing loop: -> main.options for (i = 1; i < argc; i++) if (argv[i][0] == '-') { switch(argv[i][1]) { case 'c': code_prefix = argv[i] + 2; break; case 'd': doc_prefix = argv[i] + 2; break; case 'o': out_prefix = argv[i] + 2; break; case '-': i++; goto end; default: die(USAGE); } } else break; end: // If the given argument begins with a hyphen, it is interpreted as a flag. // If the flag is --, then tt ignores the argument and stops looking for flags. // If the flag is unrecognized, the program dies. If the argument does not // begin with a hyphen, it and anything following it will not be interpreted // as a flag. // USAGE contains information about how to use tt: -> definitions #define USAGE "usage: %s [-cCODE_PREFIX] [-dDOC_PREFIX] [-oOUTPREFIX] destination ...\n", argv[0] // Of course, we can't just trust the user to provide reasonable values, so we // ensure that the code_prefix and out_prefix are not identical and that the // out_prefix is not empty -- otherwise, tt would overwrite all destination // files: -> main.options if (strcmp(code_prefix, doc_prefix) == 0) die("code_prefix and doc_prefix cannot be identical\n"); if (strlen(out_prefix) == 0) die("out_prefix cannot be empty\n"); // Command-line arguments // ============================================================================ // Having finished parsing command-line flags, it is time to collect the // remaining command-line arguments, which should be one or more destination // files. Our loop above, when broken out of or finished naturally, has set // the i variable to the position of the first non-flag argument in argv (or // simply the position after the last flag in argv). // First, we check if there actually are any further argument, or if i is past // the end of the array: -> main.options if (i == argc) die(USAGE); // At least one destination file is required. Then, we save the position of the // first destination file in argv in a special variable for later use: // -> main.options offset = i; // -> main.declarations int offset; // Now, we have successfully finished parsing both flags and arguments, and are // ready to read the lines on the standard input.