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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
|
//
// section-parse: scan standard input and do the following:
//
// 1. look for a section that begins with "test-text "STUFF/foo" and ends
// with a blank line.
// 2. print the "bar.DIGITS" suffix for each line in the section
//
// Build:
// cc -std=c17 -W -Wall -Wextra -Werror -pedantic -O2 -o section-parse{,.c}
//
// Example:
// > cc -std=c17 -W -Wall -Wextra -Werror -pedantic -O2 -o section-parse{,.c}
// > ./section-parse < input.txt
// bar.1
// bar.2
// bar.3
// bar.4
//
#include <stdlib.h> // exit()
#include <stdio.h> // fgets(), feof()
#include <string.h> // memcmp(), strlen()
#include <stdbool.h> // bool
// section header line prefix
// (used by is_header())
static const char HEAD_PREFIX[] = "test-text \"";
static const size_t HEAD_PREFIX_LEN = sizeof(HEAD_PREFIX) - 1;
// section footer line suffix
// (used by is_header())
static const char HEAD_SUFFIX[] = "/foo\"\n";
static const size_t HEAD_SUFFIX_LEN = sizeof(HEAD_SUFFIX) - 1;
/**
* Return true if the given line is a section header, and false
* otherwise.
*/
static bool is_header(const char * const s) {
const size_t len = strlen(s);
// check string length
if (len < (HEAD_PREFIX_LEN + HEAD_SUFFIX_LEN)) {
return false;
}
// compare line prefix and suffix and return true if they both match
return !memcmp(s, HEAD_PREFIX, HEAD_PREFIX_LEN) &&
!memcmp(s + len - HEAD_SUFFIX_LEN, HEAD_SUFFIX, HEAD_SUFFIX_LEN);
}
/**
* Return true if the given line is a section footer, and false
* otherwise.
*/
static bool is_footer(const char * const s) {
const size_t len = strlen(s);
for (size_t i = 0; i < len; i++) {
if (s[i] != ' ' && s[i] != '\n') {
// found non-space character, return false
return false;
}
}
// return true
return true;
}
/**
* Match ".DIGITS" suffix on given line and print "bar.DIGITS" to standard
* output.
*
* Exits with a non-zero error code if the given line is invalid.
*/
static void print_body_line(const char * const s) {
const size_t len = strlen(s);
// check line length
if (len < 2) {
fprintf(stderr, "short line\n");
exit(-1);
}
// find trailing '.'
// (note: technically this should probably use isdigit() too)
size_t pos = len - 2;
while (pos > 0 && s[pos] != '.') {
pos--;
}
// check to make sure we found trailing '.'
if (s[pos] != '.') {
fprintf(stderr, "invalid line: %s", s);
exit(-1);
}
// print trailing digits
// (note: match also includes trailing newline)
printf("bar.%s", s + pos + 1);
}
int main() {
char buf[1024]; // line buffer
bool in_body = false; // parse state
// read lines from standard input
while (fgets(buf, sizeof(buf), stdin) && !feof(stdin)) {
if ((!in_body && is_header(buf)) || (in_body && is_footer(buf))) {
// toggle state
in_body = !in_body;
} else if (in_body) {
// print body line
print_body_line(buf);
}
}
// return success
return 0;
}
|