1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
|
// 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.
|