/* G-Cows - scripting language for creation of web sites Copyright (C) 2002, 2003, 2004, 2005, 2006 Andrea Sozzi This file is part of G-Cows. G-Cows is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. G-Cows is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include "language.H" #include "file_utils.H" Exec_tree::Exec_tree (string input, string output, string root_dir, string noext_in) { input_file = input; output_file = output; noext = noext_in; string launching_dir = string (get_w_dir ()) + "/"; // absolute abs_input_file = launching_dir + input; abs_output_file = launching_dir + output; if (root_dir == "./") root_dir_level = get_dir_level (); else { root_dir = add_slash (root_dir); root_dir_level = get_dir_level (root_dir); } if (output != "-") starting_dir_level = get_dir_level (output); vars.set_var ("_argc", 1); Gen_type tmp_argv ('a'); tmp_argv.push (Gen_type (input)); vars.set_var ("_argv", tmp_argv); } Node * Exec_tree::push_node (Node *node) { node->p2tree = this; node->log_infos = Msg::inst()->get_parse_infos (); nodes.push_back (node); //cout << "DEBUG: Nodes in tree: " << nodes.size () << endl; return node; } Node * Deps_tree::push_node (Node *node) { node->p2tree = this; node->log_infos = Msg::inst()->get_parse_infos (); node->deps (); nodes.push_back (node); // cout << "Nodes in tree: " << nodes.size () << endl; return node; } void Exec_tree::start () { if ( nodes.empty () ) return; Node_Iter last_pushed = nodes.end () - 1; Msg::inst()->set_log_mode (Msg::exec_log_mode); (*last_pushed)->exec (); Msg::inst()->set_log_mode (Msg::parsing_log_mode); delete *last_pushed; nodes.erase (nodes.begin (), nodes.end ()); } void Exec_tree::save_args () { restore_flags_stack.push_back (true); Gen_type var_argc = vars.get_var ("_argc"); if (! var_argc.is_int ()) Msg::inst()->error ("_argc must be an integer"); argc_stack.push_back (var_argc.get_int ()); Gen_type var_argv = vars.get_var ("_argv"); /* if (! var_argv.is_array ()) Msg::inst()->error ("_argv must be an array"); */ argv_stack.push_back (var_argv); } void Exec_tree::restore_args () { Bool_iter iter_restore_flags = restore_flags_stack.end (); bool flag = *(--iter_restore_flags); if (flag) { Int_iter iterc = argc_stack.end (); int argc = *(--iterc); vars.set_var ("_argc", argc); argc_stack.erase (iterc); Gen_type_iter iterv = argv_stack.end (); Gen_type args = *(--iterv); vars.set_var ("_argv", args); argv_stack.erase (iterv); } restore_flags_stack.erase (iter_restore_flags); } void Deps_tree::start () { if (nodes.empty ()) return; Node_Iter last_pushed = nodes.end () - 1; delete *last_pushed; nodes.erase (nodes.begin (), nodes.end ()); } string Exec_tree::format_date (time_t time, string fmt_string) { struct tm *loc_time = localtime (&time); int start = -1; std::ostringstream tmp; while ((start = fmt_string.find ("#", start + 1)) != (int)string::npos) { switch (fmt_string.at (start+1)) { case 'y': tmp << 1900 + loc_time->tm_year; fmt_string.replace (start, 2, tmp.str()); tmp.str(""); break; case 'm': tmp << loc_time->tm_mon + 1; fmt_string.replace (start, 2, tmp.str()); tmp.str(""); break; case 'd': tmp << loc_time->tm_mday; fmt_string.replace (start, 2, tmp.str()); tmp.str(""); break; case 'h': tmp << loc_time->tm_hour; fmt_string.replace (start, 2, tmp.str()); tmp.str(""); break; case 'n': tmp << loc_time->tm_min; if (loc_time->tm_min < 10) { fmt_string.replace (start, 2, string("0").append (tmp.str())); } else fmt_string.replace (start, 2, tmp.str()); tmp.str(""); break; case 's': tmp << loc_time->tm_sec; if (loc_time->tm_sec < 10) { fmt_string.replace (start, 2, string("0").append (tmp.str())); } else fmt_string.replace (start, 2, tmp.str()); tmp.str(""); break; case 'M': fmt_string.replace (start, 2, (vars.get_array_element ( "_months", loc_time->tm_mon)).get_string()); break; case 'D': fmt_string.replace (start, 2, (vars.get_array_element ( "_days", loc_time->tm_wday)).get_string()); break; } } return fmt_string; } // Adjust file names adding a variable number of `../' string Tree::adjust_level () { string res = ""; int diff_levels = starting_dir_level - root_dir_level; if (diff_levels < 0) Msg::inst()->warning ( "Can't adjust level: output file outside root directory", "No adjust"); else for (int i = 0; i < diff_levels; i++) res += "../"; return res; } void Deps_tree::set_up (string file_scanned) { current_dir = get_dir_from_file_name (file_scanned); first_file = true; } string Deps_tree::get_output_file () { if (custom_extension != "") return output_file + custom_extension; else return output_file + target_extension; } string Deps_tree::get_and_delete_custom_ext () { string ext = custom_extension; custom_extension = ""; return ext; } void Deps_tree::refresh (string file_name) { unsigned int last_slash; current_dir_stack.push_back (current_dir); if ( (last_slash = file_name.rfind("/")) != string::npos ) current_dir += ( file_name.substr (0, last_slash) + "/" ); } void Deps_tree::undo_refresh () { if (current_dir_stack.empty ()) Msg::inst()->fatal ("Deps_tree::undo_refresh () - internal error"); String_iter iter = current_dir_stack.end (); current_dir = *(--iter); current_dir_stack.erase (iter); } void Deps_tree::write_file_list (string text) { if ( ! always_update) file_list += text; else always_list += text; } string Deps_tree::get_sources_list () { string ret; for (String_set_iter i=src_cache.begin (); i!= src_cache.end (); i++) ret += *i; src_cache.erase (src_cache.begin (), src_cache.end ()); return ret; } void Deps_tree::dump_cache () { for (String_set_iter i=dep_cache.begin (); i!= dep_cache.end (); i++) output << *i; } void Deps_tree::clear_cache () { dep_cache.erase (dep_cache.begin (), dep_cache.end ()); } void N_dep::deps () { if (( strstr (typeid (*file).name (), "const") ) || ( strstr (typeid (*file).name (), "input") ) || ( strstr (typeid (*file).name (), "output") ) ) { p2tree->ensure_dep (); set_msg_buffers (); Gen_type gfile = file->exec (); if (!gfile.is_string ()) Msg::inst()->fatal ("N_dep::deps () -- Internal Error"); string sfile = gfile.get_string (); string dep; if (*(sfile.c_str()) != '/') dep = ((Deps_tree *)p2tree)->get_current_dir () + sfile; else dep = sfile; ((Deps_tree *)p2tree)->write_cache (" \\\n\t" + opt_file_name (dep)); } else { if (! ((Deps_tree *)p2tree)->is_always ()) Msg::inst()->warning ("unable to get dependency"); } } // Constants and Variables Gen_type N_const::exec () { set_msg_buffers (); if (cst->is_int ()) return *cst; else if (cst->is_string ()) { string escaped = cst->get_string (); int start = -1; while ((start=escaped.find ("\\", start + 1)) != (int)string::npos) { if (escaped[start+1] == '\\') escaped.replace (start, 2, "\\"); else if (escaped[start+1] == 'n') escaped.replace (start, 2, "\n"); else if (escaped[start+1] == 't') escaped.replace (start, 2, "\t"); else if (escaped[start+1] == '"') escaped.replace (start, 2, "\""); else if (escaped[start+1] == '\'') escaped.replace (start, 2, "\'"); else if (escaped[start+1] == '`') escaped.replace (start, 2, "`"); else if (escaped[start+1] == '\n') escaped.erase (start, 2); } return Gen_type (escaped); } else { Msg::inst()->fatal ("N_const::exec () -- Internal Error"); } return Gen_type (-1); } string N_base_var::get_var_name () { if ( type == 'I') return ((N_base_var *)var)->get_var_name (); return var_name; } Gen_type N_base_var::get_base_index () { return (((N_base_var *)var)->index)->exec (); } Gen_type N_var::exec () { set_msg_buffers (); if ( ! (item_syntax () || var_item_syntax ()) ) return p2tree->vars.get_var (var_name); Gen_type num_index = index->exec (); if (! num_index.is_int ()) { Msg::inst()->error ("array index must be an integer"); return Gen_type (""); } if (item_syntax ()) { Gen_type new_var = p2tree->vars.get_var (var_name); if (! new_var.is_array ()) { // FIXME: this check should be done within get_element () Msg::inst()->error (var_name + " is not an array"); return Gen_type (""); } return new_var.get_element (num_index.get_int ()); } if (var_item_syntax ()) { Gen_type new_var = var->exec (); if (! new_var.is_array ()) { // FIXME: this check should be done within get_element () /* Msg::inst()->error (((N_base_var *)var)->get_var_name () + " is not an array"); */ Msg::inst()->error ( "trying to extract element from non array variable"); return Gen_type (""); } return new_var.get_element (num_index); } Msg::inst()->fatal ("N_var::exec () -- internal error"); return Gen_type (-1); } Gen_type N_ind_expr::exec () { set_msg_buffers (); string var_name = get_var_name (); if ( ! (item_syntax () || var_item_syntax ()) ) return p2tree->vars.get_var (var_name); Gen_type num_index = index->exec (); if (! num_index.is_int ()) { Msg::inst()->error ("array index must be an integer"); return Gen_type (""); } if ( item_syntax ()) { Gen_type new_var = p2tree->vars.get_var (var_name); if (! new_var.is_array ()) { // FIXME: this check should be done within get_element () Msg::inst()->error (var_name + " is not an array"); return Gen_type (""); } return new_var.get_element (num_index.get_int ()); } Msg::inst()->fatal ("N_ind_expr::exec () -- Internal Error"); return Gen_type (-1); } string N_ind_expr::get_var_name () { Gen_type var = var_exp->exec (); if (!var.is_string ()) { Msg::inst()->error ("$ (expr): expr must be a string variable"); return ""; } return var.get_string (); } Gen_type N_undef::exec () { set_msg_buffers (); N_base_var *bvar = ((N_base_var *)var); string name = bvar->get_var_name (); if (bvar->item_syntax ()) Msg::inst()->error ("can't undef an array element"); else if ( ! p2tree->vars.undef (name)) Msg::inst()->warning ("variable `" + name + "' not defined"); return Gen_type (0); } Gen_type N_tobool::exec () { set_msg_buffers (); return Gen_type ((var->exec()).to_bool ()); } Gen_type N_toint::exec () { set_msg_buffers (); Gen_type variable = var->exec(); if (variable.is_array ()) Msg::inst()->error ("can't convert array to integer"); return Gen_type (variable.to_int ()); } Gen_type N_tostring::exec () { set_msg_buffers (); return Gen_type ((var->exec()).to_string ()); } Gen_type N_toarray::exec () { set_msg_buffers (); Gen_type in_var = var->exec (); if (in_var.is_array ()) return in_var; else if (in_var.is_int () || in_var.is_string ()) { Gen_type ret_value ('a'); ret_value.push (in_var); return ret_value; } else if (in_var.is_undefined ()) return Gen_type (0); else Msg::inst()->fatal ("N_toarray::exec () -- Internal Error"); return Gen_type (-1); } // Operators Gen_type N_assign::exec () { set_msg_buffers (); N_base_var *bvar = ((N_base_var *)var); string name = bvar->get_var_name (); if ( (only_if_undef) && (p2tree->vars.is_def (name)) ) { return value->exec (); } Gen_type val = value->exec (); if (bvar->array_syntax () && (! val.is_array ())) { Msg::inst()->error ("can't assign scalar value to array variable"); return Gen_type (""); } if (bvar->item_syntax ()) // array [4] = ... return p2tree->vars.set_array_element (name, ((N_var *)var)->get_index (), val); else if (bvar->var_item_syntax ()) { // array [4][1] = ... Gen_type new_var = (p2tree->vars.get_var (bvar->get_var_name())).get_element ( (((N_var *)var)->get_base_index()).get_int ()); new_var.set_element ((((N_var *)var)->get_index()).get_int (), val); return p2tree->vars.set_array_element (bvar->get_var_name(), (((N_var *)var)->get_base_index()).get_int (), new_var); } else // var = ... | var [] = ... return p2tree->vars.set_var (name, val); } Gen_type N_assign_rec::exec () { set_msg_buffers (); N_base_var *bvar = ((N_base_var *)var); string name = bvar->get_var_name (); if ( (only_if_undef) && (p2tree->vars.is_def (name)) ) { return value->exec (); } Gen_type val = value->exec (); if (bvar->array_syntax () && (! val.is_array ())) { Msg::inst()->error ("can't assign scalar value to array variable"); return Gen_type (""); } if (bvar->item_syntax ()) // array [4] = ... return p2tree->vars.set_array_element (name, ((N_var *)var)->get_index (), val); else if (bvar->var_item_syntax ()) { // array [4][1] = ... Gen_type new_var = (p2tree->vars.get_var (bvar->get_var_name())).get_element ( (((N_var *)var)->get_base_index()).get_int ()); new_var.set_element ((((N_var *)var)->get_index()).get_int (), val); return p2tree->vars.set_array_element (bvar->get_var_name(), (((N_var *)var)->get_base_index()).get_int (), new_var); } else // var = ... | var [] = ... return p2tree->vars.set_var (name, val); } Gen_type N_empty_array::exec () { set_msg_buffers (); N_base_var *bvar = ((N_base_var *)var); string name = bvar->get_var_name (); return p2tree->vars.set_var (name, Gen_type ('a')); } Gen_type N_and::exec () { set_msg_buffers (); Gen_type st1_result = st1->exec (); if (! (bool) st1_result) return st1_result; else return st2->exec (); } Gen_type N_or::exec () { set_msg_buffers (); Gen_type st1_result = st1->exec (); if ((bool) st1_result) return st1_result; else return st2->exec (); } Gen_type N_plus::exec () { /* Important: we can't simply do: return n1->exec () + n2->exec (); exec () functions often have side effects (++ operator for example) and compiler is not obliged to keep the evaluation order. This could result in something very bad as: i=0; print ("A" + i++ + "B" + i++ + "C" + i++); returning A2B1C0 */ set_msg_buffers (); Gen_type res1 = n1->exec (); Gen_type res2 = n2->exec (); return res1 + res2; } Gen_type N_minus::exec () { set_msg_buffers (); Gen_type res1 = n1->exec (); Gen_type res2 = n2->exec (); return res1 - res2; } Gen_type N_times::exec () { set_msg_buffers (); Gen_type res1 = n1->exec (); Gen_type res2 = n2->exec (); return res1 * res2; } Gen_type N_div::exec () { set_msg_buffers (); Gen_type res1 = n1->exec (); Gen_type res2 = n2->exec (); return res1 / res2; } Gen_type N_mod::exec () { set_msg_buffers (); Gen_type res1 = n1->exec (); Gen_type res2 = n2->exec (); return res1 % res2; } Gen_type N_pow ::exec () { set_msg_buffers (); Gen_type res1 = n1->exec (); Gen_type res2 = n2->exec (); return res1 ^ res2; } Gen_type N_epp::exec () { set_msg_buffers (); string op_name = (increment > 0) ? "++" : "--"; if (! var->is_var()) { Msg::inst()->error (op_name + " can only apply to variables"); return Gen_type (-1); } N_base_var *bvar = ((N_base_var *)var); string name = bvar->get_var_name (); if (bvar->item_syntax ()) { Gen_type value = p2tree->vars.get_array_element (name, ((N_var *)var)->get_index ()); if (! value.is_int ()) { Msg::inst()->error (op_name + " can only apply to integers"); return Gen_type (-1); } p2tree->vars.set_array_element (name, ((N_var *)var)->get_index (), value.get_int () + increment); return postfix ? value : Gen_type (value.get_int () + increment); } else { Gen_type value = p2tree->vars.get_var (name); if (! value.is_int ()) { Msg::inst()->error (op_name + " can only apply to integers"); return Gen_type (-1); } p2tree->vars.set_var (name, value.get_int () + increment); return postfix ? value : Gen_type (value.get_int () + increment); } } Gen_type N_eq::exec () { set_msg_buffers (); Gen_type var1 = st1->exec (); Gen_type var2 = st2->exec (); if (var1.get_type () != var2.get_type ()) { Msg::inst()->opt ("==: Operands have different type; comparison FALSE", "==: different types; FALSE"); return Gen_type (0); } return var1 == var2; } Gen_type N_neq::exec () { set_msg_buffers (); Gen_type var1 = st1->exec (); Gen_type var2 = st2->exec (); if (var1.get_type () != var2.get_type ()) { Msg::inst()->opt ("!=: Operands have different type; comparison TRUE", "!=: different types; TRUE"); return Gen_type (1); } return var1 != var2; } // Display Gen_type N_echo::exec () { set_msg_buffers (); Gen_type res = message->exec (); p2tree->write_output (res.to_string ()); return res; } Gen_type N_print::exec () { set_msg_buffers (); Gen_type res = message->exec (); p2tree->write_output (res.to_string () + "\n"); return res; } // Conditionals Gen_type N_if::exec () { set_msg_buffers (); if (cond->exec().to_bool()) { st->exec (); return Gen_type (1); } return Gen_type (0); } Gen_type N_ifdef::exec () { set_msg_buffers (); switch ( ((N_base_var *)var)->get_type () ) { case 's': case 'a': if ( p2tree->vars.is_def (((N_var *)var)->get_var_name ())) { st->exec (); return Gen_type (1); } break; case 'i': Msg::inst()->error ("Ifdef (var): can't be an array element"); break; default: Msg::inst()->fatal ("N_ifdef::exec ()-- Internal Error"); } return Gen_type (0); } Gen_type N_ifndef::exec () { set_msg_buffers (); switch ( ((N_base_var *)var)->get_type () ) { case 's': case 'a': if ( ! p2tree->vars.is_def (((N_var *)var)->get_var_name ())) { st->exec (); return Gen_type (1); } break; case 'i': Msg::inst()->error ("Ifndef (var): can't be an array element"); break; default: Msg::inst()->fatal ("N_ifndef::exec () -- Internal Error"); } return Gen_type (0); } Gen_type N_cond_seq::exec () { set_msg_buffers (); Gen_type seq_res = seq->exec (); if (! seq_res.is_int ()) Msg::inst()->fatal ("N_cond_seq::exec () -- Internal Error"); if ( ! seq_res.get_int ()) return if_st->exec (); return Gen_type (1); } Gen_type N_if_else::exec () { set_msg_buffers (); Gen_type seq_res = seq->exec (); if (! seq_res.is_int ()) Msg::inst()->fatal ("N_if_else::exec () -- Internal Error"); if ( ! seq_res.get_int ()) { st->exec (); return Gen_type (1); } return Gen_type (0); } // Functions N_input::N_input (Gen_type *prefix_in) { if (prefix_in->is_string ()) prefix = prefix_in->get_string (); else Msg::inst()->error ("input (prefix): prefix must be a string literal"); } N_output::N_output (Gen_type *prefix_in) { if (prefix_in->is_string ()) prefix = prefix_in->get_string (); else Msg::inst()->error ("output (prefix): prefix must be a string literal"); } Gen_type N_date::exec () { set_msg_buffers (); p2tree->ensure_exec (); Gen_type fmt = format->exec (); if (! fmt.is_string ()) { Msg::inst()->error ("date (format): format must be a string"); return Gen_type (""); } return ((Exec_tree *)p2tree)->format_date (time (NULL), fmt.get_string ()); } Gen_type N_fdate::exec () { set_msg_buffers (); p2tree->ensure_exec (); struct stat f_stat; Gen_type fl = file->exec (); if (! fl.is_string ()) { Msg::inst()->error ("fdate (file, format): file must be a string"); return Gen_type (""); } Gen_type fmt = format->exec (); if (! fmt.is_string ()) { Msg::inst()->error ("fdate (file, format): format must be a string"); return Gen_type (""); } // The current working dir could have been changed so we use the // one stored while building syntax tree //string file = dir + fl.get_string(); string file = fl.get_string(); if (file[0] != '/') file = dir + file; if (stat (file.c_str(), &f_stat)) { Msg::inst()->warning ("unable to get infos about " + file, file); return Gen_type (""); } return ((Exec_tree *)p2tree)->format_date (f_stat.st_mtime, fmt.get_string ()); } Gen_type N_fsize::exec () { set_msg_buffers (); p2tree->ensure_exec (); struct stat f_stat; Gen_type fl = file->exec (); if (! fl.is_string ()) { Msg::inst()->error ("fsize (file, format): file must be a string"); return Gen_type (""); } Gen_type fmt = format->exec (); if (! fmt.is_string ()) { Msg::inst()->error ("fsize (file, format): format must be a string"); return Gen_type (""); } string fmt_string = fmt.get_string (); // The current working dir could have been changed so we use the // one stored while building syntax tree //string file = dir + fl.get_string(); string file = fl.get_string(); if (file[0] != '/') file = dir + file; if (stat (file.c_str(), &f_stat)) { Msg::inst()->warning ("unable to get infos about " + file, file); return Gen_type (""); } else { int start = -1; std::ostringstream tmp; while ((start = fmt_string.find("#", start + 1)) != (int)string::npos) { switch (fmt_string.at (start+1)) { case 'b': tmp << f_stat.st_size; fmt_string.replace (start, 2, tmp.str()); tmp.str(""); break; case 'k': tmp << (double) f_stat.st_size / 1024; fmt_string.replace (start, 2, tmp.str()); tmp.str(""); break; case 'm': tmp << (double) f_stat.st_size / 1048576; fmt_string.replace (start, 2, tmp.str()); tmp.str(""); break; } } } return Gen_type (fmt_string); } Gen_type N_substr::exec () { set_msg_buffers (); string str = (str_n->exec ()).to_string (true); Gen_type index_from = from->exec(); if (! index_from.is_int ()) { Msg::inst()->error ("substring() second parameter must be an integer"); return Gen_type (""); } int from_int=index_from.get_int (); int len_int; if (from_int < 0) { Msg::inst()->error ("substring() second parameter can't be negative"); return Gen_type (""); } if (len == NULL) { len_int=str.length()-from_int; } else { Gen_type length = len->exec(); if (! length.is_int ()) { Msg::inst()->error ("substring() third parameter must be an integer"); return Gen_type (""); } len_int=length.get_int (); } if (len_int < 0) { Msg::inst()->error ("substring() third parameter can't be negative"); return Gen_type (""); } return Gen_type (str.substr(from_int, len_int)); } Gen_type N_find::exec () { set_msg_buffers (); string src_str = (src->exec ()).to_string (true); string chars_str = (chars->exec ()).to_string (true); int from_int; if (from == NULL) { if (rev) from_int=src_str.length(); else from_int=0; } else { Gen_type search_from=from->exec(); if (! search_from.is_int ()) { Msg::inst()->error ("find() third parameter must be an integer"); return Gen_type (-1); } from_int=search_from.get_int (); } int index; if (rev) index = src_str.rfind (chars_str, from_int); else index = src_str.find (chars_str, from_int); return (Gen_type (index)); } Gen_type N_find_match::exec () { set_msg_buffers (); string src_str = (src->exec ()).to_string (true); string chrs_str = (chrs->exec ()).to_string (true); int index; if (last_flg && not_flg) index = src_str.find_last_not_of (chrs_str); else if (last_flg && (! not_flg)) index = src_str.find_last_of (chrs_str); else if ((!last_flg) && not_flg) index = src_str.find_first_not_of (chrs_str); else if ((!last_flg) && (!not_flg)) index = src_str.find_first_not_of (chrs_str); else index = -1; return (Gen_type (index)); } Gen_type N_char::exec () { set_msg_buffers (); string src_str = (src->exec ()).to_string (true); Gen_type ind = index->exec(); if (! ind.is_int ()) { Msg::inst()->error ("char() second parameter must be an integer"); return Gen_type (""); } int ind_int=ind.get_int (); if (ind_int<0) { Msg::inst()->error ("char() negative index not allowed"); return Gen_type (""); } if ((unsigned int) ind_int>src_str.length()-1) { Msg::inst()->error ("char() index beyond string bounds"); return Gen_type (""); } return Gen_type (src_str.substr(ind_int,1)); } Gen_type N_startswith::exec () { set_msg_buffers (); string src_str = (src->exec ()).to_string (true); string start_str = (start->exec ()).to_string (true); return Gen_type (src_str.substr (0, start_str.length()) == start_str); } Gen_type N_endswith::exec () { set_msg_buffers (); string src_str = (src->exec ()).to_string (true); string end_str = (end->exec ()).to_string (true); return Gen_type (src_str.substr (src_str.length() - end_str.length(), src_str.length()) == end_str); } Gen_type N_insert::exec () { set_msg_buffers (); string src_str = (source->exec ()).to_string (true); string ins_str = (ins->exec ()).to_string (true); Gen_type index_gen = index->exec(); if (! index_gen.is_int ()) { Msg::inst()->error ("insert() second parameter must be an integer"); return Gen_type (""); } int index_int=index_gen.get_int (); if (index_int<0) { Msg::inst()->error ("char() negative index not allowed"); return Gen_type (""); } if ((unsigned int) index_int > src_str.length()-1) { Msg::inst()->error ("insert(): index beyond string bounds"); return Gen_type (""); } return Gen_type (src_str.insert (index_int, ins_str)); } Gen_type N_replace::exec () { set_msg_buffers (); string source_str = (source->exec ()).to_string (true); string from_str = (from->exec ()).to_string (true); if (from_str == "") return Gen_type (source_str); string to_str = (to->exec ()).to_string (true); int start = -1; if (source_str.find (from_str) == string::npos) { string funct_name; funct_name = (all) ? "replaceall ()" : "replace ()"; Msg::inst()->opt (funct_name + "; couldn't find " + from_str + " within " + source_str, funct_name + "; no replacement" ); return Gen_type (source_str); } if (all) while ((start=source_str.find(from_str, start + 1))!=(int)string::npos) source_str.replace (start, from_str.length (), to_str); else if ((start=source_str.find(from_str, start + 1)) != (int)string::npos ) source_str.replace (start, from_str.length (), to_str); return Gen_type (source_str); } Gen_type N_tok::exec () { set_msg_buffers (); string source_str = (source->exec ()).to_string (true); Gen_type separators = sep_list->exec (); Gen_type ret_val ('a'); if (separators.get_type () == 'c') { string sep_str = separators.to_string (true); int beg_tok = 0; int sep_pos = -1; while ((sep_pos=source_str.find (sep_str, sep_pos + 1)) !=(int)string::npos) { ret_val.push (source_str.substr (beg_tok, sep_pos - beg_tok)); beg_tok = (sep_pos + sep_str.length ()); } if (beg_tok) ret_val.push (source_str.substr (beg_tok, sep_pos - beg_tok)); else ret_val.push (source_str); } else { Msg::inst()->error ("tokenize; Separator must be a string"); } return ret_val; } Gen_type N_join::exec () { set_msg_buffers (); Gen_type src_arr = source->exec (); if (! src_arr.is_array ()) { Msg::inst()->error ("join requires an array as first variable"); return Gen_type (-1); } string str_sep = (sep->exec ()).to_string (true); string ret_value = ""; for (Gen_type_c_iter i = (src_arr.get_array ())->begin (); i != (src_arr.get_array ())->end (); i++) { if (i != (src_arr.get_array ())->begin ()) ret_value += str_sep; ret_value += i->to_string (); } return Gen_type (ret_value); } Gen_type N_trim::exec () { set_msg_buffers (); string source_str = (source->exec ()).to_string (true); return Gen_type (Utils::trim (source_str)); } Gen_type N_ltrim::exec () { set_msg_buffers (); string source_str = (source->exec ()).to_string (true); return Gen_type (Utils::ltrim (source_str)); } Gen_type N_rtrim::exec () { set_msg_buffers (); string source_str = (source->exec ()).to_string (true); return Gen_type (Utils::rtrim (source_str)); } Gen_type N_ch_case::exec () { set_msg_buffers (); string source_str = (source->exec ()).to_string (true); char* buf = new char[source_str.length()]; source_str.copy(buf, source_str.length()); if (to_lower) { for (unsigned int i = 0; i < source_str.length(); i++) buf[i] = tolower (buf[i]); } else { for (unsigned int i = 0; i < source_str.length(); i++) buf[i] = toupper (buf[i]); } Gen_type ret = Gen_type (std::string (buf, source_str.length())); delete buf; return ret; } Gen_type N_length::exec () { set_msg_buffers (); Gen_type expr_eval = expr->exec (); switch (expr_eval.get_type ()) { case 'a': return Gen_type ((int) (expr_eval.get_array ())->size ()); break; case 'c': return Gen_type ((int) (expr_eval.get_string ()).size ()); break; case 'i': return Gen_type ((int) (expr_eval.to_string ()).size ()); break; case 'u': return Gen_type (0); break; default: Msg::inst()->fatal ("N_length::exec () -- Internal Error"); } return Gen_type (0); } Gen_type N_raiseerr::exec () { set_msg_buffers (); Gen_type expr1_eval = expr1->exec (); if (! expr1_eval.is_string ()) Msg::inst()->error ("raiseerror called with non string expression"); if (expr2 != NULL) { Gen_type expr2_eval = expr2->exec (); if (! expr2_eval.is_string ()) Msg::inst()->error ("raiseerror called with non string expression"); Msg::inst()->error (expr1_eval.get_string (), expr2_eval.get_string ()); } else { Msg::inst()->error (expr1_eval.get_string (), expr1_eval.get_string ()); } return Gen_type (0); } Gen_type N_raisewarn::exec () { set_msg_buffers (); Gen_type expr1_eval = expr1->exec (); if (! expr1_eval.is_string ()) Msg::inst()->error ("raisewarning called with non string expression"); /* if (! expr2) { Msg::inst()->warning (expr1_eval.get_string ()); return Gen_type (0); } */ if (expr2 != NULL) { Gen_type expr2_eval = expr2->exec (); if (! expr2_eval.is_string ()) Msg::inst()->error ("raisewarning called with non string expression"); Msg::inst()->warning (expr1_eval.get_string (), expr2_eval.get_string ()); } else { Msg::inst()->warning (expr1_eval.get_string (), expr1_eval.get_string ()); } return Gen_type (0); } Gen_type N_term::exec () { set_msg_buffers (); Gen_type res = msg->exec (); std::cout << res.to_string () << std::endl; return res; } // Inclusions Gen_type N_verbatim::exec () { set_msg_buffers (); string sfile = file->exec ().get_string (); if (sfile [0] != '/') sfile = dir + sfile; std::ifstream in (sfile.c_str ()); if ( !in ) Msg::inst()->error ("can't open file " + sfile, "open " + sfile); else { std::ostringstream tmp; tmp << in.rdbuf(); p2tree->write_output (tmp.str()); } return Gen_type (0); } Gen_type N_include::exec () { set_msg_buffers (); ((Exec_tree *)p2tree)->set_include_flag (); return Gen_type (0); } Gen_type N_arg_include::exec () { set_msg_buffers (); ((Exec_tree *)p2tree)->save_args (); Gen_type args = n_args->exec (); if (args.is_list ()) { p2tree->vars.set_var ("_argc", Gen_type ((int) (args.get_list())->size()+1)); p2tree->vars.set_var ("_argv", file->exec () + args); } else { p2tree->vars.set_var ("_argc", 2); Gen_type tmp_argv ('a'); tmp_argv.push (file->exec ()); tmp_argv.push (args); p2tree->vars.set_var ("_argv", tmp_argv); } return Gen_type (0); } // External Commands and Scripts Gen_type N_cmd::exec () { set_msg_buffers (); if ( ((Exec_tree *)p2tree)->get_noext () == "warning") Msg::inst()->warning ("can't execute command: --noext-warning used"); else if ( ((Exec_tree *)p2tree)->get_noext () == "error") Msg::inst()->error ("can't execute command: --noext-error used"); string sfile = (cmd->exec ().get_string ()); string eval_output; FILE *output = popen (sfile.c_str (), "r"); if (!output) { Msg::inst()->error ("error while executing " + sfile, "exec " + sfile); return Gen_type (-1); } char *buffer = new char [buff_size+1]; int bytes_read; while ( (bytes_read = fread (buffer, sizeof (char), buff_size, output)) == buff_size ) { buffer [bytes_read] = '\0'; if (eval) eval_output += string (buffer); else p2tree->write_output (buffer); } buffer [bytes_read] = '\0'; if (eval) eval_output += string (buffer); else p2tree->write_output (buffer); if (pclose (output) != 0) { Msg::inst()->error ("error while executing " + sfile, "exec " + sfile); return Gen_type (-1); } if (eval) return Gen_type (eval_output); else return Gen_type (0); } void N_exec::deps () { N_dep::deps (); Gen_type gfile = file->exec (); if (!gfile.is_string ()) Msg::inst()->fatal ("N_exec::deps () -- Internal Error"); string sfile = gfile.get_string (); ((Deps_tree *)p2tree)->write_sources_list (sfile+"\n" + sfile+"~\n"); } Gen_type N_exec::exec () { set_msg_buffers (); if ( ((Exec_tree *)p2tree)->get_noext () == "warning") { Msg::inst()->warning ("can't execute script: --noext-warning used"); return Gen_type (""); } else if ( ((Exec_tree *)p2tree)->get_noext () == "error") { Msg::inst()->error ("can't execute script: --noext-error used"); return Gen_type (-1); } string sfile = (file->exec ().get_string ()); string eval_output; int fd_pipe[2]; if (pipe (fd_pipe)) { Msg::inst()->error ("error while executing " + sfile, "exec " + sfile); return Gen_type (-1); } pid_t pid = fork (); if (pid == (pid_t) 0) { int args_no = 2; // at least file name and NULL terminator char **ch_args; if (! n_args) ch_args = new char * [2]; else { Gen_type args = n_args->exec (); if (args.get_type () == 'l') { args_no += args.get_list ()->size (); ch_args = new char * [args_no]; int index = 1; for (Gen_type_c_iter i = args.get_list ()->begin (); i != args.get_list ()->end (); i++) { char *arg = new char [i->get_string ().length()]; i->get_string ().copy (arg, i->get_string ().length()); ch_args[index++] = arg; } } else if (args.get_type () == 'c') { args_no++; ch_args = new char * [args_no]; char *arg = new char [args.get_string ().length()]; args.get_string ().copy (arg, args.get_string ().length()); ch_args[1] = arg; } else { Msg::inst()->fatal ("N_exec::exec () -- internal error"); // Should raise an error telling user that a string is needed } } char *ch_file = new char[sfile.length ()]; sfile.copy (ch_file, sfile.length ()); ch_args[0] = ch_file; ch_args[args_no - 1] = NULL; string env_str = "LA=" + p2tree->adjust_level (); char *env_ch = new char [env_str.length()]; env_str.copy (env_ch, env_str.length ()); char * env[] = { env_ch, NULL }; close (fd_pipe[0]); dup2 (fd_pipe[1], STDOUT_FILENO); execve (sfile.c_str (), ch_args, env); } else if (pid < (pid_t) 0) { Msg::inst()->error ("error while executing " + sfile, "exec " + sfile); return Gen_type (-1); } else { close (fd_pipe[1]); char *buffer = new char [buff_size+1]; waitpid (pid, NULL, 0); // Child process has exited so we can read its output ssize_t bytes_read; while ((bytes_read=read (fd_pipe[0], buffer, buff_size*sizeof(char))) == buff_size) { buffer [bytes_read] = '\0'; if (eval) eval_output += string (buffer); else p2tree->write_output (buffer); } buffer [bytes_read] = '\0'; if (eval) eval_output += string (buffer); else p2tree->write_output (buffer); close (fd_pipe[0]); } if (eval) return Gen_type (eval_output); else return Gen_type (0); } Gen_type N_for::exec () { set_msg_buffers (); before->exec (); while (cond->exec ()) { stmt->exec (); action->exec (); } return Gen_type (0); } Gen_type N_while::exec () { set_msg_buffers (); while (cond->exec ()) stmt->exec (); return Gen_type (0); } // Arrays Gen_type N_foreach::exec () { set_msg_buffers (); Gen_type arr = arr_expr->exec (); if (! arr.is_array ()) { Msg::inst()->error ("Foreach loops work on arrays as in: foreach (item in array [])"); return Gen_type (1); } N_base_var *bvar = ((N_var *)loop_var); for (Gen_type_c_iter i = (arr.get_array ())->begin (); i != (arr.get_array ())->end (); i++) { if (bvar->array_syntax ()) if (i->is_array ()) p2tree->vars.set_var (bvar->get_var_name (), *i); else Msg::inst()->error ("Can't assign scalar to array value"); else if (bvar->item_syntax () || bvar->var_item_syntax ()) p2tree->vars.set_array_element (bvar->get_var_name (), ((N_var *)loop_var)->get_index (), *i); else p2tree->vars.set_var (bvar->get_var_name (), *i); stmt->exec (); } return Gen_type (0); } Gen_type N_list::exec () { set_msg_buffers (); Gen_type list = expr_list->exec (); if (! list.is_list ()) { // We are starting building the list Gen_type ret_value ('l'); ret_value.push (list); list = ret_value; } Gen_type to_add = new_expr->exec (); list.push (to_add); return list; } Gen_type N_fix_list::exec () { set_msg_buffers (); Gen_type list_obj = list->exec (); if (list_obj.is_array () ) { Gen_type ret_value ('a'); ret_value.push (list_obj); return ret_value; } else { list_obj.fix_list (); return list_obj; } } Gen_type N_del::exec () { set_msg_buffers (); Gen_type src_arr = source->exec (); if (! src_arr.is_array ()) { Msg::inst()->error ("delete requires an array as first variable"); return Gen_type (-1); } Gen_type pos_gt = pos->exec (); if (! pos_gt.is_int ()) { Msg::inst()->error ("delete requires an int as second variable"); return Gen_type (-1); } int pos_int = pos_gt.get_int (); if (pos_int < 0) { Msg::inst()->error ("array indexes must be > 0"); return Gen_type (-1); } if (len) { Gen_type len_gt = len->exec (); if (! len_gt.is_int ()) { Msg::inst()->error ("delete requires an int as third variable"); return Gen_type (-1); } int len_int = len_gt.get_int (); if (len_int < 0) { Msg::inst()->error ("number of elements to delete must be > 0"); return Gen_type (-1); } src_arr.erase (pos_int, len_int); } else { src_arr.erase (pos_int); } return src_arr; } Gen_type N_push::exec () { set_msg_buffers (); Gen_type src_arr = source->exec (); if (! src_arr.is_array ()) { Msg::inst()->error ("push requires an array as first variable"); return Gen_type (-1); } if (pos) src_arr.set_element (pos->exec (), item->exec ()); else src_arr.set_element (item->exec ()); return src_arr; } Gen_type N_random::exec () { set_msg_buffers (); Gen_type minimum = from->exec (); Gen_type maximum = to->exec (); if (! minimum.is_int ()) { Msg::inst()->error ("minimum value must be an integer"); return Gen_type (""); } if (! maximum.is_int ()) { Msg::inst()->error ("maximum value must be an integer"); return Gen_type (""); } int maximum_int = maximum.get_int () + 1; int minimum_int = minimum.get_int (); if (maximum_int <= minimum_int) { Msg::inst()->error ("maximum value must be greater than minimum"); return Gen_type (""); } int result = minimum_int + (int) (rand () * (double) (maximum_int - minimum_int) / (double) (RAND_MAX)); return Gen_type (result); } // Link Adjustment Gen_type N_adj_link::exec () { set_msg_buffers (); return Gen_type (p2tree->adjust_level ()); } Gen_type N_fileexists::exec () { set_msg_buffers (); string sfile = file->exec ().get_string (); if (sfile [0] != '/') sfile = dir + sfile; return Gen_type (file_exists (sfile)); } Gen_type N_fill::exec () { set_msg_buffers (); string tmpl_str = (tmpl_string->exec ()).to_string (true); Gen_type fill_vars = n_vars->exec (); if (fill_vars.is_list () || fill_vars.is_array ()) { Gen_type_c_iter from; Gen_type_c_iter to; if (fill_vars.is_list ()) { from = fill_vars.get_list ()->begin (); to = fill_vars.get_list ()->end (); } else { from = fill_vars.get_array ()->begin (); to = fill_vars.get_array ()->end (); } for (Gen_type_c_iter i = from; i != to; i++) { int start = tmpl_str.find ("%s", 0); if (start >=0) tmpl_str.replace (start, 2, (i->to_string (true))); } } else { int start = tmpl_str.find ("%s", 0); tmpl_str.replace (start, 2, fill_vars.to_string (true)); } return Gen_type (tmpl_str); }