cpp

Coverage Report

Created: 2022-09-21 22:22

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