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)
json_mkstream(const sass::ostream & stream)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(str.c_str());
19   }
20 
handle_string_error(Sass_Context * c_ctx,const sass::string & msg,int severety)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(json_err, "status", json_mknumber(severety));
27     json_append_member(json_err, "message", json_mkstring(msg.c_str()));
28     json_append_member(json_err, "formatted", json_mkstream(msg_stream));
29     try { c_ctx->error_json = json_stringify(json_err, "  "); }
30     catch (...) {}
31     c_ctx->error_message = sass_copy_string(msg_stream.str());
32     c_ctx->error_text = sass_copy_c_string(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(json_err);
37   }
38 
handle_error(Sass_Context * c_ctx)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(e.pstate.getPath(), 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(e.pstate.getPath(), cwd, cwd));
74         msg_stream << traces_to_string(e.traces, "        ");
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(line_beg, move_in, line_end);
104         utf8::retreat(line_end, shorten, line_beg);
105         sass::string sanitized; sass::string marker(offset.column - move_in, '-');
106         utf8::replace_invalid(line_beg, line_end, std::back_inserter(sanitized));
107         msg_stream << ">> " << sanitized << "\n";
108         msg_stream << "   " << marker << "^\n";
109       }
110 
111       JsonNode* json_err = json_mkobject();
112       json_append_member(json_err, "status", json_mknumber(1));
113       json_append_member(json_err, "file", json_mkstring(e.pstate.getPath()));
114       json_append_member(json_err, "line", json_mknumber((double)(e.pstate.getLine())));
115       json_append_member(json_err, "column", json_mknumber((double)(e.pstate.getColumn())));
116       json_append_member(json_err, "message", json_mkstring(e.what()));
117       json_append_member(json_err, "formatted", json_mkstream(msg_stream));
118       try { c_ctx->error_json = json_stringify(json_err, "  "); }
119       catch (...) {} // silently ignore this error?
120       c_ctx->error_message = sass_copy_string(msg_stream.str());
121       c_ctx->error_text = sass_copy_c_string(e.what());
122       c_ctx->error_status = 1;
123       c_ctx->error_file = sass_copy_c_string(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(e.pstate.getRawData());
127       c_ctx->output_string = 0;
128       c_ctx->source_map_string = 0;
129       json_delete(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_stream.str(), 2);
135     }
136     catch (std::exception& e) {
137       handle_string_error(c_ctx, e.what(), 3);
138     }
139     catch (sass::string& e) {
140       handle_string_error(c_ctx, e, 4);
141     }
142     catch (const char* e) {
143       handle_string_error(c_ctx, e, 4);
144     }
145     catch (...) {
146       handle_string_error(c_ctx, "unknown", 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
handle_errors(Sass_Context * c_ctx)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 
sass_parse_block(Sass_Compiler * compiler)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 headers = 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);
copy_options(struct Sass_Options * to,struct Sass_Options * from)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(to);
219     // move memory
220     *to = *from;
221     // Reset pointers on source
222     sass_reset_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)
sass_prepare_context(Sass_Context * c_ctx,Context * cpp_ctx)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(*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(*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(*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(1, 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)
sass_compile_context(Sass_Context * c_ctx,Context * cpp_ctx)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 
init_options(struct Sass_Options * options)329   inline void init_options (struct Sass_Options* options)
330   {
331     options->precision = 10;
332     options->indent = "  ";
333     options->linefeed = LFEED;
334   }
335 
sass_make_options(void)336   Sass_Options* ADDCALL sass_make_options (void)
337   {
338     struct Sass_Options* options = (struct Sass_Options*) calloc(1, 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 
sass_make_file_context(const char * input_path)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(1, 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(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(ctx, input_path);
357     } catch (...) {
358       handle_errors(ctx);
359     }
360     return ctx;
361   }
362 
sass_make_data_context(char * source_string)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(1, 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(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(ctx);
378     }
379     return ctx;
380   }
381 
sass_make_data_compiler(struct Sass_Data_Context * data_ctx)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(data_ctx, cpp_ctx);
387   }
388 
sass_make_file_compiler(struct Sass_File_Context * file_ctx)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(file_ctx, cpp_ctx);
394   }
395 
sass_compile_data_context(Sass_Data_Context * data_ctx)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(data_ctx) | 1; }
407     Context* cpp_ctx = new Data_Context(*data_ctx);
408     return sass_compile_context(data_ctx, cpp_ctx);
409   }
410 
sass_compile_file_context(Sass_File_Context * file_ctx)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(file_ctx) | 1; }
421     Context* cpp_ctx = new File_Context(*file_ctx);
422     return sass_compile_context(file_ctx, cpp_ctx);
423   }
424 
sass_compiler_parse(struct Sass_Compiler * compiler)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 
sass_compiler_execute(struct Sass_Compiler * compiler)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(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
sass_reset_options(struct Sass_Options * options)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
sass_clear_options(struct Sass_Options * options)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(options->c_functions);
487     sass_delete_importer_list(options->c_importers);
488     sass_delete_importer_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(cur->string);
497         free(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(cur->string);
509         free(cur);
510         cur = next;
511       }
512     }
513     // Free options strings
514     free(options->input_path);
515     free(options->output_path);
516     free(options->plugin_path);
517     free(options->include_path);
518     free(options->source_map_file);
519     free(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
sass_clear_context(struct Sass_Context * ctx)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(ctx->output_string);
541     if (ctx->source_map_string) free(ctx->source_map_string);
542     if (ctx->error_message)     free(ctx->error_message);
543     if (ctx->error_text)        free(ctx->error_text);
544     if (ctx->error_json)        free(ctx->error_json);
545     if (ctx->error_file)        free(ctx->error_file);
546     if (ctx->error_src)         free(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(ctx);
563   }
564 
sass_delete_compiler(struct Sass_Compiler * compiler)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(compiler);
576   }
577 
sass_delete_options(struct Sass_Options * options)578   void ADDCALL sass_delete_options (struct Sass_Options* options)
579   {
580     sass_clear_options(options); free(options);
581   }
582 
583   // Deallocate all associated memory with file context
sass_delete_file_context(struct Sass_File_Context * ctx)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(ctx);
588   }
589   // Deallocate all associated memory with data context
sass_delete_data_context(struct Sass_Data_Context * ctx)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(ctx->source_string);
595     // clear the context and free it
596     sass_clear_context(ctx); free(ctx);
597   }
598 
599   // Getters for sass context from specific implementations
sass_file_context_get_context(struct Sass_File_Context * ctx)600   struct Sass_Context* ADDCALL sass_file_context_get_context(struct Sass_File_Context* ctx) { return ctx; }
sass_data_context_get_context(struct Sass_Data_Context * 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
sass_context_get_options(struct Sass_Context * ctx)604   struct Sass_Options* ADDCALL sass_context_get_options(struct Sass_Context* ctx) { return ctx; }
sass_file_context_get_options(struct Sass_File_Context * ctx)605   struct Sass_Options* ADDCALL sass_file_context_get_options(struct Sass_File_Context* ctx) { return ctx; }
sass_data_context_get_options(struct Sass_Data_Context * ctx)606   struct Sass_Options* ADDCALL sass_data_context_get_options(struct Sass_Data_Context* ctx) { return ctx; }
sass_file_context_set_options(struct Sass_File_Context * ctx,struct Sass_Options * opt)607   void ADDCALL sass_file_context_set_options (struct Sass_File_Context* ctx, struct Sass_Options* opt) { copy_options(ctx, opt); }
sass_data_context_set_options(struct Sass_Data_Context * ctx,struct Sass_Options * opt)608   void ADDCALL sass_data_context_set_options (struct Sass_Data_Context* ctx, struct Sass_Options* opt) { copy_options(ctx, opt); }
609 
610   // Getters for Sass_Compiler options (get connected sass context)
sass_compiler_get_state(struct Sass_Compiler * compiler)611   enum Sass_Compiler_State ADDCALL sass_compiler_get_state(struct Sass_Compiler* compiler) { return compiler->state; }
sass_compiler_get_context(struct Sass_Compiler * compiler)612   struct Sass_Context* ADDCALL sass_compiler_get_context(struct Sass_Compiler* compiler) { return compiler->c_ctx; }
sass_compiler_get_options(struct Sass_Compiler * compiler)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)
sass_compiler_get_import_stack_size(struct Sass_Compiler * compiler)615   size_t ADDCALL sass_compiler_get_import_stack_size(struct Sass_Compiler* compiler) { return compiler->cpp_ctx->import_stack.size(); }
sass_compiler_get_last_import(struct Sass_Compiler * compiler)616   Sass_Import_Entry ADDCALL sass_compiler_get_last_import(struct Sass_Compiler* compiler) { return compiler->cpp_ctx->import_stack.back(); }
sass_compiler_get_import_entry(struct Sass_Compiler * compiler,size_t idx)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)
sass_compiler_get_callee_stack_size(struct Sass_Compiler * compiler)619   size_t ADDCALL sass_compiler_get_callee_stack_size(struct Sass_Compiler* compiler) { return compiler->cpp_ctx->callee_stack.size(); }
sass_compiler_get_last_callee(struct Sass_Compiler * compiler)620   Sass_Callee_Entry ADDCALL sass_compiler_get_last_callee(struct Sass_Compiler* compiler) { return &compiler->cpp_ctx->callee_stack.back(); }
sass_compiler_get_callee_entry(struct Sass_Compiler * compiler,size_t idx)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
sass_context_get_included_files_size(struct Sass_Context * ctx)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, source_comments);
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, c_headers);
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)
sass_option_push_include_path(struct Sass_Options * options,const char * path)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(1, sizeof(struct string_list));
676     if (include_path == 0) return;
677     include_path->string = path ? sass_copy_c_string(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)
sass_option_get_include_path_size(struct Sass_Options * options)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)
sass_option_get_include_path(struct Sass_Options * options,size_t i)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)
sass_option_get_plugin_path_size(struct Sass_Options * options)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)
sass_option_get_plugin_path(struct Sass_Options * options,size_t i)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)
sass_option_push_plugin_path(struct Sass_Options * options,const char * path)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(1, sizeof(struct string_list));
728     if (plugin_path == 0) return;
729     plugin_path->string = path ? sass_copy_c_string(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