aboutsummaryrefslogtreecommitdiff
path: root/section-parse.c
blob: 78fda1f87da4f245ae8d89d49ca6d7e3389ad7e7 (plain)
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;
}