// // aegis - project change supervisor // Copyright (C) 1994-1996, 1999, 2002-2008 Peter Miller // // This program 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 3 of the License, or // (at your option) any later version. // // This program 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. If not, see // . // #include #include #include #include #include #include #include #include #include #include #include #include // // maximum width for numbers // #define MAX_WIDTH 200 // // info for cranking through the arguments // static size_t ac; static size_t ai; static rpt_value::pointer *av; // // NAME // build fake - construct formatting specifier string // // SYNOPSIS // void build_fake(char *fake, size_t fake_len, int flag, int width, // int prec, int qual, int spec); // // DESCRIPTION // The build_fake function is used to construct a format // specification string from the arguments presented. This is // used to guarantee exact replication of sprintf behaviour. // // ARGUMENTS // fake - buffer to store results // flag - the flag specified (zero if not) // width - the width specified (zero if not) // prec - the precision specified (zero if not) // qual - the qualifier specified (zero if not) // spec - the formatting specifier specified // static void build_fake(char *fake, size_t fake_len, int flag, int width, int precision, int qualifier, int specifier) { trace(("%s\n", __PRETTY_FUNCTION__)); char *fp; fp = fake; *fp++ = '%'; if (flag) *fp++ = flag; if (width > 0) { snprintf(fp, fake + fake_len - fp - 5, "%d", width); fp += strlen(fp); } *fp++ = '.'; snprintf(fp, fake + fake_len - fp - 3, "%d", precision); fp += strlen(fp); if (qualifier) *fp++ = qualifier; *fp++ = specifier; *fp = 0; } rpt_func_sprintf::~rpt_func_sprintf() { trace(("%s\n", __PRETTY_FUNCTION__)); } rpt_func_sprintf::rpt_func_sprintf() { trace(("%s\n", __PRETTY_FUNCTION__)); } rpt_func::pointer rpt_func_sprintf::create() { trace(("%s\n", __PRETTY_FUNCTION__)); return pointer(new rpt_func_sprintf()); } const char * rpt_func_sprintf::name() const { return "sprintf"; } bool rpt_func_sprintf::optimizable() const { return true; } bool rpt_func_sprintf::verify(const rpt_expr::pointer &ep) const { trace(("%s\n", __PRETTY_FUNCTION__)); return (ep->get_nchildren() >= 1); } static rpt_value::pointer get_arg(const rpt_expr::pointer &ep) { trace(("%s\n", __PRETTY_FUNCTION__)); if (ai >= ac) { sub_context_ty sc; sc.var_set_charstar("Function", "sprintf"); nstring s(sc.subst_intl(i18n("$function: too few arguments"))); return rpt_value_error::create(ep->get_pos(), s); } return av[ai++]; } static rpt_value::pointer get_arg_string(const rpt_expr::pointer &ep) { trace(("%s\n", __PRETTY_FUNCTION__)); rpt_value::pointer vp1 = get_arg(ep); assert(!vp1->is_an_error()); rpt_value::pointer vp2 = rpt_value::stringize(vp1); if (dynamic_cast(vp2.get())) return vp2; sub_context_ty sc; sc.var_set_charstar("Function", "sprintf"); sc.var_set_charstar("Name", vp2->name()); sc.var_set_long("Number", (long)ai); nstring s ( sc.subst_intl ( i18n("$function: argument $number: string value required " "(was given $name)") ) ); return rpt_value_error::create(ep->get_pos(), s); } static rpt_value::pointer get_arg_integer(const rpt_expr::pointer &ep, bool real_ok) { trace(("%s\n", __PRETTY_FUNCTION__)); rpt_value::pointer vp1 = get_arg(ep); assert(!vp1->is_an_error()); if (real_ok) { rpt_value::pointer vp2 = rpt_value::integerize(vp1); if (dynamic_cast(vp2.get())) return vp2; } else { rpt_value::pointer vp2 = rpt_value::arithmetic(vp1); if (dynamic_cast(vp2.get())) return vp2; } sub_context_ty sc; sc.var_set_charstar("Function", "sprintf"); sc.var_set_charstar("Name", vp1->name()); sc.var_set_long("Number", (long)ai); nstring s ( sc.subst_intl ( i18n("$function: argument $number: integer value required " "(was given $name)") ) ); return rpt_value_error::create(ep->get_pos(), s); } static rpt_value::pointer get_arg_real(const rpt_expr::pointer &ep) { trace(("%s\n", __PRETTY_FUNCTION__)); rpt_value::pointer vp1 = get_arg(ep); assert(!vp1->is_an_error()); rpt_value::pointer vp2 = rpt_value::realize(vp1); if (dynamic_cast(vp2.get())) return vp2; sub_context_ty sc; sc.var_set_charstar("Function", "sprintf"); sc.var_set_charstar("Name", vp1->name()); sc.var_set_long("Number", (long)ai); nstring s ( sc.subst_intl ( i18n("$function: argument $number: real value required (was " "given $name)") ) ); return rpt_value_error::create(ep->get_pos(), s); } rpt_value::pointer rpt_func_sprintf::run(const rpt_expr::pointer &ep, size_t argc, rpt_value::pointer *argv) const { trace(("%s\n", __PRETTY_FUNCTION__)); // // Build the result string in a temporary buffer. // Grow the temporary buffer as necessary. // static nstring_accumulator buffer; buffer.clear(); // // It is important to only make one pass across the variable argument // list. Behaviour is undefined for more than one pass. // ac = argc; ai = 0; av = argv; // // get the format string // rpt_value::pointer fmt_vp = get_arg_string(ep); if (fmt_vp->is_an_error()) return fmt_vp; rpt_value_string *fmt_vsp = dynamic_cast(fmt_vp.get()); assert(fmt_vsp); const char *fmt = fmt_vsp->query().c_str(); // // interpret the format string // for (;;) { unsigned char c = *fmt++; if (!c) break; if (c != '%') { buffer.push_back(c); continue; } c = *fmt++; if (!c) break; // // get optional flag // int flag = 0; switch (c) { case '+': case '-': case '#': case '0': case ' ': flag = c; c = *fmt++; break; } // // get optional width // int width = 0; bool width_set = false; switch (c) { case '*': { rpt_value::pointer w = get_arg_integer(ep, false); if (w->is_an_error()) return w; rpt_value_integer *wip = dynamic_cast(w.get()); assert(wip); width = wip->query(); if (width < 0) { flag = '-'; width = -width; } if (width > MAX_WIDTH) { sub_context_ty sc; sc.var_set_charstar("Function", "sprintf"); sc.var_set_long("Number", (long)ai); sc.var_set_long("Value", width); nstring s ( sc.subst_intl ( i18n("$function: argument $number: width " "$value out of range") ) ); return rpt_value_error::create(ep->get_pos(), s); } c = *fmt++; width_set = true; } break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': for (;;) { width = width * 10 + c - '0'; c = *fmt++; switch (c) { default: break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': continue; } break; } if (width > MAX_WIDTH) { sub_context_ty sc; sc.var_set_charstar("Function", "sprintf"); sc.var_set_long("Number", (long)ai); sc.var_set_long("Value", width); nstring s ( sc.subst_intl ( i18n("$function: argument $number: width $value " "out of range") ) ); return rpt_value_error::create(ep->get_pos(), s); } width_set = true; break; default: break; } // // get optional precision // int prec = 0; bool prec_set = false; if (c == '.') { c = *fmt++; switch (c) { default: prec_set = true; break; case '*': { c = *fmt++; rpt_value::pointer p = get_arg_integer(ep, false); if (p->is_an_error()) return p; rpt_value_integer *pip = dynamic_cast(p.get()); assert(pip); prec = pip->query(); if (prec < 0 || prec > MAX_WIDTH) { sub_context_ty sc; sc.var_set_charstar("Function", "sprintf"); sc.var_set_long("Number", (long)ai); sc.var_set_long("Value", prec); nstring s ( sc.subst_intl ( i18n("$function: argument $number: " "precision of $value is out of range") ) ); return rpt_value_error::create(ep->get_pos(), s); } prec_set = true; } break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': for (;;) { prec = prec * 10 + c - '0'; c = *fmt++; switch (c) { default: break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': continue; } break; } if (prec > MAX_WIDTH) { sub_context_ty sc; sc.var_set_charstar("Function", "sprintf"); sc.var_set_long("Number", (long)ai); sc.var_set_long("Value", prec); nstring s ( sc.subst_intl ( i18n("$function: argument $number: precision " "of $value is out of range") ) ); return rpt_value_error::create(ep->get_pos(), s); } prec_set = true; break; } } // // get the optional qualifier // switch (c) { default: break; case 'l': case 'h': case 'L': c = *fmt++; break; } // // get conversion specifier // if (!c) break; switch (c) { default: { sub_context_ty sc; sc.var_set_charstar("Function", "sprintf"); sc.var_set_format("Name", "%c", c); nstring s ( sc.subst_intl ( i18n("$function: unknown format specifier '$name'") ) ); return rpt_value_error::create(ep->get_pos(), s); } case '%': buffer.push_back(c); continue; case 'c': { rpt_value::pointer tmp = get_arg_integer(ep, true); if (tmp->is_an_error()) return tmp; rpt_value_integer *ip = dynamic_cast(tmp.get()); assert(ip); long a = ip->query(); if (!prec_set) prec = true; char fake[MAX_WIDTH]; build_fake(fake, sizeof(fake), flag, width, prec, 0, c); char num[MAX_WIDTH + 1]; snprintf(num, sizeof(num), fake, a); buffer.push_back(num); } break; case 'd': case 'i': { rpt_value::pointer tmp = get_arg_integer(ep, true); if (tmp->is_an_error()) return tmp; rpt_value_integer *ip = dynamic_cast(tmp.get()); assert(ip); long a = ip->query(); if (!prec_set) prec = true; char fake[MAX_WIDTH]; build_fake(fake, sizeof(fake), flag, width, prec, 'l', c); char num[MAX_WIDTH + 1]; snprintf(num, sizeof(num), fake, a); buffer.push_back(num); } break; case 'e': case 'f': case 'g': case 'E': case 'F': case 'G': { rpt_value::pointer tmp = get_arg_real(ep); if (tmp->is_an_error()) return tmp; rpt_value_real *rp = dynamic_cast(tmp.get()); assert(rp); double a = rp->query(); if (!prec_set) prec = 6; if (prec > MAX_WIDTH) prec = MAX_WIDTH; char fake[MAX_WIDTH]; build_fake(fake, sizeof(fake), flag, width, prec, 0, c); char num[MAX_WIDTH + 1]; snprintf(num, sizeof(num), fake, a); buffer.push_back(num); } break; case 'u': case 'o': case 'x': case 'X': { rpt_value::pointer tmp = get_arg_integer(ep, true); if (tmp->is_an_error()) return tmp; rpt_value_integer *ip = dynamic_cast(tmp.get()); assert(ip); unsigned long a = ip->query(); if (!prec_set) prec = true; char fake[MAX_WIDTH]; build_fake(fake, sizeof(fake), flag, width, prec, 'l', c); char num[MAX_WIDTH + 1]; snprintf(num, sizeof(num), fake, a); buffer.push_back(num); } break; case 's': { rpt_value::pointer tmp = get_arg_string(ep); if (tmp->is_an_error()) return tmp; rpt_value_string *sp = dynamic_cast(tmp.get()); assert(sp); nstring a(sp->query()); size_t len = a.size(); if (!prec_set) prec = len; if (len < (size_t)prec) prec = len; if (!width_set) width = prec; if (width < prec) width = prec; len = width; if (flag != '-') { while (width > prec) { buffer.push_back(' '); width--; } } buffer.push_back(a.c_str(), prec); width -= prec; if (flag == '-') { while (width > 0) { buffer.push_back(' '); width--; } } } break; } } // assert(buffer.count_nul_characters() == 0); if (ai < argc) { sub_context_ty sc; sc.var_set_charstar("Function", "sprintf"); sc.var_set_long("Number1", (long)argc); sc.var_set_long("Number2", (long)ai); nstring s ( sc.subst_intl ( i18n("$function: too many arguments ($number1 given, " "only $number2 used)") ) ); return rpt_value_error::create(ep->get_pos(), s); } // // all done // return rpt_value_string::create(buffer.mkstr()); }