cpp

Coverage Report

Created: 2022-11-10 11:34

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