cpp

Coverage Report

Created: 2022-07-20 01:16

/home/uke/oil/cpp/leaky_libc.cc
Line
Count
Source (jump to first uncovered line)
1
// libc.cc: Replacement for native/libcmodule.c
2
3
#include "leaky_libc.h"
4
5
#include <glob.h>
6
#include <locale.h>
7
#include <regex.h>
8
9
namespace libc {
10
11
2
List<Str*>* glob(Str* pat) {
12
2
  mylib::Str0 pat0(pat);
13
14
2
  glob_t results;
15
  // Hm, it's weird that the first one can't be called with GLOB_APPEND.  You
16
  // get a segfault.
17
2
  int flags = 0;
18
  // int flags = GLOB_APPEND;
19
  // flags |= GLOB_NOMAGIC;
20
2
  int ret = glob(pat0.Get(), flags, NULL, &results);
21
22
2
  const char* err_str = NULL;
23
2
  switch (ret) {
24
1
  case 0:  // no error
25
1
    break;
26
0
  case GLOB_ABORTED:
27
0
    err_str = "read error";
28
0
    break;
29
1
  case GLOB_NOMATCH:
30
    // No error, because not matching isn't necessarily a problem.
31
    // NOTE: This can be turned on to log overaggressive calls to glob().
32
    // err_str = "nothing matched";
33
1
    break;
34
0
  case GLOB_NOSPACE:
35
0
    err_str = "no dynamic memory";
36
0
    break;
37
0
  default:
38
0
    err_str = "unknown problem";
39
0
    break;
40
2
  }
41
2
  if (err_str) {
42
0
    throw new RuntimeError(new Str(err_str));
43
0
  }
44
45
  // http://stackoverflow.com/questions/3512414/does-this-pylist-appendlist-py-buildvalue-leak
46
2
  size_t n = results.gl_pathc;
47
2
  auto matches = new List<Str*>();
48
49
  // Print array of results
50
2
  size_t i;
51
3
  for (i = 0; i < n; i++) {
52
1
    const char* m = results.gl_pathv[i];
53
54
    // Make a copy so we own it.
55
1
    size_t len = strlen(m);
56
1
    char* buf = static_cast<char*>(malloc(len + 1));
57
1
    memcpy(buf, m, len);
58
1
    buf[len] = '\0';
59
60
1
    matches->append(new Str(buf, len));
61
1
  }
62
2
  globfree(&results);
63
64
2
  return matches;
65
2
}
66
67
// Raises RuntimeError if the pattern is invalid.  TODO: Use a different
68
// exception?
69
2
List<Str*>* regex_match(Str* pattern, Str* str) {
70
2
  List<Str*>* results = new List<Str*>();
71
72
2
  mylib::Str0 pattern0(pattern);
73
2
  mylib::Str0 str0(str);
74
75
2
  regex_t pat;
76
2
  if (regcomp(&pat, pattern0.Get(), REG_EXTENDED) != 0) {
77
    // TODO: check error code, as in func_regex_parse()
78
0
    throw new RuntimeError(new Str("Invalid regex syntax (regex_match)"));
79
0
  }
80
81
2
  int outlen = pat.re_nsub + 1;  // number of captures
82
83
2
  const char* s0 = str0.Get();
84
2
  regmatch_t* pmatch = (regmatch_t*)malloc(sizeof(regmatch_t) * outlen);
85
2
  int match = regexec(&pat, s0, outlen, pmatch, 0) == 0;
86
2
  if (match) {
87
1
    int i;
88
4
    for (i = 0; i < outlen; i++) {
89
3
      int len = pmatch[i].rm_eo - pmatch[i].rm_so;
90
3
      Str* m = new Str(s0 + pmatch[i].rm_so, len);
91
3
      results->append(m);
92
3
    }
93
1
  }
94
95
2
  free(pmatch);
96
2
  regfree(&pat);
97
98
2
  if (!match) {
99
1
    return nullptr;
100
1
  }
101
102
1
  return results;
103
2
}
104
105
// For ${//}, the number of groups is always 1, so we want 2 match position
106
// results -- the whole regex (which we ignore), and then first group.
107
//
108
// For [[ =~ ]], do we need to count how many matches the user gave?
109
110
const int NMATCH = 2;
111
112
// Why is this a Tuple2* and not Tuple2?
113
3
Tuple2<int, int>* regex_first_group_match(Str* pattern, Str* str, int pos) {
114
3
  mylib::Str0 pattern0(pattern);
115
3
  mylib::Str0 str0(str);
116
117
3
  regex_t pat;
118
3
  regmatch_t m[NMATCH];
119
120
3
  const char* old_locale = setlocale(LC_CTYPE, NULL);
121
122
3
  if (setlocale(LC_CTYPE, "") == NULL) {
123
0
    throw new RuntimeError(new Str("Invalid locale for LC_CTYPE"));
124
0
  }
125
126
  // Could have been checked by regex_parse for [[ =~ ]], but not for glob
127
  // patterns like ${foo/x*/y}.
128
129
3
  if (regcomp(&pat, pattern0.Get(), REG_EXTENDED) != 0) {
130
0
    throw new RuntimeError(
131
0
        new Str("Invalid regex syntax (func_regex_first_group_match)"));
132
0
  }
133
134
  // Match at offset 'pos'
135
3
  int result = regexec(&pat, str0.Get() + pos, NMATCH, m, 0 /*flags*/);
136
3
  regfree(&pat);
137
138
3
  setlocale(LC_CTYPE, old_locale);
139
140
3
  if (result != 0) {
141
0
    return nullptr;
142
0
  }
143
144
  // Assume there is a match
145
3
  regoff_t start = m[1].rm_so;
146
3
  regoff_t end = m[1].rm_eo;
147
3
  return new Tuple2<int, int>(pos + start, pos + end);
148
3
}
149
150
}  // namespace libc