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 "remove_placeholders.hpp"
7 #include "sass_functions.hpp"
8 #include "check_nesting.hpp"
9 #include "fn_selectors.hpp"
10 #include "fn_strings.hpp"
11 #include "fn_numbers.hpp"
12 #include "fn_colors.hpp"
13 #include "fn_miscs.hpp"
14 #include "fn_lists.hpp"
15 #include "fn_maps.hpp"
16 #include "context.hpp"
17 #include "expand.hpp"
18 #include "parser.hpp"
19 #include "cssize.hpp"
20 #include "source.hpp"
21 
22 namespace Sass {
23   using namespace Constants;
24   using namespace File;
25   using namespace Sass;
26 
27   inline bool sort_importers (const Sass_Importer_Entry& i, const Sass_Importer_Entry& j)
28   { return sass_importer_get_priority(i) > sass_importer_get_priority(j); }
29 
30   static sass::string safe_input(const char* in_path)
31   {
32     if (in_path == nullptr || in_path[0] == '\0') return "stdin";
33     return in_path;
34   }
35 
36   static sass::string safe_output(const char* out_path, sass::string input_path)
37   {
38     if (out_path == nullptr || out_path[0] == '\0') {
39       if (input_path.empty()) return "stdout";
40       return input_path.substr(0, input_path.find_last_of(".")) + ".css";
41     }
42     return out_path;
43   }
44 
45   Context::Context(struct Sass_Context& c_ctx)
46   : CWD(File::get_cwd()),
47     c_options(c_ctx),
48     entry_path(""),
49     head_imports(0),
50     plugins(),
51     emitter(c_options),
52 
53     ast_gc(),
54     strings(),
55     resources(),
56     sheets(),
57     import_stack(),
58     callee_stack(),
59     traces(),
60     extender(Extender::NORMAL, traces),
61     c_compiler(NULL),
62 
63     c_headers               (sass::vector<Sass_Importer_Entry>()),
64     c_importers             (sass::vector<Sass_Importer_Entry>()),
65     c_functions             (sass::vector<Sass_Function_Entry>()),
66 
67     indent                  (safe_str(c_options.indent, "  ")),
68     linefeed                (safe_str(c_options.linefeed, "\n")),
69 
70     input_path              (make_canonical_path(safe_input(c_options.input_path))),
71     output_path             (make_canonical_path(safe_output(c_options.output_path, input_path))),
72     source_map_file         (make_canonical_path(safe_str(c_options.source_map_file, ""))),
73     source_map_root         (make_canonical_path(safe_str(c_options.source_map_root, "")))
74 
75   {
76 
77     // Sass 3.4: The current working directory will no longer be placed onto the Sass load path by default.
78     // If you need the current working directory to be available, set SASS_PATH=. in your shell's environment.
79     // include_paths.push_back(CWD);
80 
81     // collect more paths from different options
82     collect_include_paths(c_options.include_path);
83     collect_include_paths(c_options.include_paths);
84     collect_plugin_paths(c_options.plugin_path);
85     collect_plugin_paths(c_options.plugin_paths);
86 
87     // load plugins and register custom behaviors
88     for(auto plug : plugin_paths) plugins.load_plugins(plug);
89     for(auto fn : plugins.get_headers()) c_headers.push_back(fn);
90     for(auto fn : plugins.get_importers()) c_importers.push_back(fn);
91     for(auto fn : plugins.get_functions()) c_functions.push_back(fn);
92 
93     // sort the items by priority (lowest first)
94     sort (c_headers.begin(), c_headers.end(), sort_importers);
95     sort (c_importers.begin(), c_importers.end(), sort_importers);
96 
97     emitter.set_filename(abs2rel(output_path, source_map_file, CWD));
98 
99   }
100 
101   void Context::add_c_function(Sass_Function_Entry function)
102   {
103     c_functions.push_back(function);
104   }
105   void Context::add_c_header(Sass_Importer_Entry header)
106   {
107     c_headers.push_back(header);
108     // need to sort the array afterwards (no big deal)
109     sort (c_headers.begin(), c_headers.end(), sort_importers);
110   }
111   void Context::add_c_importer(Sass_Importer_Entry importer)
112   {
113     c_importers.push_back(importer);
114     // need to sort the array afterwards (no big deal)
115     sort (c_importers.begin(), c_importers.end(), sort_importers);
116   }
117 
118   Context::~Context()
119   {
120     // resources were allocated by malloc
121     for (size_t i = 0; i < resources.size(); ++i) {
122       free(resources[i].contents);
123       free(resources[i].srcmap);
124     }
125     // free all strings we kept alive during compiler execution
126     for (size_t n = 0; n < strings.size(); ++n) free(strings[n]);
127     // everything that gets put into sources will be freed by us
128     // this shouldn't have anything in it anyway!?
129     for (size_t m = 0; m < import_stack.size(); ++m) {
130       sass_import_take_source(import_stack[m]);
131       sass_import_take_srcmap(import_stack[m]);
132       sass_delete_import(import_stack[m]);
133     }
134     // clear inner structures (vectors) and input source
135     resources.clear(); import_stack.clear();
136     sheets.clear();
137   }
138 
139   Data_Context::~Data_Context()
140   {
141     // --> this will be freed by resources
142     // make sure we free the source even if not processed!
143     // if (resources.size() == 0 && source_c_str) free(source_c_str);
144     // if (resources.size() == 0 && srcmap_c_str) free(srcmap_c_str);
145     // source_c_str = 0; srcmap_c_str = 0;
146   }
147 
148   File_Context::~File_Context()
149   {
150   }
151 
152   void Context::collect_include_paths(const char* paths_str)
153   {
154     if (paths_str) {
155       const char* beg = paths_str;
156       const char* end = Prelexer::find_first<PATH_SEP>(beg);
157 
158       while (end) {
159         sass::string path(beg, end - beg);
160         if (!path.empty()) {
161           if (*path.rbegin() != '/') path += '/';
162           include_paths.push_back(path);
163         }
164         beg = end + 1;
165         end = Prelexer::find_first<PATH_SEP>(beg);
166       }
167 
168       sass::string path(beg);
169       if (!path.empty()) {
170         if (*path.rbegin() != '/') path += '/';
171         include_paths.push_back(path);
172       }
173     }
174   }
175 
176   void Context::collect_include_paths(string_list* paths_array)
177   {
178     while (paths_array)
179     {
180       collect_include_paths(paths_array->string);
181       paths_array = paths_array->next;
182     }
183   }
184 
185   void Context::collect_plugin_paths(const char* paths_str)
186   {
187     if (paths_str) {
188       const char* beg = paths_str;
189       const char* end = Prelexer::find_first<PATH_SEP>(beg);
190 
191       while (end) {
192         sass::string path(beg, end - beg);
193         if (!path.empty()) {
194           if (*path.rbegin() != '/') path += '/';
195           plugin_paths.push_back(path);
196         }
197         beg = end + 1;
198         end = Prelexer::find_first<PATH_SEP>(beg);
199       }
200 
201       sass::string path(beg);
202       if (!path.empty()) {
203         if (*path.rbegin() != '/') path += '/';
204         plugin_paths.push_back(path);
205       }
206     }
207   }
208 
209   void Context::collect_plugin_paths(string_list* paths_array)
210   {
211     while (paths_array)
212     {
213       collect_plugin_paths(paths_array->string);
214       paths_array = paths_array->next;
215     }
216   }
217 
218   // resolve the imp_path in base_path or include_paths
219   // looks for alternatives and returns a list from one directory
220   sass::vector<Include> Context::find_includes(const Importer& import)
221   {
222     // make sure we resolve against an absolute path
223     sass::string base_path(rel2abs(import.base_path));
224     // first try to resolve the load path relative to the base path
225     sass::vector<Include> vec(resolve_includes(base_path, import.imp_path));
226     // then search in every include path (but only if nothing found yet)
227     for (size_t i = 0, S = include_paths.size(); vec.size() == 0 && i < S; ++i)
228     {
229       // call resolve_includes and individual base path and append all results
230       sass::vector<Include> resolved(resolve_includes(include_paths[i], import.imp_path));
231       if (resolved.size()) vec.insert(vec.end(), resolved.begin(), resolved.end());
232     }
233     // return vector
234     return vec;
235   }
236 
237   // register include with resolved path and its content
238   // memory of the resources will be freed by us on exit
239   void Context::register_resource(const Include& inc, const Resource& res)
240   {
241 
242     // do not parse same resource twice
243     // maybe raise an error in this case
244     // if (sheets.count(inc.abs_path)) {
245     //   free(res.contents); free(res.srcmap);
246     //   throw std::runtime_error("duplicate resource registered");
247     //   return;
248     // }
249 
250     // get index for this resource
251     size_t idx = resources.size();
252 
253     // tell emitter about new resource
254     emitter.add_source_index(idx);
255 
256     // put resources under our control
257     // the memory will be freed later
258     resources.push_back(res);
259 
260     // add a relative link to the working directory
261     included_files.push_back(inc.abs_path);
262     // add a relative link  to the source map output file
263     srcmap_links.push_back(abs2rel(inc.abs_path, source_map_file, CWD));
264 
265     // get pointer to the loaded content
266     Sass_Import_Entry import = sass_make_import(
267       inc.imp_path.c_str(),
268       inc.abs_path.c_str(),
269       res.contents,
270       res.srcmap
271     );
272     // add the entry to the stack
273     import_stack.push_back(import);
274 
275     // get pointer to the loaded content
276     const char* contents = resources[idx].contents;
277     SourceFileObj source = SASS_MEMORY_NEW(SourceFile,
278       inc.abs_path.c_str(), contents, idx);
279 
280     // create the initial parser state from resource
281     SourceSpan pstate(source);
282 
283     // check existing import stack for possible recursion
284     for (size_t i = 0; i < import_stack.size() - 2; ++i) {
285       auto parent = import_stack[i];
286       if (std::strcmp(parent->abs_path, import->abs_path) == 0) {
287         sass::string cwd(File::get_cwd());
288         // make path relative to the current directory
289         sass::string stack("An @import loop has been found:");
290         for (size_t n = 1; n < i + 2; ++n) {
291           stack += "\n    " + sass::string(File::abs2rel(import_stack[n]->abs_path, cwd, cwd)) +
292             " imports " + sass::string(File::abs2rel(import_stack[n+1]->abs_path, cwd, cwd));
293         }
294         // implement error throw directly until we
295         // decided how to handle full stack traces
296         throw Exception::InvalidSyntax(pstate, traces, stack);
297         // error(stack, prstate ? *prstate : pstate, import_stack);
298       }
299     }
300 
301     // create a parser instance from the given c_str buffer
302     Parser p(source, *this, traces);
303     // do not yet dispose these buffers
304     sass_import_take_source(import);
305     sass_import_take_srcmap(import);
306     // then parse the root block
307     Block_Obj root = p.parse();
308     // delete memory of current stack frame
309     sass_delete_import(import_stack.back());
310     // remove current stack frame
311     import_stack.pop_back();
312     // create key/value pair for ast node
313     std::pair<const sass::string, StyleSheet>
314       ast_pair(inc.abs_path, { res, root });
315     // register resulting resource
316     sheets.insert(ast_pair);
317   }
318 
319   // register include with resolved path and its content
320   // memory of the resources will be freed by us on exit
321   void Context::register_resource(const Include& inc, const Resource& res, SourceSpan& prstate)
322   {
323     traces.push_back(Backtrace(prstate));
324     register_resource(inc, res);
325     traces.pop_back();
326   }
327 
328   // Add a new import to the context (called from `import_url`)
329   Include Context::load_import(const Importer& imp, SourceSpan pstate)
330   {
331 
332     // search for valid imports (ie. partials) on the filesystem
333     // this may return more than one valid result (ambiguous imp_path)
334     const sass::vector<Include> resolved(find_includes(imp));
335 
336     // error nicely on ambiguous imp_path
337     if (resolved.size() > 1) {
338       sass::ostream msg_stream;
339       msg_stream << "It's not clear which file to import for ";
340       msg_stream << "'@import \"" << imp.imp_path << "\"'." << "\n";
341       msg_stream << "Candidates:" << "\n";
342       for (size_t i = 0, L = resolved.size(); i < L; ++i)
343       { msg_stream << "  " << resolved[i].imp_path << "\n"; }
344       msg_stream << "Please delete or rename all but one of these files." << "\n";
345       error(msg_stream.str(), pstate, traces);
346     }
347 
348     // process the resolved entry
349     else if (resolved.size() == 1) {
350       bool use_cache = c_importers.size() == 0;
351       // use cache for the resource loading
352       if (use_cache && sheets.count(resolved[0].abs_path)) return resolved[0];
353       // try to read the content of the resolved file entry
354       // the memory buffer returned must be freed by us!
355       if (char* contents = read_file(resolved[0].abs_path)) {
356         // register the newly resolved file resource
357         register_resource(resolved[0], { contents, 0 }, pstate);
358         // return resolved entry
359         return resolved[0];
360       }
361     }
362 
363     // nothing found
364     return { imp, "" };
365 
366   }
367 
368   void Context::import_url (Import* imp, sass::string load_path, const sass::string& ctx_path) {
369 
370     SourceSpan pstate(imp->pstate());
371     sass::string imp_path(unquote(load_path));
372     sass::string protocol("file");
373 
374     using namespace Prelexer;
375     if (const char* proto = sequence< identifier, exactly<':'>, exactly<'/'>, exactly<'/'> >(imp_path.c_str())) {
376 
377       protocol = sass::string(imp_path.c_str(), proto - 3);
378       // if (protocol.compare("file") && true) { }
379     }
380 
381     // add urls (protocol other than file) and urls without protocol to `urls` member
382     // ToDo: if ctx_path is already a file resource, we should not add it here?
383     if (imp->import_queries() || protocol != "file" || imp_path.substr(0, 2) == "//") {
384       imp->urls().push_back(SASS_MEMORY_NEW(String_Quoted, imp->pstate(), load_path));
385     }
386     else if (imp_path.length() > 4 && imp_path.substr(imp_path.length() - 4, 4) == ".css") {
387       String_Constant* loc = SASS_MEMORY_NEW(String_Constant, pstate, unquote(load_path));
388       Argument_Obj loc_arg = SASS_MEMORY_NEW(Argument, pstate, loc);
389       Arguments_Obj loc_args = SASS_MEMORY_NEW(Arguments, pstate);
390       loc_args->append(loc_arg);
391       Function_Call* new_url = SASS_MEMORY_NEW(Function_Call, pstate, sass::string("url"), loc_args);
392       imp->urls().push_back(new_url);
393     }
394     else {
395       const Importer importer(imp_path, ctx_path);
396       Include include(load_import(importer, pstate));
397       if (include.abs_path.empty()) {
398         error("File to import not found or unreadable: " + imp_path + ".", pstate, traces);
399       }
400       imp->incs().push_back(include);
401     }
402 
403   }
404 
405 
406   // call custom importers on the given (unquoted) load_path and eventually parse the resulting style_sheet
407   bool Context::call_loader(const sass::string& load_path, const char* ctx_path, SourceSpan& pstate, Import* imp, sass::vector<Sass_Importer_Entry> importers, bool only_one)
408   {
409     // unique counter
410     size_t count = 0;
411     // need one correct import
412     bool has_import = false;
413     // process all custom importers (or custom headers)
414     for (Sass_Importer_Entry& importer_ent : importers) {
415       // int priority = sass_importer_get_priority(importer);
416       Sass_Importer_Fn fn = sass_importer_get_function(importer_ent);
417       // skip importer if it returns NULL
418       if (Sass_Import_List includes =
419           fn(load_path.c_str(), importer_ent, c_compiler)
420       ) {
421         // get c pointer copy to iterate over
422         Sass_Import_List it_includes = includes;
423         while (*it_includes) { ++count;
424           // create unique path to use as key
425           sass::string uniq_path = load_path;
426           if (!only_one && count) {
427             sass::ostream path_strm;
428             path_strm << uniq_path << ":" << count;
429             uniq_path = path_strm.str();
430           }
431           // create the importer struct
432           Importer importer(uniq_path, ctx_path);
433           // query data from the current include
434           Sass_Import_Entry include_ent = *it_includes;
435           char* source = sass_import_take_source(include_ent);
436           char* srcmap = sass_import_take_srcmap(include_ent);
437           size_t line = sass_import_get_error_line(include_ent);
438           size_t column = sass_import_get_error_column(include_ent);
439           const char *abs_path = sass_import_get_abs_path(include_ent);
440           // handle error message passed back from custom importer
441           // it may (or may not) override the line and column info
442           if (const char* err_message = sass_import_get_error_message(include_ent)) {
443             if (source || srcmap) register_resource({ importer, uniq_path }, { source, srcmap }, pstate);
444             if (line == sass::string::npos && column == sass::string::npos) error(err_message, pstate, traces);
445             else { error(err_message, { pstate.source, { line, column } }, traces); }
446           }
447           // content for import was set
448           else if (source) {
449             // resolved abs_path should be set by custom importer
450             // use the created uniq_path as fallback (maybe enforce)
451             sass::string path_key(abs_path ? abs_path : uniq_path);
452             // create the importer struct
453             Include include(importer, path_key);
454             // attach information to AST node
455             imp->incs().push_back(include);
456             // register the resource buffers
457             register_resource(include, { source, srcmap }, pstate);
458           }
459           // only a path was retuned
460           // try to load it like normal
461           else if(abs_path) {
462             // checks some urls to preserve
463             // `http://`, `https://` and `//`
464             // or dispatchs to `import_file`
465             // which will check for a `.css` extension
466             // or resolves the file on the filesystem
467             // added and resolved via `add_file`
468             // finally stores everything on `imp`
469             import_url(imp, abs_path, ctx_path);
470           }
471           // move to next
472           ++it_includes;
473         }
474         // deallocate the returned memory
475         sass_delete_import_list(includes);
476         // set success flag
477         has_import = true;
478         // break out of loop
479         if (only_one) break;
480       }
481     }
482     // return result
483     return has_import;
484   }
485 
486   void register_function(Context&, Signature sig, Native_Function f, Env* env);
487   void register_function(Context&, Signature sig, Native_Function f, size_t arity, Env* env);
488   void register_overload_stub(Context&, sass::string name, Env* env);
489   void register_built_in_functions(Context&, Env* env);
490   void register_c_functions(Context&, Env* env, Sass_Function_List);
491   void register_c_function(Context&, Env* env, Sass_Function_Entry);
492 
493   char* Context::render(Block_Obj root)
494   {
495     // check for valid block
496     if (!root) return 0;
497     // start the render process
498     root->perform(&emitter);
499     // finish emitter stream
500     emitter.finalize();
501     // get the resulting buffer from stream
502     OutputBuffer emitted = emitter.get_buffer();
503     // should we append a source map url?
504     if (!c_options.omit_source_map_url) {
505       // generate an embedded source map
506       if (c_options.source_map_embed) {
507         emitted.buffer += linefeed;
508         emitted.buffer += format_embedded_source_map();
509       }
510       // or just link the generated one
511       else if (source_map_file != "") {
512         emitted.buffer += linefeed;
513         emitted.buffer += format_source_mapping_url(source_map_file);
514       }
515     }
516     // create a copy of the resulting buffer string
517     // this must be freed or taken over by implementor
518     return sass_copy_c_string(emitted.buffer.c_str());
519   }
520 
521   void Context::apply_custom_headers(Block_Obj root, const char* ctx_path, SourceSpan pstate)
522   {
523     // create a custom import to resolve headers
524     Import_Obj imp = SASS_MEMORY_NEW(Import, pstate);
525     // dispatch headers which will add custom functions
526     // custom headers are added to the import instance
527     call_headers(entry_path, ctx_path, pstate, imp);
528     // increase head count to skip later
529     head_imports += resources.size() - 1;
530     // add the statement if we have urls
531     if (!imp->urls().empty()) root->append(imp);
532     // process all other resources (add Import_Stub nodes)
533     for (size_t i = 0, S = imp->incs().size(); i < S; ++i) {
534       root->append(SASS_MEMORY_NEW(Import_Stub, pstate, imp->incs()[i]));
535     }
536   }
537 
538   Block_Obj File_Context::parse()
539   {
540 
541     // check if entry file is given
542     if (input_path.empty()) return {};
543 
544     // create absolute path from input filename
545     // ToDo: this should be resolved via custom importers
546     sass::string abs_path(rel2abs(input_path, CWD));
547 
548     // try to load the entry file
549     char* contents = read_file(abs_path);
550 
551     // alternatively also look inside each include path folder
552     // I think this differs from ruby sass (IMO too late to remove)
553     for (size_t i = 0, S = include_paths.size(); contents == 0 && i < S; ++i) {
554       // build absolute path for this include path entry
555       abs_path = rel2abs(input_path, include_paths[i]);
556       // try to load the resulting path
557       contents = read_file(abs_path);
558     }
559 
560     // abort early if no content could be loaded (various reasons)
561     if (!contents) throw std::runtime_error(
562       "File to read not found or unreadable: "
563       + std::string(input_path.c_str()));
564 
565     // store entry path
566     entry_path = abs_path;
567 
568     // create entry only for import stack
569     Sass_Import_Entry import = sass_make_import(
570       input_path.c_str(),
571       entry_path.c_str(),
572       contents,
573       0
574     );
575     // add the entry to the stack
576     import_stack.push_back(import);
577 
578     // create the source entry for file entry
579     register_resource({{ input_path, "." }, abs_path }, { contents, 0 });
580 
581     // create root ast tree node
582     return compile();
583 
584   }
585 
586   Block_Obj Data_Context::parse()
587   {
588 
589     // check if source string is given
590     if (!source_c_str) return {};
591 
592     // convert indented sass syntax
593     if(c_options.is_indented_syntax_src) {
594       // call sass2scss to convert the string
595       char * converted = sass2scss(source_c_str,
596         // preserve the structure as much as possible
597         SASS2SCSS_PRETTIFY_1 | SASS2SCSS_KEEP_COMMENT);
598       // replace old source_c_str with converted
599       free(source_c_str); source_c_str = converted;
600     }
601 
602     // remember entry path (defaults to stdin for string)
603     entry_path = input_path.empty() ? "stdin" : input_path;
604 
605     // ToDo: this may be resolved via custom importers
606     sass::string abs_path(rel2abs(entry_path));
607     char* abs_path_c_str = sass_copy_c_string(abs_path.c_str());
608     strings.push_back(abs_path_c_str);
609 
610     // create entry only for the import stack
611     Sass_Import_Entry import = sass_make_import(
612       entry_path.c_str(),
613       abs_path_c_str,
614       source_c_str,
615       srcmap_c_str
616     );
617     // add the entry to the stack
618     import_stack.push_back(import);
619 
620     // register a synthetic resource (path does not really exist, skip in includes)
621     register_resource({{ input_path, "." }, input_path }, { source_c_str, srcmap_c_str });
622 
623     // create root ast tree node
624     return compile();
625   }
626 
627   // parse root block from includes
628   Block_Obj Context::compile()
629   {
630     // abort if there is no data
631     if (resources.size() == 0) return {};
632     // get root block from the first style sheet
633     Block_Obj root = sheets.at(entry_path).root;
634     // abort on invalid root
635     if (root.isNull()) return {};
636     Env global; // create root environment
637     // register built-in functions on env
638     register_built_in_functions(*this, &global);
639     // register custom functions (defined via C-API)
640     for (size_t i = 0, S = c_functions.size(); i < S; ++i)
641     { register_c_function(*this, &global, c_functions[i]); }
642     // create initial backtrace entry
643     // create crtp visitor objects
644     Expand expand(*this, &global);
645     Cssize cssize(*this);
646     CheckNesting check_nesting;
647     // check nesting in all files
648     for (auto sheet : sheets) {
649       auto styles = sheet.second;
650       check_nesting(styles.root);
651     }
652     // expand and eval the tree
653     root = expand(root);
654 
655     Extension unsatisfied;
656     // check that all extends were used
657     if (extender.checkForUnsatisfiedExtends(unsatisfied)) {
658       throw Exception::UnsatisfiedExtend(traces, unsatisfied);
659     }
660 
661     // check nesting
662     check_nesting(root);
663     // merge and bubble certain rules
664     root = cssize(root);
665 
666     // clean up by removing empty placeholders
667     // ToDo: maybe we can do this somewhere else?
668     Remove_Placeholders remove_placeholders;
669     root->perform(&remove_placeholders);
670 
671     // return processed tree
672     return root;
673   }
674   // EO compile
675 
676   sass::string Context::format_embedded_source_map()
677   {
678     sass::string map = emitter.render_srcmap(*this);
679     sass::istream is( map.c_str() );
680     sass::ostream buffer;
681     base64::encoder E;
682     E.encode(is, buffer);
683     sass::string url = "data:application/json;base64," + buffer.str();
684     url.erase(url.size() - 1);
685     return "/*# sourceMappingURL=" + url + " */";
686   }
687 
688   sass::string Context::format_source_mapping_url(const sass::string& file)
689   {
690     sass::string url = abs2rel(file, output_path, CWD);
691     return "/*# sourceMappingURL=" + url + " */";
692   }
693 
694   char* Context::render_srcmap()
695   {
696     if (source_map_file == "") return 0;
697     sass::string map = emitter.render_srcmap(*this);
698     return sass_copy_c_string(map.c_str());
699   }
700 
701 
702   // for data context we want to start after "stdin"
703   // we probably always want to skip the header includes?
704   sass::vector<sass::string> Context::get_included_files(bool skip, size_t headers)
705   {
706       // create a copy of the vector for manipulations
707       sass::vector<sass::string> includes = included_files;
708       if (includes.size() == 0) return includes;
709       if (skip) { includes.erase( includes.begin(), includes.begin() + 1 + headers); }
710       else { includes.erase( includes.begin() + 1, includes.begin() + 1 + headers); }
711       includes.erase( std::unique( includes.begin(), includes.end() ), includes.end() );
712       std::sort( includes.begin() + (skip ? 0 : 1), includes.end() );
713       return includes;
714   }
715 
716   void register_function(Context& ctx, Signature sig, Native_Function f, Env* env)
717   {
718     Definition* def = make_native_function(sig, f, ctx);
719     def->environment(env);
720     (*env)[def->name() + "[f]"] = def;
721   }
722 
723   void register_function(Context& ctx, Signature sig, Native_Function f, size_t arity, Env* env)
724   {
725     Definition* def = make_native_function(sig, f, ctx);
726     sass::ostream ss;
727     ss << def->name() << "[f]" << arity;
728     def->environment(env);
729     (*env)[ss.str()] = def;
730   }
731 
732   void register_overload_stub(Context& ctx, sass::string name, Env* env)
733   {
734     Definition* stub = SASS_MEMORY_NEW(Definition,
735                                        SourceSpan{ "[built-in function]" },
736                                        nullptr,
737                                        name,
738                                        Parameters_Obj{},
739                                        nullptr,
740                                        true);
741     (*env)[name + "[f]"] = stub;
742   }
743 
744 
745   void register_built_in_functions(Context& ctx, Env* env)
746   {
747     using namespace Functions;
748     // RGB Functions
749     register_function(ctx, rgb_sig, rgb, env);
750     register_overload_stub(ctx, "rgba", env);
751     register_function(ctx, rgba_4_sig, rgba_4, 4, env);
752     register_function(ctx, rgba_2_sig, rgba_2, 2, env);
753     register_function(ctx, red_sig, red, env);
754     register_function(ctx, green_sig, green, env);
755     register_function(ctx, blue_sig, blue, env);
756     register_function(ctx, mix_sig, mix, env);
757     // HSL Functions
758     register_function(ctx, hsl_sig, hsl, env);
759     register_function(ctx, hsla_sig, hsla, env);
760     register_function(ctx, hue_sig, hue, env);
761     register_function(ctx, saturation_sig, saturation, env);
762     register_function(ctx, lightness_sig, lightness, env);
763     register_function(ctx, adjust_hue_sig, adjust_hue, env);
764     register_function(ctx, lighten_sig, lighten, env);
765     register_function(ctx, darken_sig, darken, env);
766     register_function(ctx, saturate_sig, saturate, env);
767     register_function(ctx, desaturate_sig, desaturate, env);
768     register_function(ctx, grayscale_sig, grayscale, env);
769     register_function(ctx, complement_sig, complement, env);
770     register_function(ctx, invert_sig, invert, env);
771     // Opacity Functions
772     register_function(ctx, alpha_sig, alpha, env);
773     register_function(ctx, opacity_sig, alpha, env);
774     register_function(ctx, opacify_sig, opacify, env);
775     register_function(ctx, fade_in_sig, opacify, env);
776     register_function(ctx, transparentize_sig, transparentize, env);
777     register_function(ctx, fade_out_sig, transparentize, env);
778     // Other Color Functions
779     register_function(ctx, adjust_color_sig, adjust_color, env);
780     register_function(ctx, scale_color_sig, scale_color, env);
781     register_function(ctx, change_color_sig, change_color, env);
782     register_function(ctx, ie_hex_str_sig, ie_hex_str, env);
783     // String Functions
784     register_function(ctx, unquote_sig, sass_unquote, env);
785     register_function(ctx, quote_sig, sass_quote, env);
786     register_function(ctx, str_length_sig, str_length, env);
787     register_function(ctx, str_insert_sig, str_insert, env);
788     register_function(ctx, str_index_sig, str_index, env);
789     register_function(ctx, str_slice_sig, str_slice, env);
790     register_function(ctx, to_upper_case_sig, to_upper_case, env);
791     register_function(ctx, to_lower_case_sig, to_lower_case, env);
792     // Number Functions
793     register_function(ctx, percentage_sig, percentage, env);
794     register_function(ctx, round_sig, round, env);
795     register_function(ctx, ceil_sig, ceil, env);
796     register_function(ctx, floor_sig, floor, env);
797     register_function(ctx, abs_sig, abs, env);
798     register_function(ctx, min_sig, min, env);
799     register_function(ctx, max_sig, max, env);
800     register_function(ctx, random_sig, random, env);
801     // List Functions
802     register_function(ctx, length_sig, length, env);
803     register_function(ctx, nth_sig, nth, env);
804     register_function(ctx, set_nth_sig, set_nth, env);
805     register_function(ctx, index_sig, index, env);
806     register_function(ctx, join_sig, join, env);
807     register_function(ctx, append_sig, append, env);
808     register_function(ctx, zip_sig, zip, env);
809     register_function(ctx, list_separator_sig, list_separator, env);
810     register_function(ctx, is_bracketed_sig, is_bracketed, env);
811     // Map Functions
812     register_function(ctx, map_get_sig, map_get, env);
813     register_function(ctx, map_merge_sig, map_merge, env);
814     register_function(ctx, map_remove_sig, map_remove, env);
815     register_function(ctx, map_keys_sig, map_keys, env);
816     register_function(ctx, map_values_sig, map_values, env);
817     register_function(ctx, map_has_key_sig, map_has_key, env);
818     register_function(ctx, keywords_sig, keywords, env);
819     // Introspection Functions
820     register_function(ctx, type_of_sig, type_of, env);
821     register_function(ctx, unit_sig, unit, env);
822     register_function(ctx, unitless_sig, unitless, env);
823     register_function(ctx, comparable_sig, comparable, env);
824     register_function(ctx, variable_exists_sig, variable_exists, env);
825     register_function(ctx, global_variable_exists_sig, global_variable_exists, env);
826     register_function(ctx, function_exists_sig, function_exists, env);
827     register_function(ctx, mixin_exists_sig, mixin_exists, env);
828     register_function(ctx, feature_exists_sig, feature_exists, env);
829     register_function(ctx, call_sig, call, env);
830     register_function(ctx, content_exists_sig, content_exists, env);
831     register_function(ctx, get_function_sig, get_function, env);
832     // Boolean Functions
833     register_function(ctx, not_sig, sass_not, env);
834     register_function(ctx, if_sig, sass_if, env);
835     // Misc Functions
836     register_function(ctx, inspect_sig, inspect, env);
837     register_function(ctx, unique_id_sig, unique_id, env);
838     // Selector functions
839     register_function(ctx, selector_nest_sig, selector_nest, env);
840     register_function(ctx, selector_append_sig, selector_append, env);
841     register_function(ctx, selector_extend_sig, selector_extend, env);
842     register_function(ctx, selector_replace_sig, selector_replace, env);
843     register_function(ctx, selector_unify_sig, selector_unify, env);
844     register_function(ctx, is_superselector_sig, is_superselector, env);
845     register_function(ctx, simple_selectors_sig, simple_selectors, env);
846     register_function(ctx, selector_parse_sig, selector_parse, env);
847   }
848 
849   void register_c_functions(Context& ctx, Env* env, Sass_Function_List descrs)
850   {
851     while (descrs && *descrs) {
852       register_c_function(ctx, env, *descrs);
853       ++descrs;
854     }
855   }
856   void register_c_function(Context& ctx, Env* env, Sass_Function_Entry descr)
857   {
858     Definition* def = make_c_function(descr, ctx);
859     def->environment(env);
860     (*env)[def->name() + "[f]"] = def;
861   }
862 
863 }
864