Branch: Tag:

2000-02-13

2000-02-13 18:04:40 by Martin Stjernholm <mast@lysator.liu.se>

Better error handling. Some incompatible cleanups:

o FLAG_CONTAINER is replaced by FLAG_NONCONTAINER, with the reverse
meaning.
o PHtml changed name to PXml.
o Error routines rxml_error() and rxml_fatal() are replaced by
rxml_run_error() and rxml_parse_error().

Rev: server/etc/modules/RXML.pmod/module.pmod:1.53

2:   //!   //! Created 1999-07-30 by Martin Stjernholm.   //! - //! $Id: module.pmod,v 1.52 2000/02/13 11:03:32 mast Exp $ + //! $Id: module.pmod,v 1.53 2000/02/13 18:04:40 mast Exp $      //! Kludge: Must use "RXML.refs" somewhere for the whole module to be   //! loaded correctly.    - //! WARNING: This API is not yet set in stone; expect incompatible - //! changes. + //! WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING + //! WARNING WARNING + //! WARNING This API is not yet set in stone. WARNING + //! WARNING Expect incompatible changes. WARNING + //! WARNING WARNING + //! WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING      //#pragma strict_types // Disabled for now since it doesn't work well enough.   
49:    Type def_arg_type = t_text (PEnt);    //! The type used for arguments that isn't present in neither    //! req_arg_types nor opt_arg_types. This default is a parser that -  //! only parses HTML-style entities. +  //! only parses XML-style entities.    -  Type content_type = t_same (PHtml); +  Type content_type = t_same (PXml);    //! The handled type of the content, if the tag gets any.    //!    //! This default is the special type t_same, which means the type is -  //! taken from the effective type of the result. The PHtml argument -  //! causes the HTML parser to be used to read it, which means that -  //! the content is preparsed with HTML syntax. Use no parser to get -  //! the raw text. +  //! taken from the effective type of the result. The PXml argument +  //! causes the standard XML parser to be used to read it, which +  //! means that the content is preparsed with XML syntax. Use no +  //! parser to get the raw text.       array(Type) result_types = ({t_xml, t_html, t_text});    //! The possible types of the result, in order of precedence. If a
141:    if (dont_throw) return 0;    else {    array(string) missing = sort (indices (req_arg_types - atypes)); -  rxml_fatal ("Required " + +  rxml_parse_error ("Required " +    (sizeof (missing) > 1 ?    "arguments " + String.implode_nicely (missing) + " are" :    "argument " + missing[0] + " is") + " missing.\n");
155: Inside #if defined(MODULE_DEBUG)
  #ifdef MODULE_DEBUG    }) {    if (objectp (err) && ([object] err)->thrown_at_unwind) -  error ("Can't save parser state when evaluating arguments.\n"); -  throw (err); +  fatal_error ("Can't save parser state when evaluating arguments.\n"); +  throw_fatal (err);    }   #endif    return 1;
204: Inside #if defined(DEBUG)
   if (!ustate) ustate = ctx->unwind_state = ([]);   #ifdef DEBUG    if (err != frame) -  error ("Internal error: Unexpected unwind object catched.\n"); +  fatal_error ("Internal error: Unexpected unwind object catched.\n");    if (ustate[parser]) -  error ("Internal error: Clobbering unwind state for parser.\n"); +  fatal_error ("Internal error: Clobbering unwind state for parser.\n");   #endif    ustate[parser] = ({err}); -  err = parser; +  throw (err = parser);    } -  -  throw (err); +  else throw_fatal (err);    }       DECLARE_CNT (__count);
358:    if (objectp (tagdef) && ([object] tagdef)->is_RXML_Tag)    name = [string] ([object] tagdef)->name;   #ifdef MODULE_DEBUG -  if (!name) error ("Need tag name.\n"); +  if (!name) fatal_error ("Need tag name.\n");   #endif    if (tags[name] == tagdef ||    (low_containers && low_containers[name] == tagdef) ||
549:   //! Interface for objects that emulates a scope mapping.   {    mixed `[] (string var, void|Context ctx, void|string scope_name) -  {rxml_fatal ("Cannot query variable" + _in_the_scope (scope_name) + ".\n");} +  {rxml_parse_error ("Cannot query variable" + _in_the_scope (scope_name) + ".\n");}       mixed `[]= (string var, mixed val, void|Context ctx, void|string scope_name) -  {rxml_fatal ("Cannot set variable" + _in_the_scope (scope_name) + ".\n");} +  {rxml_parse_error ("Cannot set variable" + _in_the_scope (scope_name) + ".\n");}       array(string) _indices (void|Context ctx, void|string scope_name) -  {rxml_fatal ("Cannot list variables" + _in_the_scope (scope_name) + ".\n");} +  {rxml_parse_error ("Cannot list variables" + _in_the_scope (scope_name) + ".\n");}       void m_delete (string var, void|Context ctx, void|string scope_name) -  {rxml_fatal ("Cannot delete variable" + _in_the_scope (scope_name) + ".\n");} +  {rxml_parse_error ("Cannot delete variable" + _in_the_scope (scope_name) + ".\n");}       private string _in_the_scope (string scope_name)    {
650:    else    return val;    } -  else if (scope_name) rxml_fatal ("Unknown scope %O.\n", scope_name); -  else rxml_fatal ("No current scope.\n"); +  else if (scope_name) rxml_parse_error ("Unknown scope %O.\n", scope_name); +  else rxml_parse_error ("No current scope.\n");    }       mixed user_get_var (string var, void|string scope_name, void|Type want_type)
677:    return ([object(Scope)] vars)->`[]= (var, val, this_object(), scope_name || "_");    else    return vars[var] = val; -  else if (scope_name) rxml_fatal ("Unknown scope %O.\n", scope_name); -  else rxml_fatal ("No current scope.\n"); +  else if (scope_name) rxml_parse_error ("Unknown scope %O.\n", scope_name); +  else rxml_parse_error ("No current scope.\n");    }       mixed user_set_var (string var, mixed val, void|string scope_name)
704:    ([object(Scope)] vars)->m_delete (var, this_object(), scope_name || "_");    else    m_delete ([mapping(string:mixed)] vars, var); -  else if (scope_name) rxml_fatal ("Unknown scope %O.\n", scope_name); -  else rxml_fatal ("No current scope.\n"); +  else if (scope_name) rxml_parse_error ("Unknown scope %O.\n", scope_name); +  else rxml_parse_error ("No current scope.\n");    }       void user_delete_var (string var, void|string scope_name)
731:    return ([object(Scope)] vars)->_indices (this_object(), scope_name || "_");    else    return indices ([mapping(string:mixed)] vars); -  else if (scope_name) rxml_fatal ("Unknown scope %O.\n", scope_name); -  else rxml_fatal ("No current scope.\n"); +  else if (scope_name) rxml_parse_error ("Unknown scope %O.\n", scope_name); +  else rxml_parse_error ("No current scope.\n");    }       array(string) list_scopes()
791:    else oldvars = scopes[scope_name];    }   #ifdef DEBUG -  if (!oldvars) error ("Internal error: I before e except after c.\n"); +  if (!oldvars) fatal_error ("Internal error: I before e except after c.\n");   #endif    if (!mappingp(vars)) {    return 0;
807:    //! Removes the named scope from the global level, if it exists.    {   #ifdef MODULE_DEBUG -  if (scope_name == "_") error ("Cannot remove current scope.\n"); +  if (scope_name == "_") fatal_error ("Cannot remove current scope.\n");   #endif    Frame outermost;    for (Frame f = frame; f; f = f->up)
848:    new_runtime_tags->remove_tags[tag] = 1;    }    -  string describe_rxml_backtrace (Frame f, void|string current_var) -  //! Returns a formatted backtrace from the given frame. If -  //! current_var is specified, it's taken to be the a variable entity -  //! being parsed on top of the frame. -  { -  string msg = current_var ? " | &" + current_var + ";\n" : ""; -  for (; f; f = f->up) { -  if (f->tag) msg += " | <" + f->tag->name; -  else if (!f->up) break; -  else msg += " | <(unknown tag)"; -  if (f->args) -  foreach (sort (indices (f->args)), string arg) { -  mixed val = f->args[arg]; -  msg += " " + arg + "="; -  if (arrayp (val)) msg += map (val, error_print_val) * ","; -  else msg += error_print_val (val); -  } -  else msg += " (no argmap)"; -  msg += ">\n"; -  } -  return msg; -  } -  -  void rxml_error (string msg, mixed... args) -  //! Throws an RXML error with a dump of the parser stack. This is -  //! intended to be used by tags for errors that can occur during -  //! normal operation, such as when the connection to an SQL server -  //! fails. -  { -  if (sizeof (args)) msg = sprintf (msg, @args); -  msg = rxml_error_prefix + ": " + msg + -  describe_rxml_backtrace (frame, current_var); -  array b = backtrace(); -  throw (({msg, b[..sizeof (b) - 2]})); -  } -  -  void rxml_fatal (string msg, mixed... args) -  //! Throws an RXML fatal error with a dump of the parser stack. This -  //! is intended to be used for programming errors in the RXML code, -  //! such as lookups in nonexisting scopes and invalid arguments to a -  //! tag. -  { -  if (sizeof (args)) msg = sprintf (msg, @args); -  msg = rxml_fatal_prefix + ": " + msg + -  describe_rxml_backtrace (frame, current_var); -  array b = backtrace(); -  throw (({msg, b[..sizeof (b) - 2]})); -  } -  +     void handle_exception (mixed err, PCode|Parser evaluator)    //! This function gets any exception that is catched during    //! evaluation. evaluator is the object that catched the error.    {    error_count++; -  string msg = describe_error (err); -  int error = msg[..sizeof (rxml_error_prefix) - 1] == rxml_error_prefix; -  if (error || msg[..sizeof (rxml_fatal_prefix) - 1] == rxml_fatal_prefix) { -  // An RXML error. -  while (evaluator->_parent) { -  evaluator->error_count++; -  evaluator = evaluator->_parent; -  } +  if (objectp (err) && err->is_RXML_Backtrace && err->type != "fatal") { +  string msg; +  for (object(PCode)|object(Parser) e = evaluator->_parent; e; e = e->_parent) +  e->error_count++;    if (id && id->conf) -  msg = (error ? -  ([function(mixed,Type:string)] -  ([object] id->conf)->handle_rxml_error) : -  ([function(mixed,Type:string)] -  ([object] id->conf)->handle_rxml_fatal) -  ) (err, evaluator->type); +  msg = (err->type == "run" ? +  ([function(Backtrace,Type:string)] +  ([object] id->conf)->handle_rxml_run_error) : +  ([function(Backtrace,Type:string)] +  ([object] id->conf)->handle_rxml_parse_error) +  ) ([object(Backtrace)] err, evaluator->type);    else {   #ifdef MODULE_DEBUG    report_notice (describe_backtrace (err));   #else -  report_notice (msg); +  report_notice (err->msg);   #endif    } -  if (msg && evaluator->type->free_text && evaluator->report_error) -  evaluator->report_error (msg); +  if (msg) +  while (evaluator) { +  if (evaluator->report_error && evaluator->type->free_text && +  evaluator->report_error (msg)) +  break; +  evaluator = evaluator->_parent;    } -  else throw (err); +     } -  +  else throw_fatal (err); +  }       // Internals.    -  constant rxml_error_prefix = "RXML error"; -  constant rxml_fatal_prefix = "RXML fatal"; -  -  private string error_print_val (mixed val) -  { -  if (arrayp (val)) return "array"; -  else if (mappingp (val)) return "mapping"; -  else if (multisetp (val)) return "multiset"; -  else return sprintf ("%O", val); -  } -  +     string current_var;    // Used to get the parsed variable into the RXML error backtrace.   
951:    // Normally TagSet.`() should be used instead of this.    {   #ifdef MODULE_DEBUG -  if (in_use || frame) error ("Context already in use.\n"); +  if (in_use || frame) fatal_error ("Context already in use.\n");   #endif    return top_level_type->get_parser (this_object());    }
968:    void enter_scope (Frame frame)    {   #ifdef DEBUG -  if (!frame->vars) error ("Internal error: Frame has no variables.\n"); +  if (!frame->vars) fatal_error ("Internal error: Frame has no variables.\n");   #endif    if (!hidden[frame])    if (string scope_name = [string] frame->scope_name) {
989: Inside #if defined(MODULE_DEBUG)
   if (SCOPE_TYPE named = back[1]) {   #ifdef MODULE_DEBUG    if (!stringp (frame->scope_name)) -  error ("Scope named changed to %O during parsing.\n", frame->scope_name); +  fatal_error ("Scope named changed to %O during parsing.\n", frame->scope_name);   #endif    scopes[[string] frame->scope_name] = named;    }
1053:   #endif   }    + class Backtrace + //! The object used to throw RXML errors. + { +  constant is_generic_error = 1; +  constant is_RXML_Backtrace = 1;    -  +  string type; // Currently "run", "parse" or "fatal". +  string msg; +  Context context; +  Frame frame; +  string current_var; +  array backtrace; +  +  void create (string _type, string _msg, void|Context _context) +  { +  type = _type; +  msg = _msg; +  if (context = _context || get_context()) { +  frame = context->frame; +  current_var = context->current_var; +  } +  backtrace = predef::backtrace(); +  backtrace = backtrace[..sizeof (backtrace) - 2]; +  } +  +  string describe_rxml_backtrace (void|int no_msg) +  //! Returns a formatted RXML frame backtrace. +  { +  string txt = no_msg ? "" : "RXML " + type + " error"; +  if (context) { +  if (!no_msg) txt += ": " + (msg || "(no error message)\n"); +  txt += current_var ? " | &" + current_var + ";\n" : ""; +  for (Frame f = frame; f; f = f->up) { +  if (f->tag) txt += " | <" + f->tag->name; +  else if (!f->up) break; +  else txt += " | <(unknown tag)"; +  if (f->args) +  foreach (sort (indices (f->args)), string arg) { +  mixed val = f->args[arg]; +  txt += " " + arg + "="; +  if (arrayp (val)) txt += map (val, error_print_val) * ","; +  else txt += error_print_val (val); +  } +  else txt += " (no argmap)"; +  txt += ">\n"; +  } +  } +  else +  if (!no_msg) txt += " (no context): " + (msg || "(no error message)\n"); +  return txt; +  } +  +  private string error_print_val (mixed val) +  { +  if (arrayp (val)) return "array"; +  else if (mappingp (val)) return "mapping"; +  else if (multisetp (val)) return "multiset"; +  else return sprintf ("%O", val); +  } +  +  string|array `[] (int i) +  { +  switch (i) { +  case 0: return describe_rxml_backtrace(); +  case 1: return backtrace; +  } +  } +  +  string _sprintf() {return "RXML.Backtrace(" + type + ")";} + } +  +    //! Current context.      //! It's set before any function in RXML.Tag or RXML.Frame is called.
1077:    set_context (ctx); \    if (ctx) { \    if (ctx->in_use && ctx->in_use != this_thread()) \ -  error ("Attempt to use context asynchronously.\n"); \ +  fatal_error ("Attempt to use context asynchronously.\n"); \    ctx->in_use = this_thread(); \    }   
1100:      //! Constants for the bit field RXML.Frame.flags.    - constant FLAG_NONE = 0x00000000; + constant FLAG_NONE = 0x00000000;   //! The no-flags flag. In case you think 0 is too ugly. ;)      //! Static flags (i.e. tested in the Tag object).    - constant FLAG_CONTAINER = 0x00000001; - //! If set, the tag accepts non-empty content. E.g. with the standard - //! HTML parser this defines whether the tag is a container or not. + constant FLAG_NONCONTAINER = 0x00000001; + //! If set, the tag does not use any content. E.g. with a HTML parser + //! this defines whether the tag is a container or not, and in XML + //! parsing it simply causes the content (if any) to be thrown away.    - constant FLAG_NO_PREFIX = 0x00000002; + constant FLAG_NO_PREFIX = 0x00000002;   //! Never apply any prefix to this tag.    - constant FLAG_SOCKET_TAG = 0x0000004; + constant FLAG_SOCKET_TAG = 0x0000004;   //! Declare the tag to be a socket tag, which accepts plugin tags (see   //! Tag.plugin_name for details).    - constant FLAG_DONT_PREPARSE = 0x00000040; - //! Don't preparse the content with the PHtml parser. This is only - //! used in the simple tag wrapper. Defined here as placeholder. + constant FLAG_DONT_PREPARSE = 0x00000040; + //! Don't preparse the content with the PXml parser. This is only used + //! in the simple tag wrapper. Defined here as placeholder.    - constant FLAG_POSTPARSE = 0x00000080; - //! Postparse the result with the PHtml parser. This is only used in + constant FLAG_POSTPARSE = 0x00000080; + //! Postparse the result with the PXml parser. This is only used in   //! the simple tag wrapper. Defined here as placeholder.      //! The rest of the flags are dynamic (i.e. tested in the Frame object).    - constant FLAG_PARENT_SCOPE = 0x00000100; + constant FLAG_PARENT_SCOPE = 0x00000100;   //! If set, the array from do_enter(), do_return() and cached_return()   //! will be interpreted in the scope of the parent tag, rather than in   //! the current one.    - constant FLAG_NO_IMPLICIT_ARGS = 0x00000200; + constant FLAG_NO_IMPLICIT_ARGS = 0x00000200;   //! If set, the parser won't apply any implicit arguments. FIXME: Not   //! yet implemented.    - constant FLAG_STREAM_RESULT = 0x00000400; + constant FLAG_STREAM_RESULT = 0x00000400;   //! If set, the do_return() function will be called repeatedly until   //! it returns 0 or no more content is wanted.    - constant FLAG_STREAM_CONTENT = 0x00000800; + constant FLAG_STREAM_CONTENT = 0x00000800;   //! If set, the tag supports getting its content in streaming mode:   //! do_return() will be called repeatedly with successive parts of the   //! content then. Can't be changed from do_return().
1148:   //! less effective than nonstreaming, so it should only be done when   //! big delays are expected.    - constant FLAG_STREAM = FLAG_STREAM_RESULT | FLAG_STREAM_CONTENT; + constant FLAG_STREAM = FLAG_STREAM_RESULT | FLAG_STREAM_CONTENT;    - constant FLAG_UNPARSED = 0x00001000; + constant FLAG_UNPARSED = 0x00001000;   //! If set, args and content in the frame contain unparsed strings.   //! The frame will be parsed before it's evaluated. This flag should   //! never be set in Tag.flags, but it's useful when creating frames
1162:   //! The tag name is always the same. FIXME: These are ideas only; not   //! yet implemented.    - constant FLAG_CACHE_DIFF_ARGS = 0x00010000; + constant FLAG_CACHE_DIFF_ARGS = 0x00010000;   //! If set, the arguments to the tag need not be the same (using   //! equal()) as the cached args.   
1173:   //! If set, the result type need not be the same. (Typically   //! not useful unless cached_return() is used.)    - constant FLAG_CACHE_DIFF_VARS = 0x00080000; + constant FLAG_CACHE_DIFF_VARS = 0x00080000;   //! If set, the variables with external scope in vars (i.e. normally   //! those that has been accessed with get_var()) need not have the   //! same values (using equal()) as the actual variables.    - constant FLAG_CACHE_SAME_STACK = 0x00100000; + constant FLAG_CACHE_SAME_STACK = 0x00100000;   //! If set, the stack of call frames needs to be the same.      constant FLAG_CACHE_EXECUTE_RESULT = 0x00200000;
1352:       //! Services.    -  void rxml_error (string msg, mixed... args) -  //! Throws an RXML error from the current context. This is intended -  //! to be used by tags for errors that can occur during normal -  //! operation, such as when the connection to an SQL server fails. +  void rxml_run_error (string msg, mixed... args) +  //! Throws an RXML run error with a dump of the parser stack in the +  //! current context. This is intended to be used by tags for errors +  //! that can occur during normal operation, such as when the +  //! connection to an SQL server fails.    { -  get_context()/*HMM*/->rxml_error (msg, @args); +  _rxml_run_error (msg, @args);    }    -  void rxml_fatal (string msg, mixed... args) -  //! Throws an RXML fatal error from the current context. This is -  //! intended to be used for programming errors in the RXML code, -  //! such as lookups in nonexisting scopes and invalid arguments to a -  //! tag. +  void rxml_parse_error (string msg, mixed... args) +  //! Throws an RXML parse error with a dump of the parser stack in +  //! the current context. This is intended to be used for programming +  //! errors in the RXML code, such as lookups in nonexisting scopes +  //! and invalid arguments to a tag.    { -  get_context()/*HMM*/->rxml_fatal (msg, @args); +  _rxml_parse_error (msg, @args);    }       void terminate()    //! Makes the parser abort. The data parsed so far will be returned.    //! Does not return; throws a special exception instead.    { -  error ("FIXME\n"); +  fatal_error ("FIXME\n");    }       void suspend()
1383:    //! parser is used in a place that doesn't support nonblocking, so    //! just go ahead and block.    { -  error ("FIXME\n"); +  fatal_error ("FIXME\n");    }       void resume()    //! Makes the parser continue where it left off. The function that    //! called suspend() will be called again.    { -  error ("FIXME\n"); +  fatal_error ("FIXME\n");    }       mapping(string:Tag) get_plugins()
1402: Inside #if defined(MODULE_DEBUG)
   {   #ifdef MODULE_DEBUG    if (!(flags & FLAG_SOCKET_TAG)) -  error ("This tag is not a socket tag.\n"); +  fatal_error ("This tag is not a socket tag.\n");   #endif    return get_context()->tag_set->get_plugins (tag->name);    }
1436:    }    break;    case "mapping": -  error ("Header mappings not yet implemented.\n"); +  fatal_error ("Header mappings not yet implemented.\n");    break;    case "multiset":    if (sizeof ([multiset] elem) == 1) piece = ((array) elem)[0];    else if (sizeof ([multiset] elem) > 1) -  error (sizeof ([multiset] elem) + " values in multiset in exec array.\n"); -  else error ("No value in multiset in exec array.\n"); +  fatal_error (sizeof ([multiset] elem) + +  " values in multiset in exec array.\n"); +  else fatal_error ("No value in multiset in exec array.\n");    break;    default:    if (objectp (elem))
1458:    piece = ([object(Parser)] elem)->eval(); // Might unwind.    }    else -  error ("File objects not yet implemented.\n"); +  fatal_error ("File objects not yet implemented.\n");    else -  error ("Invalid type %t in exec array.\n", elem); +  fatal_error ("Invalid type %t in exec array.\n", elem);    }       if (result_type->sequential) res += piece;
1490:    // continue in it later. It's done here to keep the original    // exec array untouched.    ([array] ustate->exec_left)[0] = subparser; -  } +     throw (err);    } -  +  throw_fatal (err); +  }       private void _handle_runtime_tags (TagSetParser parser,    Context.RuntimeTags runtime_tags)
1536:    int tags_added; // Flag that we added additional_tags to ctx->tag_set.    //ctx->new_runtime_tags    - #define PRE_INIT_ERROR(X) (ctx->frame = this, error (X)) + #define PRE_INIT_ERROR(X) (ctx->frame = this, fatal_error (X))   #ifdef DEBUG    // Internal sanity checks.    if (ctx != get_context())
1597:    mapping(string:Type) atypes = raw_args & tag->req_arg_types;    if (sizeof (atypes) < sizeof (tag->req_arg_types)) {    array(string) missing = sort (indices (tag->req_arg_types - atypes)); -  rxml_fatal ("Required " + +  rxml_parse_error ("Required " +    (sizeof (missing) > 1 ?    "arguments " + String.implode_nicely (missing) + " are" :    "argument " + missing[0] + " is") + " missing.\n");
1612: Inside #if defined(MODULE_DEBUG)
  #ifdef MODULE_DEBUG    }) {    if (objectp (err) && ([object] err)->thrown_at_unwind) -  error ("Can't save parser state when evaluating arguments.\n"); -  throw (err); +  fatal_error ("Can't save parser state when evaluating arguments.\n"); +  throw_fatal (err);    }   #endif    }    }      #ifdef MODULE_DEBUG -  if (!args) error ("args not set.\n"); +  if (!args) fatal_error ("args not set.\n");   #endif       if (TagSet add_tags = raw_content && [object(TagSet)] this->additional_tags) {
1633:       if (!result_type) {   #ifdef MODULE_DEBUG -  if (!tag) error ("result_type not set in Frame object %O, " +  if (!tag) fatal_error ("result_type not set in Frame object %O, "    "and it has no Tag object to use for inferring it.\n",    this_object());   #endif
1648:    break;    }    if (!result_type) // Sigh.. -  rxml_fatal ( +  rxml_parse_error (    "Tag returns " +    String.implode_nicely ([array(string)] tag->result_types->name, "or") +    " but " + [string] parser->type->name + " is expected.\n");    }    if (!content_type) {   #ifdef MODULE_DEBUG -  if (!tag) error ("content_type not set in Frame object %O, " +  if (!tag) fatal_error ("content_type not set in Frame object %O, "    "and it has no Tag object to use for inferring it.\n",    this_object());   #endif
1675: Inside #if defined(DEBUG)
   if (flags & FLAG_STREAM_RESULT) {   #ifdef DEBUG    if (ctx->unwind_state) -  error ("Internal error: Clobbering unwind_state to do streaming.\n"); +  fatal_error ("Internal error: Clobbering unwind_state " +  "to do streaming.\n");    if (piece != Void) -  error ("Internal error: Thanks, we think about how nice it must " +  fatal_error ("Internal error: Thanks, we think about how nice it must "    "be to play the harmonica...\n");   #endif    if (result_type->quoting_scheme != parser->type->quoting_scheme)
1742: Inside #if defined(DEBUG)
   if (flags & FLAG_STREAM_RESULT) {   #ifdef DEBUG    if (!zero_type (ctx->unwind_state->stream_piece)) -  error ("Internal error: " +  fatal_error ("Internal error: "    "Clobbering unwind_state->stream_piece.\n");   #endif    if (result_type->quoting_scheme !=
1792: Inside #if defined(DEBUG)
   if (flags & FLAG_STREAM_RESULT) {   #ifdef DEBUG    if (ctx->unwind_state) -  error ("Internal error: Clobbering unwind_state " +  fatal_error ("Internal error: Clobbering unwind_state "    "to do streaming.\n");    if (piece != Void) -  error ("Internal error: Thanks, we think about how nice it must " -  "be to play the harmonica...\n"); +  fatal_error ("Internal error: Thanks, we think about how nice " +  "it must be to play the harmonica...\n");   #endif    if (result_type->quoting_scheme != parser->type->quoting_scheme)    res = parser->type->quote (res);
1840: Inside #if defined(DEBUG)
   if (!ustate) ustate = ctx->unwind_state = ([]);   #ifdef DEBUG    if (ustate[this]) -  error ("Internal error: Frame already has an unwind state.\n"); +  fatal_error ("Internal error: Frame already has an unwind state.\n");   #endif       if (ustate->exec_left) {
1880:    exec, tags_added, ctx->new_runtime_tags});    }    else { -  ctx->handle_exception (err, parser); // May throw. +  ctx->handle_exception (err, parser); // Will rethrow unknown errors.    action = "return";    }   
1888: Inside #if defined(MODULE_DEBUG)
   case "break": // Throw and handle in parent frame.   #ifdef MODULE_DEBUG    if (!parser->unwind_state) -  error ("Trying to unwind inside a parser that isn't unwind safe.\n"); +  fatal_error ("Trying to unwind inside a parser that isn't unwind safe.\n");   #endif    throw (this);    case "continue": // Continue in this frame through tail recursion.
1897:    case "return": // A normal return.    break;    default: -  error ("Internal error: Don't you come here and %O on me!\n", action); +  fatal_error ("Internal error: Don't you come here and %O on me!\n", action);    }    }   
1917:      // Global services.    - void rxml_error (string msg, mixed... args) - //! Tries to throw an error with rxml_error() in the current context. + void rxml_run_error (string msg, mixed... args) + //! Throws an RXML run error with a dump of the parser stack in the + //! current context. This is intended to be used by tags for errors + //! that can occur during normal operation, such as when the + //! connection to an SQL server fails.   { -  Context ctx = get_context(); -  if (ctx && ctx->rxml_error) -  ctx->rxml_error (msg, @args); -  else { +     if (sizeof (args)) msg = sprintf (msg, @args); -  msg = Context.rxml_error_prefix + " (no context): " + msg; -  array b = backtrace(); -  throw (({msg, b[..sizeof (b) - 2]})); +  throw (Backtrace ("run", msg, get_context()));   } -  +  + void rxml_parse_error (string msg, mixed... args) + //! Throws an RXML parse error with a dump of the parser stack in the + //! current context. This is intended to be used for programming + //! errors in the RXML code, such as lookups in nonexisting scopes and + //! invalid arguments to a tag. + { +  if (sizeof (args)) msg = sprintf (msg, @args); +  throw (Backtrace ("parse", msg, get_context()));   }    - void rxml_fatal (string msg, mixed... args) - //! Tries to throw a fatal error with rxml_fatal() in the current - //! context. + void fatal_error (string msg, mixed... args) + //! Throws a Pike error that isn't catched and handled anywhere. It's + //! just like the common error() function, but includes the RXML frame + //! backtrace.   { -  Context ctx = get_context(); -  if (ctx && ctx->rxml_fatal) -  ctx->rxml_fatal (msg, @args); -  else { +     if (sizeof (args)) msg = sprintf (msg, @args); -  msg = Context.rxml_fatal_prefix + " (no context): " + msg; -  array b = backtrace(); -  throw (({msg, b[..sizeof (b) - 2]})); +  array bt = backtrace(); +  throw_fatal (({msg, bt[..sizeof (bt) - 2]}));   } - } +     - void error (string msg, mixed... args) + void throw_fatal (mixed err) + //! Mainly used internally to throw an error that includes the RXML + //! frame backtrace.   { -  Context ctx = get_context(); -  array b = backtrace(); -  if (ctx && ctx->describe_rxml_backtrace) -  throw (({msg + "RXML backtrace: " + ctx->describe_rxml_backtrace (ctx->frame, ctx->current_var), b[..sizeof (b) - 2]})); -  else -  throw (({msg, b[..sizeof (b) - 2]})); +  if (arrayp (err) && sizeof (err) == 2 || +  objectp (err) && !err->is_RXML_Backtrace && err->is_generic_error) { +  string msg; +  if (catch (msg = err[0])) throw (err); +  if (stringp (msg) && !has_value (msg, "\nRXML frame backtrace:\n")) { +  Backtrace b = Backtrace ("fatal", 0); +  if (sizeof (msg) && msg[-1] != '\n') msg += "\n"; +  msg += "RXML frame backtrace:\n" + b->describe_rxml_backtrace (1); +  catch (err[0] = msg);    } -  +  } +  throw (err); + }      Frame make_tag (string name, mapping(string:mixed) args, void|mixed content)   //! Returns a frame for the specified tag. The tag definition is
1964:    TagSet tag_set = get_context()->tag_set;    object(Tag)|array(LOW_TAG_TYPE|LOW_CONTAINER_TYPE) tag = tag_set->get_tag (name);    if (arrayp (tag)) -  error ("Getting frames for low level tags are currently not implemented.\n"); +  fatal_error ("Getting frames for low level tags are currently not implemented.\n");    return tag (args, content);   }   
1977:    TagSet tag_set = get_context()->tag_set;    object(Tag)|array(LOW_TAG_TYPE|LOW_CONTAINER_TYPE) tag = tag_set->get_tag (name);    if (arrayp (tag)) -  error ("Getting frames for low level tags are currently not implemented.\n"); +  fatal_error ("Getting frames for low level tags are currently not implemented.\n");    Frame frame = tag (args, content);    frame->flags |= FLAG_UNPARSED;    return frame;
2035: Inside #if defined(MODULE_DEBUG)
   if (context && context->unwind_state && context->unwind_state->top) {   #ifdef MODULE_DEBUG    if (context->unwind_state->top != this_object()) -  error ("The context got an unwound state from another parser. Can't rewind.\n"); +  fatal_error ("The context got an unwound state from another parser. " +  "Can't rewind.\n");   #endif    m_delete (context->unwind_state, "top");    if (!sizeof (context->unwind_state)) context->unwind_state = 0;
2048: Inside #if defined(DEBUG)
   if (objectp (err) && ([object] err)->thrown_at_unwind) {   #ifdef DEBUG    if (err != this_object()) -  error ("Internal error: Unexpected unwind object catched.\n"); +  fatal_error ("Internal error: Unexpected unwind object catched.\n");   #endif    if (!context->unwind_state) context->unwind_state = ([]);    context->unwind_state->top = err;    }    else if (context) -  context->handle_exception (err, this_object()); // May throw. -  else throw (err); +  context->handle_exception (err, this_object()); // Will rethrow unknown errors. +  else throw_fatal (err);    return res;    }   
2069: Inside #if defined(MODULE_DEBUG)
   if (context && context->unwind_state && context->unwind_state->top) {   #ifdef MODULE_DEBUG    if (context->unwind_state->top != this_object()) -  error ("The context got an unwound state from another parser. Can't rewind.\n"); +  fatal_error ("The context got an unwound state from another parser. " +  "Can't rewind.\n");   #endif    m_delete (context->unwind_state, "top");    if (!sizeof (context->unwind_state)) context->unwind_state = 0;
2082: Inside #if defined(DEBUG)
   if (objectp (err) && ([object] err)->thrown_at_unwind) {   #ifdef DEBUG    if (err != this_object()) -  error ("Internal error: Unexpected unwind object catched.\n"); +  fatal_error ("Internal error: Unexpected unwind object catched.\n");   #endif    if (!context->unwind_state) context->unwind_state = ([]);    context->unwind_state->top = err;    }    else if (context) -  context->handle_exception (err, this_object()); // May throw. -  else throw (err); +  context->handle_exception (err, this_object()); // Will rethrow unknown errors. +  else throw_fatal (err);    }       array handle_var (string varref)
2150:    //! data may be given. It should work to call this on an already    //! finished stream if no argument is given to it.    -  optional void report_error (string msg); +  optional int report_error (string msg);    //! Used to report errors to the end user through the output. This -  //! is only called when the type allows free text. msg should be -  //! stored in the output queue to be returned by eval(). +  //! is only called when type->free_text is nonzero. msg should be +  //! stored in the output queue to be returned by eval(). If the +  //! context is bad for an error message, do nothing and return zero, +  //! and return nonzero if a message was written.       optional mixed read();    //! Define to allow streaming operation. Returns the evaluated
2345:       //!mixed free_text;    //! Nonzero if the type keeps the free text between parsed tokens, -  //! e.g. the plain text between tags in HTML. The type must be +  //! e.g. the plain text between tags in XML. The type must be    //! sequential and use strings.       void type_check (mixed val);    //! Checks whether the given value is a valid one of this type. Type -  //! errors are thrown with RXML.rxml_fatal(). +  //! errors are thrown with RXML.rxml_parse_error().       //!string quoting_scheme;    //! An identifier for the quoting scheme this type uses, if any. The
2565:       void type_check (mixed val)    { -  if (val != Void) rxml_fatal ("A value is not accepted.\n"); +  if (val != Void) rxml_parse_error ("A value is not accepted.\n");    }       mixed convert (mixed val)
2600:    string convert (mixed val)    {    if (mixed err = catch {return (string) val;}) -  rxml_fatal ("Couldn't convert value to text: " + describe_error (err)); +  rxml_parse_error ("Couldn't convert value to text: " + describe_error (err));    }       string _sprintf() {return "RXML.t_text" + PAREN_CNT (__count);}
2636:    string convert (mixed val, void|Type from)    {    if (mixed err = catch {val = (string) val;}) -  rxml_fatal ("Couldn't convert value to text: " + describe_error (err)); +  rxml_parse_error ("Couldn't convert value to text: " + describe_error (err));    if (!from || from->quoting_scheme != quoting_scheme)    val = quote ([string] val);    return val;
2755:    //!    {   #ifdef MODULE_DEBUG -  if (fin) error ("Cannot feed data to a finished stream.\n"); +  if (fin) fatal_error ("Cannot feed data to a finished stream.\n");   #endif    array tokens = scan (end + in, 0);    end = [string] tokens[-1];
2770:    {    if (in || !fin && sizeof (end)) {   #ifdef MODULE_DEBUG -  if (in && fin) error ("Cannot feed data to a finished stream.\n"); +  if (in && fin) fatal_error ("Cannot feed data to a finished stream.\n");   #endif    fin = 1;    if (in) end += in;
2843:   }       - // Various internal stuff. + // Various internal kludges.    -  + static function(string,mixed...:void) _rxml_run_error = rxml_run_error; + static function(string,mixed...:void) _rxml_parse_error = rxml_parse_error; +    // Argh! - static program PHtml; + static program PXml;   static program PEnt;   static program PExpr;   void _fix_module_ref (string name, mixed val)   {    mixed err = catch {    switch (name) { -  case "PHtml": PHtml = [program] val; break; +  case "PXml": PXml = [program] val; break;    case "PEnt": PEnt = [program] val; break;    case "PExpr": PExpr = [program] val; break;    case "empty_tag_set": empty_tag_set = [object(TagSet)] val; break;