1 | // sass.hpp must go before all system headers to get the |
2 | // __EXTENSIONS__ fix on Solaris. |
3 | #include "sass.hpp" |
4 | #include "ast.hpp" |
5 | |
6 | #include "sass_functions.hpp" |
7 | #include "json.hpp" |
8 | |
9 | #define LFEED "\n" |
10 | |
11 | // C++ helper |
12 | namespace Sass { |
13 | // see sass_copy_c_string(sass::string str) |
14 | static inline JsonNode* json_mkstream(const sass::ostream& stream) |
15 | { |
16 | // hold on to string on stack! |
17 | sass::string str(stream.str()); |
18 | return json_mkstring(s: str.c_str()); |
19 | } |
20 | |
21 | static void handle_string_error(Sass_Context* c_ctx, const sass::string& msg, int severety) |
22 | { |
23 | sass::ostream msg_stream; |
24 | JsonNode* json_err = json_mkobject(); |
25 | msg_stream << "Internal Error: " << msg << std::endl; |
26 | json_append_member(object: json_err, key: "status" , value: json_mknumber(n: severety)); |
27 | json_append_member(object: json_err, key: "message" , value: json_mkstring(s: msg.c_str())); |
28 | json_append_member(object: json_err, key: "formatted" , value: json_mkstream(stream: msg_stream)); |
29 | try { c_ctx->error_json = json_stringify(node: json_err, space: " " ); } |
30 | catch (...) {} |
31 | c_ctx->error_message = sass_copy_string(str: msg_stream.str()); |
32 | c_ctx->error_text = sass_copy_c_string(str: msg.c_str()); |
33 | c_ctx->error_status = severety; |
34 | c_ctx->output_string = 0; |
35 | c_ctx->source_map_string = 0; |
36 | json_delete(node: json_err); |
37 | } |
38 | |
39 | static int handle_error(Sass_Context* c_ctx) { |
40 | try { |
41 | throw; |
42 | } |
43 | catch (Exception::Base& e) { |
44 | sass::ostream msg_stream; |
45 | sass::string cwd(Sass::File::get_cwd()); |
46 | sass::string msg_prefix(e.errtype()); |
47 | bool got_newline = false; |
48 | msg_stream << msg_prefix << ": " ; |
49 | const char* msg = e.what(); |
50 | while (msg && *msg) { |
51 | if (*msg == '\r') { |
52 | got_newline = true; |
53 | } |
54 | else if (*msg == '\n') { |
55 | got_newline = true; |
56 | } |
57 | else if (got_newline) { |
58 | msg_stream << sass::string(msg_prefix.size() + 2, ' '); |
59 | got_newline = false; |
60 | } |
61 | msg_stream << *msg; |
62 | ++msg; |
63 | } |
64 | if (!got_newline) msg_stream << "\n" ; |
65 | |
66 | if (e.traces.empty()) { |
67 | // we normally should have some traces, still here as a fallback |
68 | sass::string rel_path(Sass::File::abs2rel(path: e.pstate.getPath(), base: cwd, cwd)); |
69 | msg_stream << sass::string(msg_prefix.size() + 2, ' '); |
70 | msg_stream << " on line " << e.pstate.getLine() << " of " << rel_path << "\n" ; |
71 | } |
72 | else { |
73 | sass::string rel_path(Sass::File::abs2rel(path: e.pstate.getPath(), base: cwd, cwd)); |
74 | msg_stream << traces_to_string(traces: e.traces, indent: " " ); |
75 | } |
76 | |
77 | // now create the code trace (ToDo: maybe have util functions?) |
78 | if (e.pstate.position.line != sass::string::npos && |
79 | e.pstate.position.column != sass::string::npos && |
80 | e.pstate.source != nullptr) { |
81 | Offset offset(e.pstate.position); |
82 | size_t lines = offset.line; |
83 | // scan through src until target line |
84 | // move line_beg pointer to line start |
85 | const char* line_beg; |
86 | for (line_beg = e.pstate.getRawData(); *line_beg != '\0'; ++line_beg) { |
87 | if (lines == 0) break; |
88 | if (*line_beg == '\n') --lines; |
89 | } |
90 | // move line_end before next newline character |
91 | const char* line_end; |
92 | for (line_end = line_beg; *line_end != '\0'; ++line_end) { |
93 | if (*line_end == '\n' || *line_end == '\r') break; |
94 | } |
95 | if (*line_end != '\0') ++line_end; |
96 | size_t line_len = line_end - line_beg; |
97 | size_t move_in = 0; size_t shorten = 0; |
98 | size_t left_chars = 42; size_t max_chars = 76; |
99 | // reported excerpt should not exceed `max_chars` chars |
100 | if (offset.column > line_len) left_chars = offset.column; |
101 | if (offset.column > left_chars) move_in = offset.column - left_chars; |
102 | if (line_len > max_chars + move_in) shorten = line_len - move_in - max_chars; |
103 | utf8::advance(it&: line_beg, n: move_in, end: line_end); |
104 | utf8::retreat(it&: line_end, n: shorten, end: line_beg); |
105 | sass::string sanitized; sass::string marker(offset.column - move_in, '-'); |
106 | utf8::replace_invalid(start: line_beg, end: line_end, out: std::back_inserter(x&: sanitized)); |
107 | msg_stream << ">> " << sanitized << "\n" ; |
108 | msg_stream << " " << marker << "^\n" ; |
109 | } |
110 | |
111 | JsonNode* json_err = json_mkobject(); |
112 | json_append_member(object: json_err, key: "status" , value: json_mknumber(n: 1)); |
113 | json_append_member(object: json_err, key: "file" , value: json_mkstring(s: e.pstate.getPath())); |
114 | json_append_member(object: json_err, key: "line" , value: json_mknumber(n: (double)(e.pstate.getLine()))); |
115 | json_append_member(object: json_err, key: "column" , value: json_mknumber(n: (double)(e.pstate.getColumn()))); |
116 | json_append_member(object: json_err, key: "message" , value: json_mkstring(s: e.what())); |
117 | json_append_member(object: json_err, key: "formatted" , value: json_mkstream(stream: msg_stream)); |
118 | try { c_ctx->error_json = json_stringify(node: json_err, space: " " ); } |
119 | catch (...) {} // silently ignore this error? |
120 | c_ctx->error_message = sass_copy_string(str: msg_stream.str()); |
121 | c_ctx->error_text = sass_copy_c_string(str: e.what()); |
122 | c_ctx->error_status = 1; |
123 | c_ctx->error_file = sass_copy_c_string(str: e.pstate.getPath()); |
124 | c_ctx->error_line = e.pstate.getLine(); |
125 | c_ctx->error_column = e.pstate.getColumn(); |
126 | c_ctx->error_src = sass_copy_c_string(str: e.pstate.getRawData()); |
127 | c_ctx->output_string = 0; |
128 | c_ctx->source_map_string = 0; |
129 | json_delete(node: json_err); |
130 | } |
131 | catch (std::bad_alloc& ba) { |
132 | sass::ostream msg_stream; |
133 | msg_stream << "Unable to allocate memory: " << ba.what(); |
134 | handle_string_error(c_ctx, msg: msg_stream.str(), severety: 2); |
135 | } |
136 | catch (std::exception& e) { |
137 | handle_string_error(c_ctx, msg: e.what(), severety: 3); |
138 | } |
139 | catch (sass::string& e) { |
140 | handle_string_error(c_ctx, msg: e, severety: 4); |
141 | } |
142 | catch (const char* e) { |
143 | handle_string_error(c_ctx, msg: e, severety: 4); |
144 | } |
145 | catch (...) { |
146 | handle_string_error(c_ctx, msg: "unknown" , severety: 5); |
147 | } |
148 | return c_ctx->error_status; |
149 | } |
150 | |
151 | // allow one error handler to throw another error |
152 | // this can happen with invalid utf8 and json lib |
153 | static int handle_errors(Sass_Context* c_ctx) { |
154 | try { return handle_error(c_ctx); } |
155 | catch (...) { return handle_error(c_ctx); } |
156 | } |
157 | |
158 | static Block_Obj sass_parse_block(Sass_Compiler* compiler) throw() |
159 | { |
160 | |
161 | // assert valid pointer |
162 | if (compiler == 0) return {}; |
163 | // The cpp context must be set by now |
164 | Context* cpp_ctx = compiler->cpp_ctx; |
165 | Sass_Context* c_ctx = compiler->c_ctx; |
166 | // We will take care to wire up the rest |
167 | compiler->cpp_ctx->c_compiler = compiler; |
168 | compiler->state = SASS_COMPILER_PARSED; |
169 | |
170 | try { |
171 | |
172 | // get input/output path from options |
173 | sass::string input_path = safe_str(c_ctx->input_path); |
174 | sass::string output_path = safe_str(c_ctx->output_path); |
175 | |
176 | // maybe skip some entries of included files |
177 | // we do not include stdin for data contexts |
178 | bool skip = c_ctx->type == SASS_CONTEXT_DATA; |
179 | |
180 | // dispatch parse call |
181 | Block_Obj root(cpp_ctx->parse()); |
182 | // abort on errors |
183 | if (!root) return {}; |
184 | |
185 | // skip all prefixed files? (ToDo: check srcmap) |
186 | // IMO source-maps should point to headers already |
187 | // therefore don't skip it for now. re-enable or |
188 | // remove completely once this is tested |
189 | size_t = cpp_ctx->head_imports; |
190 | |
191 | // copy the included files on to the context (dont forget to free later) |
192 | if (copy_strings(cpp_ctx->get_included_files(skip, headers), &c_ctx->included_files) == NULL) |
193 | throw(std::bad_alloc()); |
194 | |
195 | // return parsed block |
196 | return root; |
197 | |
198 | } |
199 | // pass errors to generic error handler |
200 | catch (...) { handle_errors(c_ctx); } |
201 | |
202 | // error |
203 | return {}; |
204 | |
205 | } |
206 | |
207 | } |
208 | |
209 | extern "C" { |
210 | using namespace Sass; |
211 | |
212 | static void sass_clear_options (struct Sass_Options* options); |
213 | static void sass_reset_options (struct Sass_Options* options); |
214 | static void copy_options(struct Sass_Options* to, struct Sass_Options* from) { |
215 | // do not overwrite ourself |
216 | if (to == from) return; |
217 | // free assigned memory |
218 | sass_clear_options(options: to); |
219 | // move memory |
220 | *to = *from; |
221 | // Reset pointers on source |
222 | sass_reset_options(options: from); |
223 | } |
224 | |
225 | #define IMPLEMENT_SASS_OPTION_ACCESSOR(type, option) \ |
226 | type ADDCALL sass_option_get_##option (struct Sass_Options* options) { return options->option; } \ |
227 | void ADDCALL sass_option_set_##option (struct Sass_Options* options, type option) { options->option = option; } |
228 | #define IMPLEMENT_SASS_OPTION_STRING_GETTER(type, option, def) \ |
229 | type ADDCALL sass_option_get_##option (struct Sass_Options* options) { return safe_str(options->option, def); } |
230 | #define IMPLEMENT_SASS_OPTION_STRING_SETTER(type, option, def) \ |
231 | void ADDCALL sass_option_set_##option (struct Sass_Options* options, type option) \ |
232 | { free(options->option); options->option = option || def ? sass_copy_c_string(option ? option : def) : 0; } |
233 | #define IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(type, option, def) \ |
234 | IMPLEMENT_SASS_OPTION_STRING_GETTER(type, option, def) \ |
235 | IMPLEMENT_SASS_OPTION_STRING_SETTER(type, option, def) |
236 | |
237 | #define IMPLEMENT_SASS_CONTEXT_GETTER(type, option) \ |
238 | type ADDCALL sass_context_get_##option (struct Sass_Context* ctx) { return ctx->option; } |
239 | #define IMPLEMENT_SASS_CONTEXT_TAKER(type, option) \ |
240 | type sass_context_take_##option (struct Sass_Context* ctx) \ |
241 | { type foo = ctx->option; ctx->option = 0; return foo; } |
242 | |
243 | |
244 | // generic compilation function (not exported, use file/data compile instead) |
245 | static Sass_Compiler* sass_prepare_context (Sass_Context* c_ctx, Context* cpp_ctx) throw() |
246 | { |
247 | try { |
248 | // register our custom functions |
249 | if (c_ctx->c_functions) { |
250 | auto this_func_data = c_ctx->c_functions; |
251 | while (this_func_data && *this_func_data) { |
252 | cpp_ctx->add_c_function(function: *this_func_data); |
253 | ++this_func_data; |
254 | } |
255 | } |
256 | |
257 | // register our custom headers |
258 | if (c_ctx->c_headers) { |
259 | auto this_head_data = c_ctx->c_headers; |
260 | while (this_head_data && *this_head_data) { |
261 | cpp_ctx->add_c_header(header: *this_head_data); |
262 | ++this_head_data; |
263 | } |
264 | } |
265 | |
266 | // register our custom importers |
267 | if (c_ctx->c_importers) { |
268 | auto this_imp_data = c_ctx->c_importers; |
269 | while (this_imp_data && *this_imp_data) { |
270 | cpp_ctx->add_c_importer(importer: *this_imp_data); |
271 | ++this_imp_data; |
272 | } |
273 | } |
274 | |
275 | // reset error status |
276 | c_ctx->error_json = 0; |
277 | c_ctx->error_text = 0; |
278 | c_ctx->error_message = 0; |
279 | c_ctx->error_status = 0; |
280 | // reset error position |
281 | c_ctx->error_file = 0; |
282 | c_ctx->error_src = 0; |
283 | c_ctx->error_line = sass::string::npos; |
284 | c_ctx->error_column = sass::string::npos; |
285 | |
286 | // allocate a new compiler instance |
287 | void* ctxmem = calloc(nmemb: 1, size: sizeof(struct Sass_Compiler)); |
288 | if (ctxmem == 0) { std::cerr << "Error allocating memory for context" << std::endl; return 0; } |
289 | Sass_Compiler* compiler = (struct Sass_Compiler*) ctxmem; |
290 | compiler->state = SASS_COMPILER_CREATED; |
291 | |
292 | // store in sass compiler |
293 | compiler->c_ctx = c_ctx; |
294 | compiler->cpp_ctx = cpp_ctx; |
295 | cpp_ctx->c_compiler = compiler; |
296 | |
297 | // use to parse block |
298 | return compiler; |
299 | |
300 | } |
301 | // pass errors to generic error handler |
302 | catch (...) { handle_errors(c_ctx); } |
303 | |
304 | // error |
305 | return 0; |
306 | |
307 | } |
308 | |
309 | // generic compilation function (not exported, use file/data compile instead) |
310 | static int sass_compile_context (Sass_Context* c_ctx, Context* cpp_ctx) |
311 | { |
312 | |
313 | // prepare sass compiler with context and options |
314 | Sass_Compiler* compiler = sass_prepare_context(c_ctx, cpp_ctx); |
315 | |
316 | try { |
317 | // call each compiler step |
318 | sass_compiler_parse(compiler); |
319 | sass_compiler_execute(compiler); |
320 | } |
321 | // pass errors to generic error handler |
322 | catch (...) { handle_errors(c_ctx); } |
323 | |
324 | sass_delete_compiler(compiler); |
325 | |
326 | return c_ctx->error_status; |
327 | } |
328 | |
329 | inline void init_options (struct Sass_Options* options) |
330 | { |
331 | options->precision = 10; |
332 | options->indent = " " ; |
333 | options->linefeed = LFEED; |
334 | } |
335 | |
336 | Sass_Options* ADDCALL sass_make_options (void) |
337 | { |
338 | struct Sass_Options* options = (struct Sass_Options*) calloc(nmemb: 1, size: sizeof(struct Sass_Options)); |
339 | if (options == 0) { std::cerr << "Error allocating memory for options" << std::endl; return 0; } |
340 | init_options(options); |
341 | return options; |
342 | } |
343 | |
344 | Sass_File_Context* ADDCALL sass_make_file_context(const char* input_path) |
345 | { |
346 | #ifdef DEBUG_SHARED_PTR |
347 | SharedObj::setTaint(true); |
348 | #endif |
349 | struct Sass_File_Context* ctx = (struct Sass_File_Context*) calloc(nmemb: 1, size: sizeof(struct Sass_File_Context)); |
350 | if (ctx == 0) { std::cerr << "Error allocating memory for file context" << std::endl; return 0; } |
351 | ctx->type = SASS_CONTEXT_FILE; |
352 | init_options(options: ctx); |
353 | try { |
354 | if (input_path == 0) { throw(std::runtime_error("File context created without an input path" )); } |
355 | if (*input_path == 0) { throw(std::runtime_error("File context created with empty input path" )); } |
356 | sass_option_set_input_path(options: ctx, input_path); |
357 | } catch (...) { |
358 | handle_errors(c_ctx: ctx); |
359 | } |
360 | return ctx; |
361 | } |
362 | |
363 | Sass_Data_Context* ADDCALL sass_make_data_context(char* source_string) |
364 | { |
365 | #ifdef DEBUG_SHARED_PTR |
366 | SharedObj::setTaint(true); |
367 | #endif |
368 | struct Sass_Data_Context* ctx = (struct Sass_Data_Context*) calloc(nmemb: 1, size: sizeof(struct Sass_Data_Context)); |
369 | if (ctx == 0) { std::cerr << "Error allocating memory for data context" << std::endl; return 0; } |
370 | ctx->type = SASS_CONTEXT_DATA; |
371 | init_options(options: ctx); |
372 | try { |
373 | if (source_string == 0) { throw(std::runtime_error("Data context created without a source string" )); } |
374 | if (*source_string == 0) { throw(std::runtime_error("Data context created with empty source string" )); } |
375 | ctx->source_string = source_string; |
376 | } catch (...) { |
377 | handle_errors(c_ctx: ctx); |
378 | } |
379 | return ctx; |
380 | } |
381 | |
382 | struct Sass_Compiler* ADDCALL sass_make_data_compiler (struct Sass_Data_Context* data_ctx) |
383 | { |
384 | if (data_ctx == 0) return 0; |
385 | Context* cpp_ctx = new Data_Context(*data_ctx); |
386 | return sass_prepare_context(c_ctx: data_ctx, cpp_ctx); |
387 | } |
388 | |
389 | struct Sass_Compiler* ADDCALL sass_make_file_compiler (struct Sass_File_Context* file_ctx) |
390 | { |
391 | if (file_ctx == 0) return 0; |
392 | Context* cpp_ctx = new File_Context(*file_ctx); |
393 | return sass_prepare_context(c_ctx: file_ctx, cpp_ctx); |
394 | } |
395 | |
396 | int ADDCALL sass_compile_data_context(Sass_Data_Context* data_ctx) |
397 | { |
398 | if (data_ctx == 0) return 1; |
399 | if (data_ctx->error_status) |
400 | return data_ctx->error_status; |
401 | try { |
402 | if (data_ctx->source_string == 0) { throw(std::runtime_error("Data context has no source string" )); } |
403 | // empty source string is a valid case, even if not really useful (different than with file context) |
404 | // if (*data_ctx->source_string == 0) { throw(std::runtime_error("Data context has empty source string")); } |
405 | } |
406 | catch (...) { return handle_errors(c_ctx: data_ctx) | 1; } |
407 | Context* cpp_ctx = new Data_Context(*data_ctx); |
408 | return sass_compile_context(c_ctx: data_ctx, cpp_ctx); |
409 | } |
410 | |
411 | int ADDCALL sass_compile_file_context(Sass_File_Context* file_ctx) |
412 | { |
413 | if (file_ctx == 0) return 1; |
414 | if (file_ctx->error_status) |
415 | return file_ctx->error_status; |
416 | try { |
417 | if (file_ctx->input_path == 0) { throw(std::runtime_error("File context has no input path" )); } |
418 | if (*file_ctx->input_path == 0) { throw(std::runtime_error("File context has empty input path" )); } |
419 | } |
420 | catch (...) { return handle_errors(c_ctx: file_ctx) | 1; } |
421 | Context* cpp_ctx = new File_Context(*file_ctx); |
422 | return sass_compile_context(c_ctx: file_ctx, cpp_ctx); |
423 | } |
424 | |
425 | int ADDCALL sass_compiler_parse(struct Sass_Compiler* compiler) |
426 | { |
427 | if (compiler == 0) return 1; |
428 | if (compiler->state == SASS_COMPILER_PARSED) return 0; |
429 | if (compiler->state != SASS_COMPILER_CREATED) return -1; |
430 | if (compiler->c_ctx == NULL) return 1; |
431 | if (compiler->cpp_ctx == NULL) return 1; |
432 | if (compiler->c_ctx->error_status) |
433 | return compiler->c_ctx->error_status; |
434 | // parse the context we have set up (file or data) |
435 | compiler->root = sass_parse_block(compiler); |
436 | // success |
437 | return 0; |
438 | } |
439 | |
440 | int ADDCALL sass_compiler_execute(struct Sass_Compiler* compiler) |
441 | { |
442 | if (compiler == 0) return 1; |
443 | if (compiler->state == SASS_COMPILER_EXECUTED) return 0; |
444 | if (compiler->state != SASS_COMPILER_PARSED) return -1; |
445 | if (compiler->c_ctx == NULL) return 1; |
446 | if (compiler->cpp_ctx == NULL) return 1; |
447 | if (compiler->root.isNull()) return 1; |
448 | if (compiler->c_ctx->error_status) |
449 | return compiler->c_ctx->error_status; |
450 | compiler->state = SASS_COMPILER_EXECUTED; |
451 | Context* cpp_ctx = compiler->cpp_ctx; |
452 | Block_Obj root = compiler->root; |
453 | // compile the parsed root block |
454 | try { compiler->c_ctx->output_string = cpp_ctx->render(root); } |
455 | // pass catched errors to generic error handler |
456 | catch (...) { return handle_errors(c_ctx: compiler->c_ctx) | 1; } |
457 | // generate source map json and store on context |
458 | compiler->c_ctx->source_map_string = cpp_ctx->render_srcmap(); |
459 | // success |
460 | return 0; |
461 | } |
462 | |
463 | // helper function, not exported, only accessible locally |
464 | static void sass_reset_options (struct Sass_Options* options) |
465 | { |
466 | // free pointer before |
467 | // or copy/move them |
468 | options->input_path = 0; |
469 | options->output_path = 0; |
470 | options->plugin_path = 0; |
471 | options->include_path = 0; |
472 | options->source_map_file = 0; |
473 | options->source_map_root = 0; |
474 | options->c_functions = 0; |
475 | options->c_importers = 0; |
476 | options->c_headers = 0; |
477 | options->plugin_paths = 0; |
478 | options->include_paths = 0; |
479 | } |
480 | |
481 | // helper function, not exported, only accessible locally |
482 | static void sass_clear_options (struct Sass_Options* options) |
483 | { |
484 | if (options == 0) return; |
485 | // Deallocate custom functions, headers and imports |
486 | sass_delete_function_list(list: options->c_functions); |
487 | sass_delete_importer_list(list: options->c_importers); |
488 | sass_delete_importer_list(list: options->c_headers); |
489 | // Deallocate inc paths |
490 | if (options->plugin_paths) { |
491 | struct string_list* cur; |
492 | struct string_list* next; |
493 | cur = options->plugin_paths; |
494 | while (cur) { |
495 | next = cur->next; |
496 | free(ptr: cur->string); |
497 | free(ptr: cur); |
498 | cur = next; |
499 | } |
500 | } |
501 | // Deallocate inc paths |
502 | if (options->include_paths) { |
503 | struct string_list* cur; |
504 | struct string_list* next; |
505 | cur = options->include_paths; |
506 | while (cur) { |
507 | next = cur->next; |
508 | free(ptr: cur->string); |
509 | free(ptr: cur); |
510 | cur = next; |
511 | } |
512 | } |
513 | // Free options strings |
514 | free(ptr: options->input_path); |
515 | free(ptr: options->output_path); |
516 | free(ptr: options->plugin_path); |
517 | free(ptr: options->include_path); |
518 | free(ptr: options->source_map_file); |
519 | free(ptr: options->source_map_root); |
520 | // Reset our pointers |
521 | options->input_path = 0; |
522 | options->output_path = 0; |
523 | options->plugin_path = 0; |
524 | options->include_path = 0; |
525 | options->source_map_file = 0; |
526 | options->source_map_root = 0; |
527 | options->c_functions = 0; |
528 | options->c_importers = 0; |
529 | options->c_headers = 0; |
530 | options->plugin_paths = 0; |
531 | options->include_paths = 0; |
532 | } |
533 | |
534 | // helper function, not exported, only accessible locally |
535 | // sass_free_context is also defined in old sass_interface |
536 | static void sass_clear_context (struct Sass_Context* ctx) |
537 | { |
538 | if (ctx == 0) return; |
539 | // release the allocated memory (mostly via sass_copy_c_string) |
540 | if (ctx->output_string) free(ptr: ctx->output_string); |
541 | if (ctx->source_map_string) free(ptr: ctx->source_map_string); |
542 | if (ctx->error_message) free(ptr: ctx->error_message); |
543 | if (ctx->error_text) free(ptr: ctx->error_text); |
544 | if (ctx->error_json) free(ptr: ctx->error_json); |
545 | if (ctx->error_file) free(ptr: ctx->error_file); |
546 | if (ctx->error_src) free(ptr: ctx->error_src); |
547 | free_string_array(ctx->included_files); |
548 | // play safe and reset properties |
549 | ctx->output_string = 0; |
550 | ctx->source_map_string = 0; |
551 | ctx->error_message = 0; |
552 | ctx->error_text = 0; |
553 | ctx->error_json = 0; |
554 | ctx->error_file = 0; |
555 | ctx->error_src = 0; |
556 | ctx->included_files = 0; |
557 | // debug leaked memory |
558 | #ifdef DEBUG_SHARED_PTR |
559 | SharedObj::dumpMemLeaks(); |
560 | #endif |
561 | // now clear the options |
562 | sass_clear_options(options: ctx); |
563 | } |
564 | |
565 | void ADDCALL sass_delete_compiler (struct Sass_Compiler* compiler) |
566 | { |
567 | if (compiler == 0) { |
568 | return; |
569 | } |
570 | Context* cpp_ctx = compiler->cpp_ctx; |
571 | if (cpp_ctx) delete(cpp_ctx); |
572 | compiler->cpp_ctx = NULL; |
573 | compiler->c_ctx = NULL; |
574 | compiler->root = {}; |
575 | free(ptr: compiler); |
576 | } |
577 | |
578 | void ADDCALL sass_delete_options (struct Sass_Options* options) |
579 | { |
580 | sass_clear_options(options); free(ptr: options); |
581 | } |
582 | |
583 | // Deallocate all associated memory with file context |
584 | void ADDCALL sass_delete_file_context (struct Sass_File_Context* ctx) |
585 | { |
586 | // clear the context and free it |
587 | sass_clear_context(ctx); free(ptr: ctx); |
588 | } |
589 | // Deallocate all associated memory with data context |
590 | void ADDCALL sass_delete_data_context (struct Sass_Data_Context* ctx) |
591 | { |
592 | // clean the source string if it was not passed |
593 | // we reset this member once we start parsing |
594 | if (ctx->source_string) free(ptr: ctx->source_string); |
595 | // clear the context and free it |
596 | sass_clear_context(ctx); free(ptr: ctx); |
597 | } |
598 | |
599 | // Getters for sass context from specific implementations |
600 | struct Sass_Context* ADDCALL sass_file_context_get_context(struct Sass_File_Context* ctx) { return ctx; } |
601 | struct Sass_Context* ADDCALL sass_data_context_get_context(struct Sass_Data_Context* ctx) { return ctx; } |
602 | |
603 | // Getters for context options from Sass_Context |
604 | struct Sass_Options* ADDCALL sass_context_get_options(struct Sass_Context* ctx) { return ctx; } |
605 | struct Sass_Options* ADDCALL sass_file_context_get_options(struct Sass_File_Context* ctx) { return ctx; } |
606 | struct Sass_Options* ADDCALL sass_data_context_get_options(struct Sass_Data_Context* ctx) { return ctx; } |
607 | void ADDCALL sass_file_context_set_options (struct Sass_File_Context* ctx, struct Sass_Options* opt) { copy_options(to: ctx, from: opt); } |
608 | void ADDCALL sass_data_context_set_options (struct Sass_Data_Context* ctx, struct Sass_Options* opt) { copy_options(to: ctx, from: opt); } |
609 | |
610 | // Getters for Sass_Compiler options (get connected sass context) |
611 | enum Sass_Compiler_State ADDCALL sass_compiler_get_state(struct Sass_Compiler* compiler) { return compiler->state; } |
612 | struct Sass_Context* ADDCALL sass_compiler_get_context(struct Sass_Compiler* compiler) { return compiler->c_ctx; } |
613 | struct Sass_Options* ADDCALL sass_compiler_get_options(struct Sass_Compiler* compiler) { return compiler->c_ctx; } |
614 | // Getters for Sass_Compiler options (query import stack) |
615 | size_t ADDCALL sass_compiler_get_import_stack_size(struct Sass_Compiler* compiler) { return compiler->cpp_ctx->import_stack.size(); } |
616 | Sass_Import_Entry ADDCALL sass_compiler_get_last_import(struct Sass_Compiler* compiler) { return compiler->cpp_ctx->import_stack.back(); } |
617 | Sass_Import_Entry ADDCALL sass_compiler_get_import_entry(struct Sass_Compiler* compiler, size_t idx) { return compiler->cpp_ctx->import_stack[idx]; } |
618 | // Getters for Sass_Compiler options (query function stack) |
619 | size_t ADDCALL sass_compiler_get_callee_stack_size(struct Sass_Compiler* compiler) { return compiler->cpp_ctx->callee_stack.size(); } |
620 | Sass_Callee_Entry ADDCALL sass_compiler_get_last_callee(struct Sass_Compiler* compiler) { return &compiler->cpp_ctx->callee_stack.back(); } |
621 | Sass_Callee_Entry ADDCALL sass_compiler_get_callee_entry(struct Sass_Compiler* compiler, size_t idx) { return &compiler->cpp_ctx->callee_stack[idx]; } |
622 | |
623 | // Calculate the size of the stored null terminated array |
624 | size_t ADDCALL sass_context_get_included_files_size (struct Sass_Context* ctx) |
625 | { size_t l = 0; auto i = ctx->included_files; while (i && *i) { ++i; ++l; } return l; } |
626 | |
627 | // Create getter and setters for options |
628 | IMPLEMENT_SASS_OPTION_ACCESSOR(int, precision); |
629 | IMPLEMENT_SASS_OPTION_ACCESSOR(enum Sass_Output_Style, output_style); |
630 | IMPLEMENT_SASS_OPTION_ACCESSOR(bool, ); |
631 | IMPLEMENT_SASS_OPTION_ACCESSOR(bool, source_map_embed); |
632 | IMPLEMENT_SASS_OPTION_ACCESSOR(bool, source_map_contents); |
633 | IMPLEMENT_SASS_OPTION_ACCESSOR(bool, source_map_file_urls); |
634 | IMPLEMENT_SASS_OPTION_ACCESSOR(bool, omit_source_map_url); |
635 | IMPLEMENT_SASS_OPTION_ACCESSOR(bool, is_indented_syntax_src); |
636 | IMPLEMENT_SASS_OPTION_ACCESSOR(Sass_Function_List, c_functions); |
637 | IMPLEMENT_SASS_OPTION_ACCESSOR(Sass_Importer_List, c_importers); |
638 | IMPLEMENT_SASS_OPTION_ACCESSOR(Sass_Importer_List, ); |
639 | IMPLEMENT_SASS_OPTION_ACCESSOR(const char*, indent); |
640 | IMPLEMENT_SASS_OPTION_ACCESSOR(const char*, linefeed); |
641 | IMPLEMENT_SASS_OPTION_STRING_SETTER(const char*, plugin_path, 0); |
642 | IMPLEMENT_SASS_OPTION_STRING_SETTER(const char*, include_path, 0); |
643 | IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, input_path, 0); |
644 | IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, output_path, 0); |
645 | IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, source_map_file, 0); |
646 | IMPLEMENT_SASS_OPTION_STRING_ACCESSOR(const char*, source_map_root, 0); |
647 | |
648 | // Create getter and setters for context |
649 | IMPLEMENT_SASS_CONTEXT_GETTER(int, error_status); |
650 | IMPLEMENT_SASS_CONTEXT_GETTER(const char*, error_json); |
651 | IMPLEMENT_SASS_CONTEXT_GETTER(const char*, error_message); |
652 | IMPLEMENT_SASS_CONTEXT_GETTER(const char*, error_text); |
653 | IMPLEMENT_SASS_CONTEXT_GETTER(const char*, error_file); |
654 | IMPLEMENT_SASS_CONTEXT_GETTER(const char*, error_src); |
655 | IMPLEMENT_SASS_CONTEXT_GETTER(size_t, error_line); |
656 | IMPLEMENT_SASS_CONTEXT_GETTER(size_t, error_column); |
657 | IMPLEMENT_SASS_CONTEXT_GETTER(const char*, output_string); |
658 | IMPLEMENT_SASS_CONTEXT_GETTER(const char*, source_map_string); |
659 | IMPLEMENT_SASS_CONTEXT_GETTER(char**, included_files); |
660 | |
661 | // Take ownership of memory (value on context is set to 0) |
662 | IMPLEMENT_SASS_CONTEXT_TAKER(char*, error_json); |
663 | IMPLEMENT_SASS_CONTEXT_TAKER(char*, error_message); |
664 | IMPLEMENT_SASS_CONTEXT_TAKER(char*, error_text); |
665 | IMPLEMENT_SASS_CONTEXT_TAKER(char*, error_file); |
666 | IMPLEMENT_SASS_CONTEXT_TAKER(char*, error_src); |
667 | IMPLEMENT_SASS_CONTEXT_TAKER(char*, output_string); |
668 | IMPLEMENT_SASS_CONTEXT_TAKER(char*, source_map_string); |
669 | IMPLEMENT_SASS_CONTEXT_TAKER(char**, included_files); |
670 | |
671 | // Push function for include paths (no manipulation support for now) |
672 | void ADDCALL sass_option_push_include_path(struct Sass_Options* options, const char* path) |
673 | { |
674 | |
675 | struct string_list* include_path = (struct string_list*) calloc(nmemb: 1, size: sizeof(struct string_list)); |
676 | if (include_path == 0) return; |
677 | include_path->string = path ? sass_copy_c_string(str: path) : 0; |
678 | struct string_list* last = options->include_paths; |
679 | if (!options->include_paths) { |
680 | options->include_paths = include_path; |
681 | } else { |
682 | while (last->next) |
683 | last = last->next; |
684 | last->next = include_path; |
685 | } |
686 | |
687 | } |
688 | |
689 | // Push function for include paths (no manipulation support for now) |
690 | size_t ADDCALL sass_option_get_include_path_size(struct Sass_Options* options) |
691 | { |
692 | size_t len = 0; |
693 | struct string_list* cur = options->include_paths; |
694 | while (cur) { len ++; cur = cur->next; } |
695 | return len; |
696 | } |
697 | |
698 | // Push function for include paths (no manipulation support for now) |
699 | const char* ADDCALL sass_option_get_include_path(struct Sass_Options* options, size_t i) |
700 | { |
701 | struct string_list* cur = options->include_paths; |
702 | while (i) { i--; cur = cur->next; } |
703 | return cur->string; |
704 | } |
705 | |
706 | // Push function for plugin paths (no manipulation support for now) |
707 | size_t ADDCALL sass_option_get_plugin_path_size(struct Sass_Options* options) |
708 | { |
709 | size_t len = 0; |
710 | struct string_list* cur = options->plugin_paths; |
711 | while (cur) { len++; cur = cur->next; } |
712 | return len; |
713 | } |
714 | |
715 | // Push function for plugin paths (no manipulation support for now) |
716 | const char* ADDCALL sass_option_get_plugin_path(struct Sass_Options* options, size_t i) |
717 | { |
718 | struct string_list* cur = options->plugin_paths; |
719 | while (i) { i--; cur = cur->next; } |
720 | return cur->string; |
721 | } |
722 | |
723 | // Push function for plugin paths (no manipulation support for now) |
724 | void ADDCALL sass_option_push_plugin_path(struct Sass_Options* options, const char* path) |
725 | { |
726 | |
727 | struct string_list* plugin_path = (struct string_list*) calloc(nmemb: 1, size: sizeof(struct string_list)); |
728 | if (plugin_path == 0) return; |
729 | plugin_path->string = path ? sass_copy_c_string(str: path) : 0; |
730 | struct string_list* last = options->plugin_paths; |
731 | if (!options->plugin_paths) { |
732 | options->plugin_paths = plugin_path; |
733 | } else { |
734 | while (last->next) |
735 | last = last->next; |
736 | last->next = plugin_path; |
737 | } |
738 | |
739 | } |
740 | |
741 | } |
742 | |