OILS / cpp / libc.cc View on Github | oils.pub

239 lines, 138 significant
1// libc.cc: Replacement for pyext/libc.c
2
3#include "cpp/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 <sys/ioctl.h>
11#include <unistd.h> // gethostname()
12#include <wchar.h>
13
14namespace libc {
15
16BigStr* gethostname() {
17 // Note: Fixed issue #1656 - OS X and FreeBSD don't have HOST_NAME_MAX
18 // https://reviews.freebsd.org/D30062
19 BigStr* result = OverAllocatedStr(_POSIX_HOST_NAME_MAX);
20 int status = ::gethostname(result->data_, _POSIX_HOST_NAME_MAX);
21 if (status != 0) {
22 throw Alloc<OSError>(errno);
23 }
24 // Important: set the length of the string!
25 result->MaybeShrink(strlen(result->data_));
26 return result;
27}
28
29BigStr* realpath(BigStr* path) {
30 BigStr* result = OverAllocatedStr(PATH_MAX);
31 char* p = ::realpath(path->data_, result->data_);
32 if (p == nullptr) {
33 throw Alloc<OSError>(errno);
34 }
35 result->MaybeShrink(strlen(result->data_));
36 return result;
37}
38
39int fnmatch(BigStr* pat, BigStr* str, int flags) {
40#ifdef FNM_EXTMATCH
41 flags |= FNM_EXTMATCH;
42#else
43 // Detected by ./configure
44#endif
45
46 int result = ::fnmatch(pat->data_, str->data_, flags);
47 switch (result) {
48 case 0:
49 return 1;
50 case FNM_NOMATCH:
51 return 0;
52 default:
53 // Other error
54 return -1;
55 }
56}
57
58List<BigStr*>* glob(BigStr* pat, int flags) {
59 glob_t results;
60 // Hm, it's weird that the first one can't be called with GLOB_APPEND. You
61 // get a segfault.
62 // int flags = GLOB_APPEND;
63 // flags |= GLOB_NOMAGIC;
64 int ret = glob(pat->data_, flags, NULL, &results);
65
66 const char* err_str = NULL;
67 switch (ret) {
68 case 0: // no error
69 break;
70 case GLOB_ABORTED:
71 err_str = "read error";
72 break;
73 case GLOB_NOMATCH:
74 // No error, because not matching isn't necessarily a problem.
75 // NOTE: This can be turned on to log overaggressive calls to glob().
76 // err_str = "nothing matched";
77 break;
78 case GLOB_NOSPACE:
79 err_str = "no dynamic memory";
80 break;
81 default:
82 err_str = "unknown problem";
83 break;
84 }
85 if (err_str) {
86 throw Alloc<RuntimeError>(StrFromC(err_str));
87 }
88
89 // http://stackoverflow.com/questions/3512414/does-this-pylist-appendlist-py-buildvalue-leak
90 size_t n = results.gl_pathc;
91 auto matches = NewList<BigStr*>();
92
93 // Print array of results
94 size_t i;
95 for (i = 0; i < n; i++) {
96 const char* m = results.gl_pathv[i];
97 matches->append(StrFromC(m));
98 }
99 globfree(&results);
100
101 return matches;
102}
103
104// Raises RuntimeError if the pattern is invalid. TODO: Use a different
105// exception?
106List<int>* regex_search(BigStr* pattern, int cflags, BigStr* str, int eflags,
107 int pos) {
108 cflags |= REG_EXTENDED;
109 regex_t pat;
110 int status = regcomp(&pat, pattern->data_, cflags);
111 if (status != 0) {
112 char error_desc[50];
113 regerror(status, &pat, error_desc, 50);
114
115 char error_message[80];
116 snprintf(error_message, 80, "Invalid regex %s (%s)", pattern->data_,
117 error_desc);
118
119 throw Alloc<ValueError>(StrFromC(error_message));
120 }
121 // log("pat = %d, str = %d", len(pattern), len(str));
122
123 int num_groups = pat.re_nsub + 1; // number of captures
124
125 List<int>* indices = NewList<int>();
126 indices->reserve(num_groups * 2);
127
128 const char* s = str->data_;
129 regmatch_t* pmatch =
130 static_cast<regmatch_t*>(malloc(sizeof(regmatch_t) * num_groups));
131 bool match = regexec(&pat, s + pos, num_groups, pmatch, eflags) == 0;
132 if (match) {
133 int i;
134 for (i = 0; i < num_groups; i++) {
135 int start = pmatch[i].rm_so;
136 if (start != -1) {
137 start += pos;
138 }
139 indices->append(start);
140
141 int end = pmatch[i].rm_eo;
142 if (end != -1) {
143 end += pos;
144 }
145 indices->append(end);
146 }
147 }
148
149 free(pmatch);
150 regfree(&pat);
151
152 if (!match) {
153 return nullptr;
154 }
155
156 return indices;
157}
158
159// For ${//}, the number of groups is always 1, so we want 2 match position
160// results -- the whole regex (which we ignore), and then first group.
161//
162// For [[ =~ ]], do we need to count how many matches the user gave?
163
164const int NMATCH = 2;
165
166// Odd: This a Tuple2* not Tuple2 because it's Optional[Tuple2]!
167Tuple2<int, int>* regex_first_group_match(BigStr* pattern, BigStr* str,
168 int pos) {
169 regex_t pat;
170 regmatch_t m[NMATCH];
171
172 // Could have been checked by regex_parse for [[ =~ ]], but not for glob
173 // patterns like ${foo/x*/y}.
174
175 if (regcomp(&pat, pattern->data_, REG_EXTENDED) != 0) {
176 throw Alloc<RuntimeError>(
177 StrFromC("Invalid regex syntax (func_regex_first_group_match)"));
178 }
179
180 // Match at offset 'pos'
181 int result = regexec(&pat, str->data_ + pos, NMATCH, m, 0 /*flags*/);
182 regfree(&pat);
183
184 if (result != 0) {
185 return nullptr;
186 }
187
188 // Assume there is a match
189 regoff_t start = m[1].rm_so;
190 regoff_t end = m[1].rm_eo;
191 Tuple2<int, int>* tup = Alloc<Tuple2<int, int>>(pos + start, pos + end);
192
193 return tup;
194}
195
196int wcswidth(BigStr* s) {
197 // Behavior of mbstowcs() depends on LC_CTYPE
198
199 // Calculate length first
200 int num_wide_chars = ::mbstowcs(NULL, s->data_, 0);
201 if (num_wide_chars == -1) {
202 throw Alloc<UnicodeError>(StrFromC("mbstowcs() 1"));
203 }
204
205 // Allocate buffer
206 int buf_size = (num_wide_chars + 1) * sizeof(wchar_t);
207 wchar_t* wide_chars = static_cast<wchar_t*>(malloc(buf_size));
208 DCHECK(wide_chars != nullptr);
209
210 // Convert to wide chars
211 num_wide_chars = ::mbstowcs(wide_chars, s->data_, num_wide_chars);
212 if (num_wide_chars == -1) {
213 free(wide_chars); // cleanup
214
215 throw Alloc<UnicodeError>(StrFromC("mbstowcs() 2"));
216 }
217
218 // Find number of columns
219 int width = ::wcswidth(wide_chars, num_wide_chars);
220 if (width == -1) {
221 free(wide_chars); // cleanup
222
223 // unprintable chars
224 throw Alloc<UnicodeError>(StrFromC("wcswidth()"));
225 }
226
227 free(wide_chars);
228 return width;
229}
230
231int get_terminal_width() {
232 struct winsize w;
233 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) == -1) {
234 throw Alloc<IOError>(errno);
235 }
236 return w.ws_col;
237}
238
239} // namespace libc