struct vrend_context *vrend_create_context(int id, uint32_t nlen, const char *debug_name)
{
   struct vrend_context *grctx = CALLOC_STRUCT(vrend_context);

   if (!grctx)
      return NULL;

   if (nlen && debug_name) {
      strncpy(grctx->debug_name, debug_name,
	      nlen < sizeof(grctx->debug_name) - 1 ?
	      nlen : sizeof(grctx->debug_name) - 1);
      grctx->debug_name[sizeof(grctx->debug_name) - 1] = 0;
   }

   VREND_DEBUG(dbg_caller, grctx, "create context\n");

   grctx->ctx_id = id;

   list_inithead(&grctx->sub_ctxs);
   list_inithead(&grctx->active_nontimer_query_list);

   grctx->res_hash = vrend_object_init_ctx_table();

   grctx->shader_cfg.use_gles = vrend_state.use_gles;
   grctx->shader_cfg.use_core_profile = vrend_state.use_core_profile;
   grctx->shader_cfg.use_explicit_locations = vrend_state.use_explicit_locations;
   grctx->shader_cfg.max_draw_buffers = vrend_state.max_draw_buffers;
   grctx->shader_cfg.has_arrays_of_arrays = has_feature(feat_arrays_of_arrays);
   grctx->shader_cfg.has_gpu_shader5 = has_feature(feat_gpu_shader5);
   grctx->shader_cfg.has_es31_compat = has_feature(feat_gles31_compatibility);
   grctx->shader_cfg.has_conservative_depth = has_feature(feat_conservative_depth);

   vrend_renderer_create_sub_ctx(grctx, 0);
   vrend_renderer_set_sub_ctx(grctx, 0);

   vrender_get_glsl_version(&grctx->shader_cfg.glsl_version);

   list_addtail(&grctx->ctx_entry, &vrend_state.active_ctx_list);
   return grctx;
}


static bool vrend_compile_shader(struct vrend_context *ctx,
                                 struct vrend_shader *shader)
{
   GLint param;
   const char *shader_parts[SHADER_MAX_STRINGS];

   for (int i = 0; i < shader->glsl_strings.num_strings; i++)
      shader_parts[i] = shader->glsl_strings.strings[i].buf;
   glShaderSource(shader->id, shader->glsl_strings.num_strings, shader_parts, NULL);
   glCompileShader(shader->id);
   glGetShaderiv(shader->id, GL_COMPILE_STATUS, &param);
   if (param == GL_FALSE) {
      char infolog[65536];
      int len;
      glGetShaderInfoLog(shader->id, 65536, &len, infolog);
      report_context_error(ctx, VIRGL_ERROR_CTX_ILLEGAL_SHADER, 0);
      vrend_printf("shader failed to compile\n%s\n", infolog);
      vrend_shader_dump(shader);
      return false;
   }
   return true;
}


int vrend_end_query(struct vrend_context *ctx, uint32_t handle)
{
   struct vrend_query *q;
   q = vrend_object_lookup(ctx->sub->object_hash, handle, VIRGL_OBJECT_QUERY);
   if (!q)
      return EINVAL;

   if (q->index > 0 && !has_feature(feat_transform_feedback3))
      return EINVAL;

   if (vrend_is_timer_query(q->gltype)) {
      if (q->gltype == GL_TIMESTAMP && !has_feature(feat_timer_query)) {
         report_gles_warn(ctx, GLES_WARN_TIMESTAMP);
      } else if (q->gltype == GL_TIMESTAMP) {
         glQueryCounter(q->id, q->gltype);
      } else {
         /* remove from active query list for this context */
         glEndQuery(q->gltype);
      }
      return 0;
   }

   if (q->index > 0)
      glEndQueryIndexed(q->gltype, q->index);
   else
      glEndQuery(q->gltype);
   return 0;
}


void vrend_set_streamout_targets(struct vrend_context *ctx,
                                 UNUSED uint32_t append_bitmask,
                                 uint32_t num_targets,
                                 uint32_t *handles)
{
   struct vrend_so_target *target;
   uint i;

   if (!has_feature(feat_transform_feedback))
      return;

   if (num_targets) {
      bool found = false;
      struct vrend_streamout_object *obj;
      LIST_FOR_EACH_ENTRY(obj, &ctx->sub->streamout_list, head) {
         if (obj->num_targets == num_targets) {
            if (!memcmp(handles, obj->handles, num_targets * 4)) {
               found = true;
               break;
            }
         }
      }
      if (found) {
         ctx->sub->current_so = obj;
         glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, obj->id);
         return;
      }

      obj = CALLOC_STRUCT(vrend_streamout_object);
      if (has_feature(feat_transform_feedback2)) {
         glGenTransformFeedbacks(1, &obj->id);
         glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, obj->id);
      }
      obj->num_targets = num_targets;
      for (i = 0; i < num_targets; i++) {
         obj->handles[i] = handles[i];
         if (handles[i] == 0)
            continue;
         target = vrend_object_lookup(ctx->sub->object_hash, handles[i], VIRGL_OBJECT_STREAMOUT_TARGET);
         if (!target) {
            report_context_error(ctx, VIRGL_ERROR_CTX_ILLEGAL_HANDLE, handles[i]);
            free(obj);
            return;
         }
         vrend_so_target_reference(&obj->so_targets[i], target);
      }
      vrend_hw_emit_streamout_targets(ctx, obj);
      list_addtail(&obj->head, &ctx->sub->streamout_list);
      ctx->sub->current_so = obj;
      obj->xfb_state = XFB_STATE_STARTED_NEED_BEGIN;
   } else {
      if (has_feature(feat_transform_feedback2))
         glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0);
      ctx->sub->current_so = NULL;
   }
}


static int check_resource_valid(struct vrend_renderer_resource_create_args *args)
{
   /* do not accept handle 0 */
   if (args->handle == 0)
      return -1;

   /* limit the target */
   if (args->target >= PIPE_MAX_TEXTURE_TYPES)
      return -1;

   if (args->format >= VIRGL_FORMAT_MAX)
      return -1;
   bool format_can_texture_storage = has_feature(feat_texture_storage) &&
         (tex_conv_table[args->format].flags & VIRGL_TEXTURE_CAN_TEXTURE_STORAGE);

   /* only texture 2d and 2d array can have multiple samples */
   if (args->nr_samples > 0) {
      if (!has_feature(feat_texture_multisample))
         return -1;

      if (args->target != PIPE_TEXTURE_2D && args->target != PIPE_TEXTURE_2D_ARRAY)
         return -1;
      /* multisample can't have miplevels */
      if (args->last_level > 0)
         return -1;
   }

   if (args->last_level > 0) {
      /* buffer and rect textures can't have mipmaps */
      if (args->target == PIPE_BUFFER || args->target == PIPE_TEXTURE_RECT)
         return -1;

      if (args->last_level > (floor(log2(MAX2(args->width, args->height))) + 1))
         return -1;
   }

   if (args->flags != 0 && args->flags != VIRGL_RESOURCE_Y_0_TOP)
      return -1;

   if (args->flags & VIRGL_RESOURCE_Y_0_TOP) {
      if (args->target != PIPE_TEXTURE_2D && args->target != PIPE_TEXTURE_RECT)
         return -1;
   }

   /* array size for array textures only */
   if (args->target == PIPE_TEXTURE_CUBE) {
      if (args->array_size != 6)
         return -1;
   } else if (args->target == PIPE_TEXTURE_CUBE_ARRAY) {
      if (!has_feature(feat_cube_map_array))
         return -1;
      if (args->array_size % 6)
         return -1;
   } else if (args->array_size > 1) {
      if (args->target != PIPE_TEXTURE_2D_ARRAY &&
          args->target != PIPE_TEXTURE_1D_ARRAY)
         return -1;

      if (!has_feature(feat_texture_array))
         return -1;
   }
   if (format_can_texture_storage && !args->width) {
      return -1;
   }

   if (args->bind == 0 ||
       args->bind == VIRGL_BIND_CUSTOM ||
       args->bind == VIRGL_BIND_STAGING ||
       args->bind == VIRGL_BIND_INDEX_BUFFER ||
       args->bind == VIRGL_BIND_STREAM_OUTPUT ||
       args->bind == VIRGL_BIND_VERTEX_BUFFER ||
       args->bind == VIRGL_BIND_CONSTANT_BUFFER ||
       args->bind == VIRGL_BIND_QUERY_BUFFER ||
       args->bind == VIRGL_BIND_COMMAND_ARGS ||
       args->bind == VIRGL_BIND_SHADER_BUFFER) {
      if (args->target != PIPE_BUFFER)
         return -1;
      if (args->height != 1 || args->depth != 1)
         return -1;
      if (args->bind == VIRGL_BIND_QUERY_BUFFER && !has_feature(feat_qbo))
         return -1;
      if (args->bind == VIRGL_BIND_COMMAND_ARGS && !has_feature(feat_indirect_draw))
         return -1;
   } else {
      if (!((args->bind & VIRGL_BIND_SAMPLER_VIEW) ||
            (args->bind & VIRGL_BIND_DEPTH_STENCIL) ||
            (args->bind & VIRGL_BIND_RENDER_TARGET) ||
            (args->bind & VIRGL_BIND_CURSOR) ||
            (args->bind & VIRGL_BIND_SHARED) ||
            (args->bind & VIRGL_BIND_LINEAR))) {
         return -1;
      }

      if (args->target == PIPE_TEXTURE_2D ||
          args->target == PIPE_TEXTURE_RECT ||
          args->target == PIPE_TEXTURE_CUBE ||
          args->target == PIPE_TEXTURE_2D_ARRAY ||
          args->target == PIPE_TEXTURE_CUBE_ARRAY) {
         if (args->depth != 1)
            return -1;
         if (format_can_texture_storage && !args->height) {
            return -1;
         }
      }
      if (args->target == PIPE_TEXTURE_1D ||
          args->target == PIPE_TEXTURE_1D_ARRAY) {
         if (args->height != 1 || args->depth != 1)
            return -1;
         if (args->width > vrend_state.max_texture_2d_size) {
            return -1;
         }
      }

      if (args->target == PIPE_TEXTURE_2D ||
          args->target == PIPE_TEXTURE_RECT ||
          args->target == PIPE_TEXTURE_2D_ARRAY) {
         if (args->width > vrend_state.max_texture_2d_size ||
             args->height > vrend_state.max_texture_2d_size) {
            return -1;
         }
      }

      if (args->target == PIPE_TEXTURE_3D) {
         if (format_can_texture_storage &&
             (!args->height || !args->depth)) {
            return -1;
         }
         if (args->width > vrend_state.max_texture_3d_size ||
             args->height > vrend_state.max_texture_3d_size ||
             args->depth > vrend_state.max_texture_3d_size) {
            return -1;
         }
      }
      if (args->target == PIPE_TEXTURE_2D_ARRAY ||
          args->target == PIPE_TEXTURE_CUBE_ARRAY ||
          args->target == PIPE_TEXTURE_1D_ARRAY) {
         if (format_can_texture_storage &&
             !args->array_size) {
            return -1;
         }
      }
      if (args->target == PIPE_TEXTURE_CUBE ||
          args->target == PIPE_TEXTURE_CUBE_ARRAY) {
         if (args->width != args->height) {
            return -1;
         }
         if (args->width > vrend_state.max_texture_cube_size) {
            return -1;
         }
      }
   }
   return 0;
}


int vrend_renderer_resource_create(struct vrend_renderer_resource_create_args *args,
                                   struct iovec *iov, uint32_t num_iovs, void *image_oes)
{
   struct vrend_resource *gr;
   int ret;
   char error_string[256];

   ret = check_resource_valid(args, error_string);
   if (ret) {
      vrend_printf("%s, Illegal resource parameters, error: %s\n", __func__, error_string);
      return EINVAL;
   }

   gr = (struct vrend_resource *)CALLOC_STRUCT(vrend_texture);
   if (!gr)
      return ENOMEM;

   vrend_renderer_resource_copy_args(args, gr);
   gr->iov = iov;
   gr->num_iovs = num_iovs;

   if (args->flags & VIRGL_RESOURCE_Y_0_TOP)
      gr->y_0_top = true;

   pipe_reference_init(&gr->base.reference, 1);

   if (args->bind == VIRGL_BIND_CUSTOM) {
      assert(args->target == PIPE_BUFFER);
      /* use iovec directly when attached */
      gr->storage = VREND_RESOURCE_STORAGE_GUEST_ELSE_SYSTEM;
      gr->ptr = malloc(args->width);
      if (!gr->ptr) {
         FREE(gr);
         return ENOMEM;
      }
   } else if (args->bind == VIRGL_BIND_STAGING) {
      /* Staging buffers use only guest memory. */
      gr->storage = VREND_RESOURCE_STORAGE_GUEST;
   } else if (args->bind == VIRGL_BIND_INDEX_BUFFER) {
      gr->target = GL_ELEMENT_ARRAY_BUFFER_ARB;
      vrend_create_buffer(gr, args->width);
   } else if (args->bind == VIRGL_BIND_STREAM_OUTPUT) {
      gr->target = GL_TRANSFORM_FEEDBACK_BUFFER;
      vrend_create_buffer(gr, args->width);
   } else if (args->bind == VIRGL_BIND_VERTEX_BUFFER) {
      gr->target = GL_ARRAY_BUFFER_ARB;
      vrend_create_buffer(gr, args->width);
   } else if (args->bind == VIRGL_BIND_CONSTANT_BUFFER) {
      gr->target = GL_UNIFORM_BUFFER;
      vrend_create_buffer(gr, args->width);
   } else if (args->bind == VIRGL_BIND_QUERY_BUFFER) {
      gr->target = GL_QUERY_BUFFER;
      vrend_create_buffer(gr, args->width);
   } else if (args->bind == VIRGL_BIND_COMMAND_ARGS) {
      gr->target = GL_DRAW_INDIRECT_BUFFER;
      vrend_create_buffer(gr, args->width);
   } else if (args->target == PIPE_BUFFER && (args->bind == 0 || args->bind == VIRGL_BIND_SHADER_BUFFER)) {
      gr->target = GL_ARRAY_BUFFER_ARB;
      vrend_create_buffer(gr, args->width);
   } else if (args->target == PIPE_BUFFER && (args->bind & VIRGL_BIND_SAMPLER_VIEW)) {
      /*
       * On Desktop we use GL_ARB_texture_buffer_object on GLES we use
       * GL_EXT_texture_buffer (it is in the ANDRIOD extension pack).
       */
#if GL_TEXTURE_BUFFER != GL_TEXTURE_BUFFER_EXT
#error "GL_TEXTURE_BUFFER enums differ, they shouldn't."
#endif

      /* need to check GL version here */
      if (has_feature(feat_arb_or_gles_ext_texture_buffer)) {
         gr->target = GL_TEXTURE_BUFFER;
      } else {
         gr->target = GL_PIXEL_PACK_BUFFER_ARB;
      }
      vrend_create_buffer(gr, args->width);
   } else {
      int r = vrend_renderer_resource_allocate_texture(gr, image_oes);
      if (r) {
         FREE(gr);
         return r;
      }
   }

   ret = vrend_resource_insert(gr, args->handle);
   if (ret == 0) {
      vrend_renderer_resource_destroy(gr);
      return ENOMEM;
   }
   return 0;
}


lookup_fullname(const char *find)
{
    int state = -1;

    for (;;) {
	int count = 0;
	NCURSES_CONST char *const *names;

	switch (++state) {
	case BOOLEAN:
	    names = boolfnames;
	    break;
	case STRING:
	    names = strfnames;
	    break;
	case NUMBER:
	    names = numfnames;
	    break;
	default:
	    return NOTFOUND;
	}

	for (count = 0; names[count] != 0; count++) {
	    if (!strcmp(names[count], find)) {
		struct name_table_entry const *entry_ptr = _nc_get_table(FALSE);
		while (entry_ptr->nte_type != state
		       || entry_ptr->nte_index != count)
		    entry_ptr++;
		return entry_ptr;
	    }
	}
    }
}


set_obsolete_termcaps(TERMTYPE2 *tp)
{
#include "capdefaults.c"
}


_nc_leaks_dump_entry(void)
{
    free_DYN(&outbuf);
    free_DYN(&tmpbuf);
}


fmt_entry(TERMTYPE2 *tterm,
	  PredFunc pred,
	  int content_only,
	  int suppress_untranslatable,
	  int infodump,
	  int numbers)
{
    PredIdx i, j;
    char buffer[MAX_TERMINFO_LENGTH + EXTRA_CAP];
    char *capability;
    NCURSES_CONST char *name;
    int predval, len;
    PredIdx num_bools = 0;
    PredIdx num_values = 0;
    PredIdx num_strings = 0;
    bool outcount = 0;

#define WRAP_CONCAT1(s)		wrap_concat1(s); outcount = TRUE
#define WRAP_CONCAT		WRAP_CONCAT1(buffer)

    len = 12;			/* terminfo file-header */

    if (pred == 0) {
	cur_type = tterm;
	pred = dump_predicate;
    }

    strcpy_DYN(&outbuf, 0);
    if (content_only) {
	column = indent;	/* FIXME: workaround to prevent empty lines */
    } else {
	strcpy_DYN(&outbuf, tterm->term_names);

	/*
	 * Colon is legal in terminfo descriptions, but not in termcap.
	 */
	if (!infodump) {
	    char *p = outbuf.text;
	    while (*p) {
		if (*p == ':') {
		    *p = '=';
		}
		++p;
	    }
	}
	strcpy_DYN(&outbuf, separator);
	column = (int) outbuf.used;
	if (height > 1)
	    force_wrap();
    }

    for_each_boolean(j, tterm) {
	i = BoolIndirect(j);
	name = ExtBoolname(tterm, (int) i, bool_names);
	assert(strlen(name) < sizeof(buffer) - EXTRA_CAP);

	if (!version_filter(BOOLEAN, i))
	    continue;
	else if (isObsolete(outform, name))
	    continue;

	predval = pred(BOOLEAN, i);
	if (predval != FAIL) {
	    _nc_STRCPY(buffer, name, sizeof(buffer));
	    if (predval <= 0)
		_nc_STRCAT(buffer, "@", sizeof(buffer));
	    else if (i + 1 > num_bools)
		num_bools = i + 1;
	    WRAP_CONCAT;
	}
    }

    if (column != indent && height > 1)
	force_wrap();

    for_each_number(j, tterm) {
	i = NumIndirect(j);
	name = ExtNumname(tterm, (int) i, num_names);
	assert(strlen(name) < sizeof(buffer) - EXTRA_CAP);

	if (!version_filter(NUMBER, i))
	    continue;
	else if (isObsolete(outform, name))
	    continue;

	predval = pred(NUMBER, i);
	if (predval != FAIL) {
	    if (tterm->Numbers[i] < 0) {
		_nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer))
			    "%s@", name);
	    } else {
		size_t nn;
		_nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer))
			    "%s#", name);
		nn = strlen(buffer);
		_nc_SPRINTF(buffer + nn, _nc_SLIMIT(sizeof(buffer) - nn)
			    number_format(tterm->Numbers[i]),
			    tterm->Numbers[i]);
		if (i + 1 > num_values)
		    num_values = i + 1;
	    }
	    WRAP_CONCAT;
	}
    }

    if (column != indent && height > 1)
	force_wrap();

    len += (int) (num_bools
		  + num_values * 2
		  + strlen(tterm->term_names) + 1);
    if (len & 1)
	len++;

#undef CUR
#define CUR tterm->
    if (outform == F_TERMCAP) {
	if (VALID_STRING(termcap_reset)) {
	    if (VALID_STRING(init_3string)
		&& !strcmp(init_3string, termcap_reset))
		DISCARD(init_3string);

	    if (VALID_STRING(reset_2string)
		&& !strcmp(reset_2string, termcap_reset))
		DISCARD(reset_2string);
	}
    }

    for_each_string(j, tterm) {
	i = StrIndirect(j);
	name = ExtStrname(tterm, (int) i, str_names);
	assert(strlen(name) < sizeof(buffer) - EXTRA_CAP);

	capability = tterm->Strings[i];

	if (!version_filter(STRING, i))
	    continue;
	else if (isObsolete(outform, name))
	    continue;

#if NCURSES_XNAMES
	/*
	 * Extended names can be longer than 2 characters, but termcap programs
	 * cannot read those (filter them out).
	 */
	if (outform == F_TERMCAP && (strlen(name) > 2))
	    continue;
#endif

	if (outform == F_TERMCAP) {
	    /*
	     * Some older versions of vi want rmir/smir to be defined
	     * for ich/ich1 to work.  If they're not defined, force
	     * them to be output as defined and empty.
	     */
	    if (PRESENT(insert_character) || PRESENT(parm_ich)) {
		if (SAME_CAP(i, enter_insert_mode)
		    && enter_insert_mode == ABSENT_STRING) {
		    _nc_STRCPY(buffer, "im=", sizeof(buffer));
		    WRAP_CONCAT;
		    continue;
		}

		if (SAME_CAP(i, exit_insert_mode)
		    && exit_insert_mode == ABSENT_STRING) {
		    _nc_STRCPY(buffer, "ei=", sizeof(buffer));
		    WRAP_CONCAT;
		    continue;
		}
	    }
	    /*
	     * termcap applications such as screen will be confused if sgr0
	     * is translated to a string containing rmacs.  Filter that out.
	     */
	    if (PRESENT(exit_attribute_mode)) {
		if (SAME_CAP(i, exit_attribute_mode)) {
		    char *trimmed_sgr0;
		    char *my_sgr = set_attributes;

		    set_attributes = save_sgr;

		    trimmed_sgr0 = _nc_trim_sgr0(tterm);
		    if (strcmp(capability, trimmed_sgr0)) {
			capability = trimmed_sgr0;
		    } else {
			if (trimmed_sgr0 != exit_attribute_mode)
			    free(trimmed_sgr0);
		    }

		    set_attributes = my_sgr;
		}
	    }
	}

	predval = pred(STRING, i);
	buffer[0] = '\0';

	if (predval != FAIL) {
	    if (VALID_STRING(capability)
		&& i + 1 > num_strings)
		num_strings = i + 1;

	    if (!VALID_STRING(capability)) {
		_nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer))
			    "%s@", name);
		WRAP_CONCAT;
	    } else if (TcOutput()) {
		char *srccap = _nc_tic_expand(capability, TRUE, numbers);
		int params = ((i < (int) SIZEOF(parametrized))
			      ? parametrized[i]
			      : ((*srccap == 'k')
				 ? 0
				 : has_params(srccap)));
		char *cv = _nc_infotocap(name, srccap, params);

		if (cv == 0) {
		    if (outform == F_TCONVERR) {
			_nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer))
				    "%s=!!! %s WILL NOT CONVERT !!!",
				    name, srccap);
			WRAP_CONCAT;
		    } else if (suppress_untranslatable) {
			continue;
		    } else {
			char *s = srccap, *d = buffer;
			int need = 3 + (int) strlen(name);
			while ((*d = *s++) != 0) {
			    if ((d - buffer + 1) >= (int) sizeof(buffer)) {
				fprintf(stderr,
					"%s: value for %s is too long\n",
					_nc_progname,
					name);
				*d = '\0';
				break;
			    }
			    if (*d == ':') {
				*d++ = '\\';
				*d = ':';
			    } else if (*d == '\\') {
				if ((*++d = *s++) == '\0')
				    break;
			    }
			    d++;
			    *d = '\0';
			}
			need += (int) (d - buffer);
			wrap_concat("..", need, w1ST | wERR);
			need -= 2;
			wrap_concat(name, need, wOFF | wERR);
			need -= (int) strlen(name);
			wrap_concat("=", need, w2ND | wERR);
			need -= 1;
			wrap_concat(buffer, need, wEND | wERR);
			outcount = TRUE;
		    }
		} else {
		    wrap_concat3(name, "=", cv);
		}
		len += (int) strlen(capability) + 1;
	    } else {
		char *src = _nc_tic_expand(capability,
					   outform == F_TERMINFO, numbers);

		strcpy_DYN(&tmpbuf, 0);
		strcpy_DYN(&tmpbuf, name);
		strcpy_DYN(&tmpbuf, "=");
		if (pretty
		    && (outform == F_TERMINFO
			|| outform == F_VARIABLE)) {
		    fmt_complex(tterm, name, src, 1);
		} else {
		    strcpy_DYN(&tmpbuf, src);
		}
		len += (int) strlen(capability) + 1;
		WRAP_CONCAT1(tmpbuf.text);
	    }
	}
	/* e.g., trimmed_sgr0 */
	if (VALID_STRING(capability) &&
	    capability != tterm->Strings[i])
	    free(capability);
    }
    len += (int) (num_strings * 2);

    /*
     * This piece of code should be an effective inverse of the functions
     * postprocess_terminfo() and postprocess_terminfo() in parse_entry.c.
     * Much more work should be done on this to support dumping termcaps.
     */
    if (tversion == V_HPUX) {
	if (VALID_STRING(memory_lock)) {
	    _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer))
			"meml=%s", memory_lock);
	    WRAP_CONCAT;
	}
	if (VALID_STRING(memory_unlock)) {
	    _nc_SPRINTF(buffer, _nc_SLIMIT(sizeof(buffer))
			"memu=%s", memory_unlock);
	    WRAP_CONCAT;
	}
    } else if (tversion == V_AIX) {
	if (VALID_STRING(acs_chars)) {
	    bool box_ok = TRUE;
	    const char *acstrans = "lqkxjmwuvtn";
	    const char *cp;
	    char *tp, *sp, boxchars[11];

	    tp = boxchars;
	    for (cp = acstrans; *cp; cp++) {
		sp = (strchr) (acs_chars, *cp);
		if (sp)
		    *tp++ = sp[1];
		else {
		    box_ok = FALSE;
		    break;
		}
	    }
	    tp[0] = '\0';

	    if (box_ok) {
		char *tmp = _nc_tic_expand(boxchars,
					   (outform == F_TERMINFO),
					   numbers);
		_nc_STRCPY(buffer, "box1=", sizeof(buffer));
		while (*tmp != '\0') {
		    size_t have = strlen(buffer);
		    size_t next = strlen(tmp);
		    size_t want = have + next + 1;
		    size_t last = next;
		    char save = '\0';

		    /*
		     * If the expanded string is too long for the buffer,
		     * chop it off and save the location where we chopped it.
		     */
		    if (want >= sizeof(buffer)) {
			save = tmp[last];
			tmp[last] = '\0';
		    }
		    _nc_STRCAT(buffer, tmp, sizeof(buffer));

		    /*
		     * If we chopped the buffer, replace the missing piece and
		     * shift everything to append the remainder.
		     */
		    if (save != '\0') {
			next = 0;
			tmp[last] = save;
			while ((tmp[next] = tmp[last + next]) != '\0') {
			    ++next;
			}
		    } else {
			break;
		    }
		}
		WRAP_CONCAT;
	    }
	}
    }

    /*
     * kludge: trim off trailer to avoid an extra blank line
     * in infocmp -u output when there are no string differences
     */
    if (outcount) {
	bool trimmed = FALSE;
	j = (PredIdx) outbuf.used;
	if (wrapped && did_wrap) {
	    /* EMPTY */ ;
	} else if (j >= 2
		   && outbuf.text[j - 1] == '\t'
		   && outbuf.text[j - 2] == '\n') {
	    outbuf.used -= 2;
	    trimmed = TRUE;
	} else if (j >= 4
		   && outbuf.text[j - 1] == ':'
		   && outbuf.text[j - 2] == '\t'
		   && outbuf.text[j - 3] == '\n'
		   && outbuf.text[j - 4] == '\\') {
	    outbuf.used -= 4;
	    trimmed = TRUE;
	}
	if (trimmed) {
	    outbuf.text[outbuf.used] = '\0';
	    column = oldcol;
	    strcpy_DYN(&outbuf, " ");
	}
    }
#if 0
    fprintf(stderr, "num_bools = %d\n", num_bools);
    fprintf(stderr, "num_values = %d\n", num_values);
    fprintf(stderr, "num_strings = %d\n", num_strings);
    fprintf(stderr, "term_names=%s, len=%d, strlen(outbuf)=%d, outbuf=%s\n",
	    tterm->term_names, len, outbuf.used, outbuf.text);
#endif
    /*
     * Here's where we use infodump to trigger a more stringent length check
     * for termcap-translation purposes.
     * Return the length of the raw entry, without tc= expansions,
     * It gives an idea of which entries are deadly to even *scan past*,
     * as opposed to *use*.
     */
    return (infodump ? len : (int) termcap_length(outbuf.text));
}


_nc_find_type_entry(const char *string,
		    int type,
		    bool termcap)
{
    struct name_table_entry const *ptr = NULL;
    const HashData *data = _nc_get_hash_info(termcap);
    int hashvalue = data->hash_of(string);

    if (hashvalue >= 0
	&& (unsigned) hashvalue < data->table_size
	&& data->table_data[hashvalue] >= 0) {
	const struct name_table_entry *const table = _nc_get_table(termcap);

	ptr = table + data->table_data[hashvalue];
	while (ptr->nte_type != type
	       || !data->compare_names(ptr->nte_name, string)) {
	    if (ptr->nte_link < 0) {
		ptr = 0;
		break;
	    }
	    ptr = table + (ptr->nte_link + data->table_data[data->table_size]);
	}
    }

    return ptr;
}


append_acs0(string_desc * dst, int code, char *src, size_t off)
{
    if (src != 0 && off < strlen(src)) {
	char temp[3];
	temp[0] = (char) code;
	temp[1] = src[off];
	temp[2] = 0;
	_nc_safe_strcat(dst, temp);
    }
}


g_utf8_strchug (char *string)
{
	char     *scan;
	gunichar  c;

	g_return_val_if_fail (string != NULL, NULL);

	scan = string;
	c = g_utf8_get_char (scan);
	while (g_unichar_isspace (c)) {
		scan = g_utf8_next_char (scan);
		c = g_utf8_get_char (scan);
	}

	memmove (string, scan, strlen (scan) + 1);

	return string;
}


char *mutt_gecos_name (char *dest, size_t destlen, struct passwd *pw)
{
  regmatch_t pat_match[1];
  size_t pwnl;
  int idx;
  char *p;

  if (!pw || !pw->pw_gecos)
    return NULL;

  memset (dest, 0, destlen);

  if (GecosMask.rx)
  {
    if (regexec (GecosMask.rx, pw->pw_gecos, 1, pat_match, 0) == 0)
      strfcpy (dest, pw->pw_gecos + pat_match[0].rm_so,
	       MIN (pat_match[0].rm_eo - pat_match[0].rm_so + 1, destlen));
  }
  else if ((p = strchr (pw->pw_gecos, ',')))
    strfcpy (dest, pw->pw_gecos, MIN (destlen, p - pw->pw_gecos + 1));
  else
    strfcpy (dest, pw->pw_gecos, destlen);

  pwnl = strlen (pw->pw_name);

  for (idx = 0; dest[idx]; idx++)
  {
    if (dest[idx] == '&')
    {
      memmove (&dest[idx + pwnl], &dest[idx + 1],
	       MAX((ssize_t)(destlen - idx - pwnl - 1), 0));
      memcpy (&dest[idx], pw->pw_name, MIN(destlen - idx - 1, pwnl));
      dest[idx] = toupper ((unsigned char) dest[idx]);
    }
  }

  return dest;
}


static void toggle_quadoption (int opt)
{
  int n = opt/4;
  int b = (opt % 4) * 2;

  QuadOptions[n] ^= (1 << b);
}


static const char* myvar_get (const char* var)
{
  myvar_t* cur;

  for (cur = MyVars; cur; cur = cur->next)
    if (!mutt_strcmp (cur->name, var))
      return NONULL(cur->value);

  return NULL;
}


int mutt_match_spam_list (const char *s, SPAM_LIST *l, char *text, int textsize)
{
  static regmatch_t *pmatch = NULL;
  static int nmatch = 0;
  int tlen = 0;
  char *p;

  if (!s) return 0;

  for (; l; l = l->next)
  {
    /* If this pattern needs more matches, expand pmatch. */
    if (l->nmatch > nmatch)
    {
      safe_realloc (&pmatch, l->nmatch * sizeof(regmatch_t));
      nmatch = l->nmatch;
    }

    /* Does this pattern match? */
    if (regexec (l->rx->rx, s, (size_t) l->nmatch, (regmatch_t *) pmatch, (int) 0) == 0)
    {
      dprint (5, (debugfile, "mutt_match_spam_list: %s matches %s\n", s, l->rx->pattern));
      dprint (5, (debugfile, "mutt_match_spam_list: %d subs\n", (int)l->rx->rx->re_nsub));

      /* Copy template into text, with substitutions. */
      for (p = l->template; *p && tlen < textsize - 1;)
      {
	/* backreference to pattern match substring, eg. %1, %2, etc) */
	if (*p == '%')
	{
	  char *e; /* used as pointer to end of integer backreference in strtol() call */
	  int n;

	  ++p; /* skip over % char */
	  n = strtol(p, &e, 10);
	  /* Ensure that the integer conversion succeeded (e!=p) and bounds check.  The upper bound check
	   * should not strictly be necessary since add_to_spam_list() finds the largest value, and
	   * the static array above is always large enough based on that value. */
	  if (e != p && n >= 0 && n <= l->nmatch && pmatch[n].rm_so != -1) {
	    /* copy as much of the substring match as will fit in the output buffer, saving space for
	     * the terminating nul char */
	    int idx;
	    for (idx = pmatch[n].rm_so; (idx < pmatch[n].rm_eo) && (tlen < textsize - 1); ++idx)
	      text[tlen++] = s[idx];
	  }
	  p = e; /* skip over the parsed integer */
	}
	else
	{
	  text[tlen++] = *p++;
	}
      }
      /* tlen should always be less than textsize except when textsize<=0
       * because the bounds checks in the above code leave room for the
       * terminal nul char.   This should avoid returning an unterminated
       * string to the caller.  When textsize<=0 we make no assumption about
       * the validity of the text pointer. */
      if (tlen < textsize) {
	text[tlen] = '\0';
	dprint (5, (debugfile, "mutt_match_spam_list: \"%s\"\n", text));
      }
      return 1;
    }
  }

  return 0;
}


int query_quadoption (int opt, const char *prompt)
{
  int v = quadoption (opt);

  switch (v)
  {
    case M_YES:
    case M_NO:
      return (v);

    default:
      v = mutt_yesorno (prompt, (v == M_ASKYES));
      CLEARLINE (LINES - 1);
      return (v);
  }

  /* not reached */
}


int mutt_parse_rc_line (/* const */ char *line, BUFFER *token, BUFFER *err)
{
  int i, r = -1;
  BUFFER expn;

  if (!line || !*line)
    return 0;

  memset (&expn, 0, sizeof (expn));
  expn.data = expn.dptr = line;
  expn.dsize = mutt_strlen (line);

  *err->data = 0;

  SKIPWS (expn.dptr);
  while (*expn.dptr)
  {
    if (*expn.dptr == '#')
      break; /* rest of line is a comment */
    if (*expn.dptr == ';')
    {
      expn.dptr++;
      continue;
    }
    mutt_extract_token (token, &expn, 0);
    for (i = 0; Commands[i].name; i++)
    {
      if (!mutt_strcmp (token->data, Commands[i].name))
      {
	if (Commands[i].func (token, &expn, Commands[i].data, err) != 0)
	  goto finish;
        break;
      }
    }
    if (!Commands[i].name)
    {
      snprintf (err->data, err->dsize, _("%s: unknown command"), NONULL (token->data));
      goto finish;
    }
  }
  r = 0;
finish:
  if (expn.destroy)
    FREE (&expn.data);
  return (r);
}


void _mutt_mktemp (char *s, size_t slen, const char *src, int line)
{
  size_t n = snprintf (s, slen, "%s/mutt-%s-%d-%d-%ld%ld", NONULL(Tempdir), NONULL(Hostname),
      (int) getuid(), (int) getpid(), random(), random());
  if (n >= slen)
    dprint(1, (debugfile, "%s:%d: ERROR: insufficient buffer space to hold temporary filename! slen=%zu but need %zu\n",
	  src, line, slen, n));
  dprint (3, (debugfile, "%s:%d: mutt_mktemp returns \"%s\".\n", src, line, s));
  if (unlink (s))
    dprint(1, (debugfile, "%s:%d: ERROR: unable to unlink temporary file\n", src, line));
}


FILE *mutt_open_read (const char *path, pid_t *thepid)
{
  FILE *f;
  struct stat s;

  int len = mutt_strlen (path);

  if (path[len - 1] == '|')
  {
    /* read from a pipe */

    char *s = safe_strdup (path);

    s[len - 1] = 0;
    mutt_endwin (NULL);
    *thepid = mutt_create_filter (s, NULL, &f, NULL);
    FREE (&s);
  }
  else
  {
    if (stat (path, &s) < 0)
      return (NULL);
    if (S_ISDIR (s.st_mode))
    {
      errno = EINVAL;
      return (NULL);
    }
    f = fopen (path, "r");
    *thepid = -1;
  }
  return (f);
}


static int parse_unalternates (BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
{
  _alternates_clean();
  do
  {
    mutt_extract_token (buf, s, 0);
    mutt_remove_from_rx_list (&Alternates, buf->data);

    if (mutt_strcmp (buf->data, "*") &&
	mutt_add_to_rx_list (&UnAlternates, buf->data, REG_ICASE, err) != 0)
      return -1;

  }
  while (MoreArgs (s));

  return 0;
}


BUFFER * mutt_buffer_init(BUFFER *b)
{
  if (!b)
  {
    b = safe_malloc(sizeof(BUFFER));
    if (!b)
      return NULL;
  }
  else
  {
    FREE(&b->data);
  }
  memset(b, 0, sizeof(BUFFER));
  return b;
}


static int parse_attach_list (BUFFER *buf, BUFFER *s, LIST **ldata, BUFFER *err)
{
  ATTACH_MATCH *a;
  LIST *listp, *lastp;
  char *p;
  char *tmpminor;
  int len;
  int ret;

  /* Find the last item in the list that data points to. */
  lastp = NULL;
  dprint(5, (debugfile, "parse_attach_list: ldata = %p, *ldata = %p\n",
	      (void *)ldata, (void *)*ldata));
  for (listp = *ldata; listp; listp = listp->next)
  {
    a = (ATTACH_MATCH *)listp->data;
    dprint(5, (debugfile, "parse_attach_list: skipping %s/%s\n",
		a->major, a->minor));
    lastp = listp;
  }

  do
  {
    mutt_extract_token (buf, s, 0);

    if (!buf->data || *buf->data == '\0')
      continue;

    a = safe_malloc(sizeof(ATTACH_MATCH));

    /* some cheap hacks that I expect to remove */
    if (!ascii_strcasecmp(buf->data, "any"))
      a->major = safe_strdup("*/.*");
    else if (!ascii_strcasecmp(buf->data, "none"))
      a->major = safe_strdup("cheap_hack/this_should_never_match");
    else
      a->major = safe_strdup(buf->data);

    if ((p = strchr(a->major, '/')))
    {
      *p = '\0';
      ++p;
      a->minor = p;
    }
    else
    {
      a->minor = "unknown";
    }

    len = strlen(a->minor);
    tmpminor = safe_malloc(len+3);
    strcpy(&tmpminor[1], a->minor); /* __STRCPY_CHECKED__ */
    tmpminor[0] = '^';
    tmpminor[len+1] = '$';
    tmpminor[len+2] = '\0';

    a->major_int = mutt_check_mime_type(a->major);
    ret = REGCOMP(&a->minor_rx, tmpminor, 0);

    FREE(&tmpminor);

    if (ret)
    {
      regerror(ret, &a->minor_rx, err->data, err->dsize);
      regfree(&a->minor_rx);
      FREE(&a->major);
      FREE(&a);
      return -1;
    }

    dprint(5, (debugfile, "parse_attach_list: added %s/%s [%d]\n",
		a->major, a->minor, a->major_int));

    listp = safe_malloc(sizeof(LIST));
    listp->data = (char *)a;
    listp->next = NULL;
    if (lastp)
    {
      lastp->next = listp;
    }
    else
    {
      *ldata = listp;
    }
    lastp = listp;
  }
  while (MoreArgs (s));

  _attachments_clean();
  return 0;
}


char *mutt_expand_path (char *s, size_t slen)
{
  return _mutt_expand_path (s, slen, 0);
}


int mutt_command_complete (char *buffer, size_t len, int pos, int numtabs)
{
  char *pt = buffer;
  int num;
  int spaces; /* keep track of the number of leading spaces on the line */
  myvar_t *myv;

  SKIPWS (buffer);
  spaces = buffer - pt;

  pt = buffer + pos - spaces;
  while ((pt > buffer) && !isspace ((unsigned char) *pt))
    pt--;

  if (pt == buffer) /* complete cmd */
  {
    /* first TAB. Collect all the matches */
    if (numtabs == 1)
    {
      Num_matched = 0;
      strfcpy (User_typed, pt, sizeof (User_typed));
      memset (Matches, 0, Matches_listsize);
      memset (Completed, 0, sizeof (Completed));
      for (num = 0; Commands[num].name; num++)
	candidate (Completed, User_typed, Commands[num].name, sizeof (Completed));
      matches_ensure_morespace (Num_matched);
      Matches[Num_matched++] = User_typed;

      /* All matches are stored. Longest non-ambiguous string is ""
       * i.e. dont change 'buffer'. Fake successful return this time */
      if (User_typed[0] == 0)
	return 1;
    }

    if (Completed[0] == 0 && User_typed[0])
      return 0;

     /* Num_matched will _always_ be atleast 1 since the initial
      * user-typed string is always stored */
    if (numtabs == 1 && Num_matched == 2)
      snprintf(Completed, sizeof(Completed),"%s", Matches[0]);
    else if (numtabs > 1 && Num_matched > 2)
      /* cycle thru all the matches */
      snprintf(Completed, sizeof(Completed), "%s",
	       Matches[(numtabs - 2) % Num_matched]);

    /* return the completed command */
    strncpy (buffer, Completed, len - spaces);
  }
  else if (!mutt_strncmp (buffer, "set", 3)
	   || !mutt_strncmp (buffer, "unset", 5)
	   || !mutt_strncmp (buffer, "reset", 5)
	   || !mutt_strncmp (buffer, "toggle", 6))
  { 		/* complete variables */
    char *prefixes[] = { "no", "inv", "?", "&", 0 };

    pt++;
    /* loop through all the possible prefixes (no, inv, ...) */
    if (!mutt_strncmp (buffer, "set", 3))
    {
      for (num = 0; prefixes[num]; num++)
      {
	if (!mutt_strncmp (pt, prefixes[num], mutt_strlen (prefixes[num])))
	{
	  pt += mutt_strlen (prefixes[num]);
	  break;
	}
      }
    }

    /* first TAB. Collect all the matches */
    if (numtabs == 1)
    {
      Num_matched = 0;
      strfcpy (User_typed, pt, sizeof (User_typed));
      memset (Matches, 0, Matches_listsize);
      memset (Completed, 0, sizeof (Completed));
      for (num = 0; MuttVars[num].option; num++)
	candidate (Completed, User_typed, MuttVars[num].option, sizeof (Completed));
      for (myv = MyVars; myv; myv = myv->next)
	candidate (Completed, User_typed, myv->name, sizeof (Completed));
      matches_ensure_morespace (Num_matched);
      Matches[Num_matched++] = User_typed;

      /* All matches are stored. Longest non-ambiguous string is ""
       * i.e. dont change 'buffer'. Fake successful return this time */
      if (User_typed[0] == 0)
	return 1;
    }

    if (Completed[0] == 0 && User_typed[0])
      return 0;

    /* Num_matched will _always_ be atleast 1 since the initial
     * user-typed string is always stored */
    if (numtabs == 1 && Num_matched == 2)
      snprintf(Completed, sizeof(Completed),"%s", Matches[0]);
    else if (numtabs > 1 && Num_matched > 2)
    /* cycle thru all the matches */
      snprintf(Completed, sizeof(Completed), "%s",
	       Matches[(numtabs - 2) % Num_matched]);

    strncpy (pt, Completed, buffer + len - pt - spaces);
  }
  else if (!mutt_strncmp (buffer, "exec", 4))
  {
    struct binding_t *menu = km_get_table (CurrentMenu);

    if (!menu && CurrentMenu != MENU_PAGER)
      menu = OpGeneric;

    pt++;
    /* first TAB. Collect all the matches */
    if (numtabs == 1)
    {
      Num_matched = 0;
      strfcpy (User_typed, pt, sizeof (User_typed));
      memset (Matches, 0, Matches_listsize);
      memset (Completed, 0, sizeof (Completed));
      for (num = 0; menu[num].name; num++)
	candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
      /* try the generic menu */
      if (Completed[0] == 0 && CurrentMenu != MENU_PAGER)
      {
	menu = OpGeneric;
	for (num = 0; menu[num].name; num++)
	  candidate (Completed, User_typed, menu[num].name, sizeof (Completed));
      }
      matches_ensure_morespace (Num_matched);
      Matches[Num_matched++] = User_typed;

      /* All matches are stored. Longest non-ambiguous string is ""
       * i.e. dont change 'buffer'. Fake successful return this time */
      if (User_typed[0] == 0)
	return 1;
    }

    if (Completed[0] == 0 && User_typed[0])
      return 0;

    /* Num_matched will _always_ be atleast 1 since the initial
     * user-typed string is always stored */
    if (numtabs == 1 && Num_matched == 2)
      snprintf(Completed, sizeof(Completed),"%s", Matches[0]);
    else if (numtabs > 1 && Num_matched > 2)
    /* cycle thru all the matches */
      snprintf(Completed, sizeof(Completed), "%s",
	       Matches[(numtabs - 2) % Num_matched]);

    strncpy (pt, Completed, buffer + len - pt - spaces);
  }
  else
    return 0;

  return 1;
}


static int parse_source (BUFFER *tmp, BUFFER *s, unsigned long data, BUFFER *err)
{
  char path[_POSIX_PATH_MAX];

  if (mutt_extract_token (tmp, s, 0) != 0)
  {
    snprintf (err->data, err->dsize, _("source: error at %s"), s->dptr);
    return (-1);
  }
  if (MoreArgs (s))
  {
    strfcpy (err->data, _("source: too many arguments"), err->dsize);
    return (-1);
  }
  strfcpy (path, tmp->data, sizeof (path));
  mutt_expand_path (path, sizeof (path));
  return (source_rc (path, err));
}


void mutt_free_header (HEADER **h)
{
  if(!h || !*h) return;
  mutt_free_envelope (&(*h)->env);
  mutt_free_body (&(*h)->content);
  FREE (&(*h)->maildir_flags);
  FREE (&(*h)->tree);
  FREE (&(*h)->path);
#ifdef MIXMASTER
  mutt_free_list (&(*h)->chain);
#endif
#if defined USE_POP || defined USE_IMAP
  FREE (&(*h)->data);
#endif
  FREE (h);		/* __FREE_CHECKED__ */
}


static int parse_group_context (group_context_t **ctx, BUFFER *buf, BUFFER *s, unsigned long data, BUFFER *err)
{
  while (!mutt_strcasecmp (buf->data, "-group"))
  {
    if (!MoreArgs (s))
    {
      strfcpy (err->data, _("-group: no group name"), err->dsize);
      goto bail;
    }

    mutt_extract_token (buf, s, 0);

    mutt_group_context_add (ctx, mutt_pattern_group (buf->data));

    if (!MoreArgs (s))
    {
      strfcpy (err->data, _("out of arguments"), err->dsize);
      goto bail;
    }

    mutt_extract_token (buf, s, 0);
  }

  return 0;

  bail:
  mutt_group_context_destroy (ctx);
  return -1;
}


static inline void v2_mem2diskdqinfo(struct v2_disk_dqinfo *d,
				     struct util_dqinfo *m)
{
	d->dqi_bgrace = ext2fs_cpu_to_le32(m->dqi_bgrace);
	d->dqi_igrace = ext2fs_cpu_to_le32(m->dqi_igrace);
	d->dqi_flags = ext2fs_cpu_to_le32(m->u.v2_mdqi.dqi_flags & V2_DQF_MASK);
	d->dqi_blocks = ext2fs_cpu_to_le32(m->u.v2_mdqi.dqi_qtree.dqi_blocks);
	d->dqi_free_blk =
		ext2fs_cpu_to_le32(m->u.v2_mdqi.dqi_qtree.dqi_free_blk);
	d->dqi_free_entry =
		ext2fs_cpu_to_le32(m->u.v2_mdqi.dqi_qtree.dqi_free_entry);
}


static void v2r1_disk2memdqblk(struct dquot *dquot, void *dp)
{
	struct util_dqblk *m = &dquot->dq_dqb;
	struct v2r1_disk_dqblk *d = dp, empty;

	dquot->dq_id = ext2fs_le32_to_cpu(d->dqb_id);
	m->dqb_ihardlimit = ext2fs_le64_to_cpu(d->dqb_ihardlimit);
	m->dqb_isoftlimit = ext2fs_le64_to_cpu(d->dqb_isoftlimit);
	m->dqb_bhardlimit = ext2fs_le64_to_cpu(d->dqb_bhardlimit);
	m->dqb_bsoftlimit = ext2fs_le64_to_cpu(d->dqb_bsoftlimit);
	m->dqb_curinodes = ext2fs_le64_to_cpu(d->dqb_curinodes);
	m->dqb_curspace = ext2fs_le64_to_cpu(d->dqb_curspace);
	m->dqb_itime = ext2fs_le64_to_cpu(d->dqb_itime);
	m->dqb_btime = ext2fs_le64_to_cpu(d->dqb_btime);

	memset(&empty, 0, sizeof(struct v2r1_disk_dqblk));
	empty.dqb_itime = ext2fs_cpu_to_le64(1);
	if (!memcmp(&empty, dp, sizeof(struct v2r1_disk_dqblk)))
		m->dqb_itime = 0;
}


static inline void mark_quotafile_info_dirty(struct quota_handle *h)
{
	h->qh_io_flags |= IOFL_INFODIRTY;
}


int quota_file_exists(ext2_filsys fs, enum quota_type qtype)
{
	char qf_name[256];
	errcode_t ret;
	ext2_ino_t ino;

	if (qtype >= MAXQUOTAS)
		return -EINVAL;

	quota_get_qf_name(qtype, QFMT_VFS_V1, qf_name);

	ret = ext2fs_lookup(fs, EXT2_ROOT_INO, qf_name, strlen(qf_name), 0,
			    &ino);
	if (ret)
		return 0;

	return ino;
}


static ext2_loff_t find_tree_dqentry(struct quota_handle *h,
				     struct dquot *dquot,
				     unsigned int blk, int depth)
{
	dqbuf_t buf = getdqbuf();
	ext2_loff_t ret = 0;
	__le32 *ref = (__le32 *) buf;

	if (!buf)
		return -ENOMEM;

	read_blk(h, blk, buf);
	ret = 0;
	blk = ext2fs_le32_to_cpu(ref[get_index(dquot->dq_id, depth)]);
	if (!blk)	/* No reference? */
		goto out_buf;
	if (depth < QT_TREEDEPTH - 1)
		ret = find_tree_dqentry(h, dquot, blk, depth + 1);
	else
		ret = find_block_dqentry(h, dquot, blk);
out_buf:
	freedqbuf(buf);
	return ret;
}


fribidi_log2vis_get_embedding_levels (
  const FriBidiCharType *bidi_types,	/* input list of bidi types as returned by
					   fribidi_get_bidi_types() */
  const FriBidiStrIndex len,	/* input string length of the paragraph */
  FriBidiParType *pbase_dir,	/* requested and resolved paragraph
				 * base direction */
  FriBidiLevel *embedding_levels	/* output list of embedding levels */
)
{
  return fribidi_get_par_embedding_levels_ex (bidi_types, NULL, len, pbase_dir, embedding_levels);
}


bitset_invert_to(BitSetRef from, BitSetRef to)
{
  int i;
  for (i = 0; i < BITSET_SIZE; i++) { to[i] = ~(from[i]); }
}


parse_exp(Node** np, OnigToken* tok, int term,
	  UChar** src, UChar* end, ScanEnv* env)
{
  int r, len, group = 0;
  Node* qn;
  Node** targetp;

  *np = NULL;
  if (tok->type == (enum TokenSyms )term)
    goto end_of_token;

  switch (tok->type) {
  case TK_ALT:
  case TK_EOT:
  end_of_token:
    *np = node_new_empty();
    return tok->type;
    break;

  case TK_SUBEXP_OPEN:
    r = parse_enclose(np, tok, TK_SUBEXP_CLOSE, src, end, env);
    if (r < 0) return r;
    if (r == 1) group = 1;
    else if (r == 2) { /* option only */
      Node* target;
      OnigOptionType prev = env->option;

      env->option = NENCLOSE(*np)->option;
      r = fetch_token(tok, src, end, env);
      if (r < 0) {
	env->option = prev;
	return r;
      }
      r = parse_subexp(&target, tok, term, src, end, env);
      env->option = prev;
      if (r < 0) {
	onig_node_free(target);
	return r;
      }
      NENCLOSE(*np)->target = target;
      return tok->type;
    }
    break;

  case TK_SUBEXP_CLOSE:
    if (! IS_SYNTAX_BV(env->syntax, ONIG_SYN_ALLOW_UNMATCHED_CLOSE_SUBEXP))
      return ONIGERR_UNMATCHED_CLOSE_PARENTHESIS;

    if (tok->escaped) goto tk_raw_byte;
    else goto tk_byte;
    break;

  case TK_LINEBREAK:
    r = node_linebreak(np, env);
    if (r < 0) return r;
    break;

  case TK_EXTENDED_GRAPHEME_CLUSTER:
    r = node_extended_grapheme_cluster(np, env);
    if (r < 0) return r;
    break;

  case TK_KEEP:
    *np = onig_node_new_anchor(ANCHOR_KEEP);
    CHECK_NULL_RETURN_MEMERR(*np);
    break;

  case TK_STRING:
  tk_byte:
    {
      *np = node_new_str(tok->backp, *src);
      CHECK_NULL_RETURN_MEMERR(*np);

    string_loop:
      while (1) {
	r = fetch_token(tok, src, end, env);
	if (r < 0) return r;
	if (r == TK_STRING) {
	  r = onig_node_str_cat(*np, tok->backp, *src);
	}
#ifndef NUMBERED_CHAR_IS_NOT_CASE_AMBIG
	else if (r == TK_CODE_POINT) {
	  r = node_str_cat_codepoint(*np, env->enc, tok->u.code);
	}
#endif
	else {
	  break;
	}
	if (r < 0) return r;
      }

    string_end:
      targetp = np;
      goto repeat;
    }
    break;

  case TK_RAW_BYTE:
  tk_raw_byte:
    {
      *np = node_new_str_raw_char((UChar )tok->u.c);
      CHECK_NULL_RETURN_MEMERR(*np);
      len = 1;
      while (1) {
	if (len >= ONIGENC_MBC_MINLEN(env->enc)) {
	  if (len == enclen(env->enc, NSTR(*np)->s, NSTR(*np)->end)) {
	    r = fetch_token(tok, src, end, env);
	    NSTRING_CLEAR_RAW(*np);
	    goto string_end;
	  }
	}

	r = fetch_token(tok, src, end, env);
	if (r < 0) return r;
	if (r != TK_RAW_BYTE) {
	  /* Don't use this, it is wrong for little endian encodings. */
#ifdef USE_PAD_TO_SHORT_BYTE_CHAR
	  int rem;
	  if (len < ONIGENC_MBC_MINLEN(env->enc)) {
	    rem = ONIGENC_MBC_MINLEN(env->enc) - len;
	    (void )node_str_head_pad(NSTR(*np), rem, (UChar )0);
	    if (len + rem == enclen(env->enc, NSTR(*np)->s)) {
	      NSTRING_CLEAR_RAW(*np);
	      goto string_end;
	    }
	  }
#endif
	  return ONIGERR_TOO_SHORT_MULTI_BYTE_STRING;
	}

	r = node_str_cat_char(*np, (UChar )tok->u.c);
	if (r < 0) return r;

	len++;
      }
    }
    break;

  case TK_CODE_POINT:
    {
      *np = node_new_empty();
      CHECK_NULL_RETURN_MEMERR(*np);
      r = node_str_cat_codepoint(*np, env->enc, tok->u.code);
      if (r != 0) return r;
#ifdef NUMBERED_CHAR_IS_NOT_CASE_AMBIG
      NSTRING_SET_RAW(*np);
#else
      goto string_loop;
#endif
    }
    break;

  case TK_QUOTE_OPEN:
    {
      OnigCodePoint end_op[2];
      UChar *qstart, *qend, *nextp;

      end_op[0] = (OnigCodePoint )MC_ESC(env->syntax);
      end_op[1] = (OnigCodePoint )'E';
      qstart = *src;
      qend = find_str_position(end_op, 2, qstart, end, &nextp, env->enc);
      if (IS_NULL(qend)) {
	nextp = qend = end;
      }
      *np = node_new_str(qstart, qend);
      CHECK_NULL_RETURN_MEMERR(*np);
      *src = nextp;
    }
    break;

  case TK_CHAR_TYPE:
    {
      switch (tok->u.prop.ctype) {
      case ONIGENC_CTYPE_WORD:
	*np = node_new_ctype(tok->u.prop.ctype, tok->u.prop.not,
			     IS_ASCII_RANGE(env->option));
	CHECK_NULL_RETURN_MEMERR(*np);
	break;

      case ONIGENC_CTYPE_SPACE:
      case ONIGENC_CTYPE_DIGIT:
      case ONIGENC_CTYPE_XDIGIT:
	{
	  CClassNode* cc;

	  *np = node_new_cclass();
	  CHECK_NULL_RETURN_MEMERR(*np);
	  cc = NCCLASS(*np);
	  r = add_ctype_to_cc(cc, tok->u.prop.ctype, 0,
	      IS_ASCII_RANGE(env->option), env);
	  if (r != 0) return r;
	  if (tok->u.prop.not != 0) NCCLASS_SET_NOT(cc);
	}
	break;

      default:
	return ONIGERR_PARSER_BUG;
	break;
      }
    }
    break;

  case TK_CHAR_PROPERTY:
    r = parse_char_property(np, tok, src, end, env);
    if (r != 0) return r;
    break;

  case TK_CC_OPEN:
    {
      Node *asc_node;
      CClassNode* cc;
      OnigCodePoint code;

      r = parse_char_class(np, &asc_node, tok, src, end, env);
      if (r != 0) {
	onig_node_free(asc_node);
	return r;
      }

      cc = NCCLASS(*np);
      if (is_onechar_cclass(cc, &code)) {
	onig_node_free(*np);
	onig_node_free(asc_node);
	*np = node_new_empty();
	CHECK_NULL_RETURN_MEMERR(*np);
	r = node_str_cat_codepoint(*np, env->enc, code);
	if (r != 0) return r;
	goto string_loop;
      }
      if (IS_IGNORECASE(env->option)) {
	r = cclass_case_fold(np, cc, NCCLASS(asc_node), env);
	if (r != 0) {
	  onig_node_free(asc_node);
	  return r;
	}
      }
      onig_node_free(asc_node);
    }
    break;

  case TK_ANYCHAR:
    *np = node_new_anychar();
    CHECK_NULL_RETURN_MEMERR(*np);
    break;

  case TK_ANYCHAR_ANYTIME:
    *np = node_new_anychar();
    CHECK_NULL_RETURN_MEMERR(*np);
    qn = node_new_quantifier(0, REPEAT_INFINITE, 0);
    CHECK_NULL_RETURN_MEMERR(qn);
    NQTFR(qn)->target = *np;
    *np = qn;
    break;

  case TK_BACKREF:
    len = tok->u.backref.num;
    *np = node_new_backref(len,
		   (len > 1 ? tok->u.backref.refs : &(tok->u.backref.ref1)),
			   tok->u.backref.by_name,
#ifdef USE_BACKREF_WITH_LEVEL
			   tok->u.backref.exist_level,
			   tok->u.backref.level,
#endif
			   env);
    CHECK_NULL_RETURN_MEMERR(*np);
    break;

#ifdef USE_SUBEXP_CALL
  case TK_CALL:
    {
      int gnum = tok->u.call.gnum;

      if (gnum < 0 || tok->u.call.rel != 0) {
	if (gnum > 0) gnum--;
	gnum = BACKREF_REL_TO_ABS(gnum, env);
	if (gnum <= 0)
	  return ONIGERR_INVALID_BACKREF;
      }
      *np = node_new_call(tok->u.call.name, tok->u.call.name_end, gnum);
      CHECK_NULL_RETURN_MEMERR(*np);
      env->num_call++;
    }
    break;
#endif

  case TK_ANCHOR:
    *np = onig_node_new_anchor(tok->u.anchor.subtype);
    CHECK_NULL_RETURN_MEMERR(*np);
    NANCHOR(*np)->ascii_range = tok->u.anchor.ascii_range;
    break;

  case TK_OP_REPEAT:
  case TK_INTERVAL:
    if (IS_SYNTAX_BV(env->syntax, ONIG_SYN_CONTEXT_INDEP_REPEAT_OPS)) {
      if (IS_SYNTAX_BV(env->syntax, ONIG_SYN_CONTEXT_INVALID_REPEAT_OPS))
	return ONIGERR_TARGET_OF_REPEAT_OPERATOR_NOT_SPECIFIED;
      else
	*np = node_new_empty();
    }
    else {
      goto tk_byte;
    }
    break;

  default:
    return ONIGERR_PARSER_BUG;
    break;
  }

  {
    targetp = np;

  re_entry:
    r = fetch_token(tok, src, end, env);
    if (r < 0) return r;

  repeat:
    if (r == TK_OP_REPEAT || r == TK_INTERVAL) {
      if (is_invalid_quantifier_target(*targetp))
	return ONIGERR_TARGET_OF_REPEAT_OPERATOR_INVALID;

      qn = node_new_quantifier(tok->u.repeat.lower, tok->u.repeat.upper,
			       (r == TK_INTERVAL ? 1 : 0));
      CHECK_NULL_RETURN_MEMERR(qn);
      NQTFR(qn)->greedy = tok->u.repeat.greedy;
      r = set_quantifier(qn, *targetp, group, env);
      if (r < 0) {
	onig_node_free(qn);
	return r;
      }

      if (tok->u.repeat.possessive != 0) {
	Node* en;
	en = node_new_enclose(ENCLOSE_STOP_BACKTRACK);
	if (IS_NULL(en)) {
	  onig_node_free(qn);
	  return ONIGERR_MEMORY;
	}
	NENCLOSE(en)->target = qn;
	qn = en;
      }

      if (r == 0) {
	*targetp = qn;
      }
      else if (r == 1) {
	onig_node_free(qn);
      }
      else if (r == 2) { /* split case: /abc+/ */
	Node *tmp;

	*targetp = node_new_list(*targetp, NULL);
	if (IS_NULL(*targetp)) {
	  onig_node_free(qn);
	  return ONIGERR_MEMORY;
	}
	tmp = NCDR(*targetp) = node_new_list(qn, NULL);
	if (IS_NULL(tmp)) {
	  onig_node_free(qn);
	  return ONIGERR_MEMORY;
	}
	targetp = &(NCAR(tmp));
      }
      goto re_entry;
    }
  }

  return r;
}


and_code_range1(BBuf** pbuf, ScanEnv* env, OnigCodePoint from1, OnigCodePoint to1,
		OnigCodePoint* data, int n)
{
  int i, r;
  OnigCodePoint from2, to2;

  for (i = 0; i < n; i++) {
    from2 = data[i*2];
    to2   = data[i*2+1];
    if (from2 < from1) {
      if (to2 < from1) continue;
      else {
	from1 = to2 + 1;
      }
    }
    else if (from2 <= to1) {
      if (to2 < to1) {
	if (from1 <= from2 - 1) {
	  r = add_code_range_to_buf(pbuf, env, from1, from2-1);
	  if (r != 0) return r;
	}
	from1 = to2 + 1;
      }
      else {
	to1 = from2 - 1;
      }
    }
    else {
      from1 = from2;
    }
    if (from1 > to1) break;
  }
  if (from1 <= to1) {
    r = add_code_range_to_buf(pbuf, env, from1, to1);
    if (r != 0) return r;
  }
  return 0;
}


strdup_with_null(OnigEncoding enc, UChar* s, UChar* end)
{
  ptrdiff_t slen;
  int term_len, i;
  UChar *r;

  slen = end - s;
  term_len = ONIGENC_MBC_MINLEN(enc);

  r = (UChar* )xmalloc(slen + term_len);
  CHECK_NULL_RETURN(r);
  xmemcpy(r, s, slen);

  for (i = 0; i < term_len; i++)
    r[slen + i] = (UChar )0;

  return r;
}


onig_st_init_strend_table_with_size(st_index_t size)
{
  static const struct st_hash_type hashType = {
    str_end_cmp,
    str_end_hash,
  };

  return (hash_table_type* )
           onig_st_init_table_with_size(&hashType, size);
}


UNKNOWN_ESC_WARN(ScanEnv *env, int c)
{
  if (onig_warn == onig_null_warn || !RTEST(ruby_verbose)) return ;
  onig_syntax_warn(env, "Unknown escape \\%c is ignored", c);
}


create_node_from_array(int kind, Node **np, Node **node_array)
{
  Node* tmp = NULL_NODE;
  int i = 0;

  while (node_array[i] != NULL_NODE)  i++;
  while (--i >= 0) {
    *np = kind==LIST ? node_new_list(node_array[i], tmp)
                     : onig_node_new_alt(node_array[i], tmp);
    if (IS_NULL(*np)) {
      while (i >= 0) {
        onig_node_free(node_array[i]);
        node_array[i--] = NULL_NODE;
      }
      onig_node_free(tmp);
      return ONIGERR_MEMORY;
    }
    else
      node_array[i] = NULL_NODE;
    tmp = *np;
  }
  return 0;
}


strcat_capa_from_static(UChar* dest, UChar* dest_end,
			const UChar* src, const UChar* src_end, size_t capa)
{
  UChar* r;

  r = (UChar* )xmalloc(capa + 1);
  CHECK_NULL_RETURN(r);
  onig_strcpy(r, dest, dest_end);
  onig_strcpy(r + (dest_end - dest), src, src_end);
  return r;
}


fetch_range_quantifier(UChar** src, UChar* end, OnigToken* tok, ScanEnv* env)
{
  int low, up, syn_allow, non_low = 0;
  int r = 0;
  OnigCodePoint c;
  OnigEncoding enc = env->enc;
  UChar* p = *src;
  PFETCH_READY;

  syn_allow = IS_SYNTAX_BV(env->syntax, ONIG_SYN_ALLOW_INVALID_INTERVAL);

  if (PEND) {
    if (syn_allow)
      return 1;  /* "....{" : OK! */
    else
      return ONIGERR_END_PATTERN_AT_LEFT_BRACE;  /* "....{" syntax error */
  }

  if (! syn_allow) {
    c = PPEEK;
    if (c == ')' || c == '(' || c == '|') {
      return ONIGERR_END_PATTERN_AT_LEFT_BRACE;
    }
  }

  low = onig_scan_unsigned_number(&p, end, env->enc);
  if (low < 0) return ONIGERR_TOO_BIG_NUMBER_FOR_REPEAT_RANGE;
  if (low > ONIG_MAX_REPEAT_NUM)
    return ONIGERR_TOO_BIG_NUMBER_FOR_REPEAT_RANGE;

  if (p == *src) { /* can't read low */
    if (IS_SYNTAX_BV(env->syntax, ONIG_SYN_ALLOW_INTERVAL_LOW_ABBREV)) {
      /* allow {,n} as {0,n} */
      low = 0;
      non_low = 1;
    }
    else
      goto invalid;
  }

  if (PEND) goto invalid;
  PFETCH(c);
  if (c == ',') {
    UChar* prev = p;
    up = onig_scan_unsigned_number(&p, end, env->enc);
    if (up < 0) return ONIGERR_TOO_BIG_NUMBER_FOR_REPEAT_RANGE;
    if (up > ONIG_MAX_REPEAT_NUM)
      return ONIGERR_TOO_BIG_NUMBER_FOR_REPEAT_RANGE;

    if (p == prev) {
      if (non_low != 0)
	goto invalid;
      up = REPEAT_INFINITE;  /* {n,} : {n,infinite} */
    }
  }
  else {
    if (non_low != 0)
      goto invalid;

    PUNFETCH;
    up = low;  /* {n} : exact n times */
    r = 2;     /* fixed */
  }

  if (PEND) goto invalid;
  PFETCH(c);
  if (IS_SYNTAX_OP(env->syntax, ONIG_SYN_OP_ESC_BRACE_INTERVAL)) {
    if (c != MC_ESC(env->syntax)) goto invalid;
    if (PEND) goto invalid;
    PFETCH(c);
  }
  if (c != '}') goto invalid;

  if (!IS_REPEAT_INFINITE(up) && low > up) {
    return ONIGERR_UPPER_SMALLER_THAN_LOWER_IN_REPEAT_RANGE;
  }

  tok->type = TK_INTERVAL;
  tok->u.repeat.lower = low;
  tok->u.repeat.upper = up;
  *src = p;
  return r; /* 0: normal {n,m}, 2: fixed {n} */

 invalid:
  if (syn_allow)
    return 1;  /* OK */
  else
    return ONIGERR_INVALID_REPEAT_RANGE_PATTERN;
}


bitset_invert(BitSetRef bs)
{
  int i;
  for (i = 0; i < BITSET_SIZE; i++) { bs[i] = ~(bs[i]); }
}


parse_branch(Node** top, OnigToken* tok, int term,
	     UChar** src, UChar* end, ScanEnv* env)
{
  int r;
  Node *node, **headp;

  *top = NULL;
  r = parse_exp(&node, tok, term, src, end, env);
  if (r < 0) {
    onig_node_free(node);
    return r;
  }

  if (r == TK_EOT || r == term || r == TK_ALT) {
    *top = node;
  }
  else {
    *top  = node_new_list(node, NULL);
    headp = &(NCDR(*top));
    while (r != TK_EOT && r != term && r != TK_ALT) {
      r = parse_exp(&node, tok, term, src, end, env);
      if (r < 0) {
	onig_node_free(node);
	return r;
      }

      if (NTYPE(node) == NT_LIST) {
	*headp = node;
	while (IS_NOT_NULL(NCDR(node))) node = NCDR(node);
	headp = &(NCDR(node));
      }
      else {
	*headp = node_new_list(node, NULL);
	headp = &(NCDR(*headp));
      }
    }
  }

  return r;
}


onig_noname_group_capture_is_active(const regex_t* reg)
{
  if (ONIG_IS_OPTION_ON(reg->options, ONIG_OPTION_DONT_CAPTURE_GROUP))
    return 0;

#ifdef USE_NAMED_GROUP
  if (onig_number_of_names(reg) > 0 &&
      IS_SYNTAX_BV(reg->syntax, ONIG_SYN_CAPTURE_ONLY_NAMED_GROUP) &&
      !ONIG_IS_OPTION_ON(reg->options, ONIG_OPTION_CAPTURE_GROUP)) {
    return 0;
  }
#endif

  return 1;
}


setup_look_behind(Node* node, regex_t* reg, ScanEnv* env)
{
  int r, len;
  AnchorNode* an = ANCHOR_(node);

  r = get_char_length_tree(NODE_ANCHOR_BODY(an), reg, &len);
  if (r == 0)
    an->char_len = len;
  else if (r == GET_CHAR_LEN_VARLEN)
    r = ONIGERR_INVALID_LOOK_BEHIND_PATTERN;
  else if (r == GET_CHAR_LEN_TOP_ALT_VARLEN) {
    if (IS_SYNTAX_BV(env->syntax, ONIG_SYN_DIFFERENT_LEN_ALT_LOOK_BEHIND))
      r = divide_look_behind_alternatives(node);
    else
      r = ONIGERR_INVALID_LOOK_BEHIND_PATTERN;
  }

  return r;
}


setup_call2_call(Node* node)
{
  switch (NODE_TYPE(node)) {
  case NODE_LIST:
  case NODE_ALT:
    do {
      setup_call2_call(NODE_CAR(node));
    } while (IS_NOT_NULL(node = NODE_CDR(node)));
    break;

  case NODE_QUANT:
    setup_call2_call(NODE_BODY(node));
    break;

  case NODE_ANCHOR:
    if (ANCHOR_HAS_BODY(ANCHOR_(node)))
      setup_call2_call(NODE_BODY(node));
    break;

  case NODE_ENCLOSURE:
    {
      EnclosureNode* en = ENCLOSURE_(node);

      if (en->type == ENCLOSURE_MEMORY) {
        if (! NODE_IS_MARK1(node)) {
          NODE_STATUS_ADD(node, NST_MARK1);
          setup_call2_call(NODE_BODY(node));
          NODE_STATUS_REMOVE(node, NST_MARK1);
        }
      }
      else if (en->type == ENCLOSURE_IF_ELSE) {
        setup_call2_call(NODE_BODY(node));
        if (IS_NOT_NULL(en->te.Then))
          setup_call2_call(en->te.Then);
        if (IS_NOT_NULL(en->te.Else))
          setup_call2_call(en->te.Else);
      }
      else {
        setup_call2_call(NODE_BODY(node));
      }
    }
    break;

  case NODE_CALL:
    if (! NODE_IS_MARK1(node)) {
      NODE_STATUS_ADD(node, NST_MARK1);
      {
        CallNode* cn = CALL_(node);
        Node* called = NODE_CALL_BODY(cn);

        cn->entry_count++;

        NODE_STATUS_ADD(called, NST_CALLED);
        ENCLOSURE_(called)->m.entry_count++;
        setup_call2_call(called);
      }
      NODE_STATUS_REMOVE(node, NST_MARK1);
    }
    break;

  default:
    break;
  }
}


add_bitset(regex_t* reg, BitSetRef bs)
{
  BB_ADD(reg, bs, SIZE_BITSET);
  return 0;
}


compile_length_option_node(EnclosureNode* node, regex_t* reg)
{
  int tlen;
  OnigOptionType prev = reg->options;

  reg->options = node->o.options;
  tlen = compile_length_tree(NODE_ENCLOSURE_BODY(node), reg);
  reg->options = prev;

  if (tlen < 0) return tlen;

  if (IS_DYNAMIC_OPTION(prev ^ node->option)) {
    return SIZE_OP_SET_OPTION_PUSH + SIZE_OP_SET_OPTION + SIZE_OP_FAIL
           + tlen + SIZE_OP_SET_OPTION;
  }
  else
    return tlen;
}


print_anchor(FILE* f, int anchor)
{
  int q = 0;

  fprintf(f, "[");

  if (anchor & ANCHOR_BEGIN_BUF) {
    fprintf(f, "begin-buf");
    q = 1;
  }
  if (anchor & ANCHOR_BEGIN_LINE) {
    if (q) fprintf(f, ", ");
    q = 1;
    fprintf(f, "begin-line");
  }
  if (anchor & ANCHOR_BEGIN_POSITION) {
    if (q) fprintf(f, ", ");
    q = 1;
    fprintf(f, "begin-pos");
  }
  if (anchor & ANCHOR_END_BUF) {
    if (q) fprintf(f, ", ");
    q = 1;
    fprintf(f, "end-buf");
  }
  if (anchor & ANCHOR_SEMI_END_BUF) {
    if (q) fprintf(f, ", ");
    q = 1;
    fprintf(f, "semi-end-buf");
  }
  if (anchor & ANCHOR_END_LINE) {
    if (q) fprintf(f, ", ");
    q = 1;
    fprintf(f, "end-line");
  }
  if (anchor & ANCHOR_ANYCHAR_INF) {
    if (q) fprintf(f, ", ");
    q = 1;
    fprintf(f, "anychar-inf");
  }
  if (anchor & ANCHOR_ANYCHAR_INF_ML) {
    if (q) fprintf(f, ", ");
    fprintf(f, "anychar-inf-ml");
  }

  fprintf(f, "]");
}


add_update_var_type(regex_t* reg, enum UpdateVarType type)
{
  UpdateVarType t = (UpdateVarType )type;

  BB_ADD(reg, &t, SIZE_UPDATE_VAR_TYPE);
  return 0;
}


get_char_length_tree1(Node* node, regex_t* reg, int* len, int level)
{
  int tlen;
  int r = 0;

  level++;
  *len = 0;
  switch (NODE_TYPE(node)) {
  case NODE_LIST:
    do {
      r = get_char_length_tree1(NODE_CAR(node), reg, &tlen, level);
      if (r == 0)
        *len = distance_add(*len, tlen);
    } while (r == 0 && IS_NOT_NULL(node = NODE_CDR(node)));
    break;

  case NODE_ALT:
    {
      int tlen2;
      int varlen = 0;

      r = get_char_length_tree1(NODE_CAR(node), reg, &tlen, level);
      while (r == 0 && IS_NOT_NULL(node = NODE_CDR(node))) {
        r = get_char_length_tree1(NODE_CAR(node), reg, &tlen2, level);
        if (r == 0) {
          if (tlen != tlen2)
            varlen = 1;
        }
      }
      if (r == 0) {
        if (varlen != 0) {
          if (level == 1)
            r = GET_CHAR_LEN_TOP_ALT_VARLEN;
          else
            r = GET_CHAR_LEN_VARLEN;
        }
        else
          *len = tlen;
      }
    }
    break;

  case NODE_STRING:
    {
      StrNode* sn = STR_(node);
      UChar *s = sn->s;

      while (s < sn->end) {
        s += enclen(reg->enc, s);
        (*len)++;
      }
    }
    break;

  case NODE_QUANT:
    {
      QuantNode* qn = QUANT_(node);

      if (qn->lower == qn->upper) {
        if (qn->upper == 0) {
          *len = 0;
        }
        else {
          r = get_char_length_tree1(NODE_BODY(node), reg, &tlen, level);
          if (r == 0)
            *len = distance_multiply(tlen, qn->lower);
        }
      }
      else
        r = GET_CHAR_LEN_VARLEN;
    }
    break;

#ifdef USE_CALL
  case NODE_CALL:
    if (! NODE_IS_RECURSION(node))
      r = get_char_length_tree1(NODE_BODY(node), reg, len, level);
    else
      r = GET_CHAR_LEN_VARLEN;
    break;
#endif

  case NODE_CTYPE:
  case NODE_CCLASS:
    *len = 1;
    break;

  case NODE_ENCLOSURE:
    {
      EnclosureNode* en = ENCLOSURE_(node);

      switch (en->type) {
      case ENCLOSURE_MEMORY:
#ifdef USE_CALL
        if (NODE_IS_CLEN_FIXED(node))
          *len = en->char_len;
        else {
          r = get_char_length_tree1(NODE_BODY(node), reg, len, level);
          if (r == 0) {
            en->char_len = *len;
            NODE_STATUS_ADD(node, NST_CLEN_FIXED);
          }
        }
        break;
#endif
      case ENCLOSURE_OPTION:
      case ENCLOSURE_STOP_BACKTRACK:
        r = get_char_length_tree1(NODE_BODY(node), reg, len, level);
        break;
      case ENCLOSURE_IF_ELSE:
        {
          int clen, elen;

          r = get_char_length_tree1(NODE_BODY(node), reg, &clen, level);
          if (r == 0) {
            if (IS_NOT_NULL(en->te.Then)) {
              r = get_char_length_tree1(en->te.Then, reg, &tlen, level);
              if (r != 0) break;
            }
            else tlen = 0;
            if (IS_NOT_NULL(en->te.Else)) {
              r = get_char_length_tree1(en->te.Else, reg, &elen, level);
              if (r != 0) break;
            }
            else elen = 0;

            if (clen + tlen != elen) {
              r = GET_CHAR_LEN_VARLEN;
            }
            else {
              *len = elen;
            }
          }
        }
        break;

      default:
        break;
      }
    }
    break;

  case NODE_ANCHOR:
  case NODE_GIMMICK:
    break;

  case NODE_BACKREF:
    if (NODE_IS_CHECKER(node))
      break;
    /* fall */
  default:
    r = GET_CHAR_LEN_VARLEN;
    break;
  }

  return r;
}


add_opcode(regex_t* reg, int opcode)
{
  BB_ADD1(reg, opcode);
  return 0;
}


compile_cclass_node(CClassNode* cc, regex_t* reg)
{
  int r;

  if (IS_NULL(cc->mbuf)) {
    if (IS_NCCLASS_NOT(cc))
      add_opcode(reg, OP_CCLASS_NOT);
    else
      add_opcode(reg, OP_CCLASS);

    r = add_bitset(reg, cc->bs);
  }
  else {
    if (ONIGENC_MBC_MINLEN(reg->enc) > 1 || bitset_is_empty(cc->bs)) {
      if (IS_NCCLASS_NOT(cc))
        add_opcode(reg, OP_CCLASS_MB_NOT);
      else
        add_opcode(reg, OP_CCLASS_MB);

      r = add_multi_byte_cclass(cc->mbuf, reg);
    }
    else {
      if (IS_NCCLASS_NOT(cc))
        add_opcode(reg, OP_CCLASS_MIX_NOT);
      else
        add_opcode(reg, OP_CCLASS_MIX);

      r = add_bitset(reg, cc->bs);
      if (r != 0) return r;
      r = add_multi_byte_cclass(cc->mbuf, reg);
    }
  }

  return r;
}


set_optimize_map(regex_t* reg, OptMap* m)
{
  int i;

  for (i = 0; i < ONIG_CHAR_TABLE_SIZE; i++)
    reg->map[i] = m->map[i];

  reg->optimize   = OPTIMIZE_MAP;
  reg->dmin       = m->mmd.min;
  reg->dmax       = m->mmd.max;

  if (reg->dmin != INFINITE_LEN) {
    reg->threshold_len = reg->dmin + 1;
  }
}


onig_get_callout_tag_end(regex_t* reg, int callout_num)
{
  CalloutListEntry* e = onig_reg_callout_list_at(reg, callout_num);
  return e->tag_end;
}


parse_enclosure(Node** np, OnigToken* tok, int term, UChar** src, UChar* end,
                ScanEnv* env)
{
  int r, num;
  Node *target;
  OnigOptionType option;
  OnigCodePoint c;
  int list_capture;
  OnigEncoding enc = env->enc;

  UChar* p = *src;
  PFETCH_READY;

  *np = NULL;
  if (PEND) return ONIGERR_END_PATTERN_WITH_UNMATCHED_PARENTHESIS;

  option = env->options;
  c = PPEEK;
  if (c == '?' && IS_SYNTAX_OP2(env->syntax, ONIG_SYN_OP2_QMARK_GROUP_EFFECT)) {
    PINC;
    if (PEND) return ONIGERR_END_PATTERN_IN_GROUP;

    PFETCH(c);
    switch (c) {
    case ':':   /* (?:...) grouping only */
    group:
      r = fetch_token(tok, &p, end, env);
      if (r < 0) return r;
      r = parse_subexp(np, tok, term, &p, end, env);
      if (r < 0) return r;
      *src = p;
      return 1; /* group */
      break;

    case '=':
      *np = onig_node_new_anchor(ANCHOR_PREC_READ, 0);
      break;
    case '!':  /*         preceding read */
      *np = onig_node_new_anchor(ANCHOR_PREC_READ_NOT, 0);
      break;
    case '>':            /* (?>...) stop backtrack */
      *np = node_new_enclosure(ENCLOSURE_STOP_BACKTRACK);
      break;

    case '\'':
      if (IS_SYNTAX_OP2(env->syntax, ONIG_SYN_OP2_QMARK_LT_NAMED_GROUP)) {
        goto named_group1;
      }
      else
        return ONIGERR_UNDEFINED_GROUP_OPTION;
      break;

    case '<':   /* look behind (?<=...), (?<!...) */
      if (PEND) return ONIGERR_END_PATTERN_WITH_UNMATCHED_PARENTHESIS;
      PFETCH(c);
      if (c == '=')
        *np = onig_node_new_anchor(ANCHOR_LOOK_BEHIND, 0);
      else if (c == '!')
        *np = onig_node_new_anchor(ANCHOR_LOOK_BEHIND_NOT, 0);
      else {
        if (IS_SYNTAX_OP2(env->syntax, ONIG_SYN_OP2_QMARK_LT_NAMED_GROUP)) {
          UChar *name;
          UChar *name_end;
          enum REF_NUM num_type;

          PUNFETCH;
          c = '<';

        named_group1:
          list_capture = 0;

        named_group2:
          name = p;
          r = fetch_name((OnigCodePoint )c, &p, end, &name_end, env, &num,
                         &num_type, 0);
          if (r < 0) return r;

          num = scan_env_add_mem_entry(env);
          if (num < 0) return num;
          if (list_capture != 0 && num >= (int )MEM_STATUS_BITS_NUM)
            return ONIGERR_GROUP_NUMBER_OVER_FOR_CAPTURE_HISTORY;

          r = name_add(env->reg, name, name_end, num, env);
          if (r != 0) return r;
          *np = node_new_memory(1);
          CHECK_NULL_RETURN_MEMERR(*np);
          ENCLOSURE_(*np)->m.regnum = num;
          if (list_capture != 0)
            MEM_STATUS_ON_SIMPLE(env->capture_history, num);
          env->num_named++;
        }
        else {
          return ONIGERR_UNDEFINED_GROUP_OPTION;
        }
      }
      break;

    case '~':
      if (IS_SYNTAX_OP2(env->syntax, ONIG_SYN_OP2_QMARK_TILDE_ABSENT_GROUP)) {
        Node* absent;
        Node* expr;
        int head_bar;
        int is_range_cutter;

        if (PEND) return ONIGERR_END_PATTERN_IN_GROUP;

        if (PPEEK_IS('|')) { // (?~|generator|absent)
          PINC;
          if (PEND) return ONIGERR_END_PATTERN_IN_GROUP;

          head_bar = 1;
          if (PPEEK_IS(')')) { // (?~|)  : range clear
            PINC;
            r = make_range_clear(np, env);
            if (r != 0) return r;
            goto end;
          }
        }
        else
          head_bar = 0;

        r = fetch_token(tok, &p, end, env);
        if (r < 0) return r;
        r = parse_subexp(&absent, tok, term, &p, end, env);
        if (r < 0) {
          onig_node_free(absent);
          return r;
        }

        expr = NULL_NODE;
        is_range_cutter = 0;
        if (head_bar != 0) {
          Node* top = absent;
          if (NODE_TYPE(top) != NODE_ALT || IS_NULL(NODE_CDR(top))) {
            expr = NULL_NODE;
            is_range_cutter = 1;
            //return ONIGERR_INVALID_ABSENT_GROUP_GENERATOR_PATTERN;
          }
          else {
            absent = NODE_CAR(top);
            expr   = NODE_CDR(top);
            NODE_CAR(top) = NULL_NODE;
            NODE_CDR(top) = NULL_NODE;
            onig_node_free(top);
            if (IS_NULL(NODE_CDR(expr))) {
              top = expr;
              expr = NODE_CAR(top);
              NODE_CAR(top) = NULL_NODE;
              onig_node_free(top);
            }
          }
        }

        r = make_absent_tree(np, absent, expr, is_range_cutter, env);
        if (r != 0) {
          return r;
        }
        goto end;
      }
      else {
        return ONIGERR_UNDEFINED_GROUP_OPTION;
      }
      break;

#ifdef USE_CALLOUT
    case '{':
      if (! IS_SYNTAX_OP2(env->syntax, ONIG_SYN_OP2_QMARK_BRACE_CALLOUT_CONTENTS))
        return ONIGERR_UNDEFINED_GROUP_OPTION;

      r = parse_callout_of_contents(np, ')', &p, end, env);
      if (r != 0) return r;

      goto end;
      break;
#endif

    case '(':
      /* (?()...) */
      if (IS_SYNTAX_OP2(env->syntax, ONIG_SYN_OP2_QMARK_LPAREN_IF_ELSE)) {
        UChar *prev;
        Node* condition;
        int condition_is_checker;

        if (PEND) return ONIGERR_END_PATTERN_IN_GROUP;
        PFETCH(c);
        if (PEND) return ONIGERR_END_PATTERN_IN_GROUP;

        if (IS_CODE_DIGIT_ASCII(enc, c)
            || c == '-' || c == '+' || c == '<' || c == '\'') {
          UChar* name_end;
          int back_num;
          int exist_level;
          int level;
          enum REF_NUM num_type;
          int is_enclosed;

          is_enclosed = (c == '<' || c == '\'') ? 1 : 0;
          if (! is_enclosed)
            PUNFETCH;
          prev = p;
          exist_level = 0;
#ifdef USE_BACKREF_WITH_LEVEL
          name_end = NULL_UCHARP; /* no need. escape gcc warning. */
          r = fetch_name_with_level(
                    (OnigCodePoint )(is_enclosed != 0 ? c : '('),
                    &p, end, &name_end,
                    env, &back_num, &level, &num_type);
          if (r == 1) exist_level = 1;
#else
          r = fetch_name((OnigCodePoint )(is_enclosed != 0 ? c : '('),
                         &p, end, &name_end, env, &back_num, &num_type, 1);
#endif
          if (r < 0) {
            if (is_enclosed == 0) {
              goto any_condition;
            }
            else
              return r;
          }

          condition_is_checker = 1;
          if (num_type != IS_NOT_NUM) {
            if (num_type == IS_REL_NUM) {
              back_num = backref_rel_to_abs(back_num, env);
            }
            if (back_num <= 0)
              return ONIGERR_INVALID_BACKREF;

            if (IS_SYNTAX_BV(env->syntax, ONIG_SYN_STRICT_CHECK_BACKREF)) {
              if (back_num > env->num_mem ||
                  IS_NULL(SCANENV_MEMENV(env)[back_num].node))
                return ONIGERR_INVALID_BACKREF;
            }

            condition = node_new_backref_checker(1, &back_num, 0,
#ifdef USE_BACKREF_WITH_LEVEL
                                                 exist_level, level,
#endif
                                                 env);
          }
          else {
            int num;
            int* backs;

            num = onig_name_to_group_numbers(env->reg, prev, name_end, &backs);
            if (num <= 0) {
              onig_scan_env_set_error_string(env,
                        ONIGERR_UNDEFINED_NAME_REFERENCE, prev, name_end);
              return ONIGERR_UNDEFINED_NAME_REFERENCE;
            }
            if (IS_SYNTAX_BV(env->syntax, ONIG_SYN_STRICT_CHECK_BACKREF)) {
              int i;
              for (i = 0; i < num; i++) {
                if (backs[i] > env->num_mem ||
                    IS_NULL(SCANENV_MEMENV(env)[backs[i]].node))
                  return ONIGERR_INVALID_BACKREF;
              }
            }

            condition = node_new_backref_checker(num, backs, 1,
#ifdef USE_BACKREF_WITH_LEVEL
                                                 exist_level, level,
#endif
                                                 env);
          }

          if (is_enclosed != 0) {
            if (PEND) goto err_if_else;
            PFETCH(c);
            if (c != ')') goto err_if_else;
          }
        }
#ifdef USE_CALLOUT
        else if (c == '?') {
          if (IS_SYNTAX_OP2(env->syntax,
                            ONIG_SYN_OP2_QMARK_BRACE_CALLOUT_CONTENTS)) {
            if (! PEND && PPEEK_IS('{')) {
              /* condition part is callouts of contents: (?(?{...})THEN|ELSE) */
              condition_is_checker = 0;
              PFETCH(c);
              r = parse_callout_of_contents(&condition, ')', &p, end, env);
              if (r != 0) return r;
              goto end_condition;
            }
          }
          goto any_condition;
        }
        else if (c == '*' &&
                 IS_SYNTAX_OP2(env->syntax, ONIG_SYN_OP2_ASTERISK_CALLOUT_NAME)) {
          condition_is_checker = 0;
          r = parse_callout_of_name(&condition, ')', &p, end, env);
          if (r != 0) return r;
          goto end_condition;
        }
#endif
        else {
        any_condition:
          PUNFETCH;
          condition_is_checker = 0;
          r = fetch_token(tok, &p, end, env);
          if (r < 0) return r;
          r = parse_subexp(&condition, tok, term, &p, end, env);
          if (r < 0) {
            onig_node_free(condition);
            return r;
          }
        }

      end_condition:
        CHECK_NULL_RETURN_MEMERR(condition);

        if (PEND) {
        err_if_else:
          onig_node_free(condition);
          return ONIGERR_END_PATTERN_IN_GROUP;
        }

        if (PPEEK_IS(')')) { /* case: empty body: make backref checker */
          if (condition_is_checker == 0) {
            onig_node_free(condition);
            return ONIGERR_INVALID_IF_ELSE_SYNTAX;
          }
          PFETCH(c);
          *np = condition;
        }
        else { /* if-else */
          int then_is_empty;
          Node *Then, *Else;

          if (PPEEK_IS('|')) {
            PFETCH(c);
            Then = 0;
            then_is_empty = 1;
          }
          else
            then_is_empty = 0;

          r = fetch_token(tok, &p, end, env);
          if (r < 0) {
            onig_node_free(condition);
            return r;
          }
          r = parse_subexp(&target, tok, term, &p, end, env);
          if (r < 0) {
            onig_node_free(condition);
            onig_node_free(target);
            return r;
          }

          if (then_is_empty != 0) {
            Else = target;
          }
          else {
            if (NODE_TYPE(target) == NODE_ALT) {
              Then = NODE_CAR(target);
              if (NODE_CDR(NODE_CDR(target)) == NULL_NODE) {
                Else = NODE_CAR(NODE_CDR(target));
                cons_node_free_alone(NODE_CDR(target));
              }
              else {
                Else = NODE_CDR(target);
              }
              cons_node_free_alone(target);
            }
            else {
              Then = target;
              Else = 0;
            }
          }

          *np = node_new_enclosure_if_else(condition, Then, Else);
          if (IS_NULL(*np)) {
            onig_node_free(condition);
            onig_node_free(Then);
            onig_node_free(Else);
            return ONIGERR_MEMORY;
          }
        }
        goto end;
      }
      else {
        return ONIGERR_UNDEFINED_GROUP_OPTION;
      }
      break;

    case '@':
      if (IS_SYNTAX_OP2(env->syntax, ONIG_SYN_OP2_ATMARK_CAPTURE_HISTORY)) {
        if (IS_SYNTAX_OP2(env->syntax, ONIG_SYN_OP2_QMARK_LT_NAMED_GROUP)) {
          PFETCH(c);
          if (c == '<' || c == '\'') {
            list_capture = 1;
            goto named_group2; /* (?@<name>...) */
          }
          PUNFETCH;
        }

        *np = node_new_memory(0);
        CHECK_NULL_RETURN_MEMERR(*np);
        num = scan_env_add_mem_entry(env);
        if (num < 0) {
          return num;
        }
        else if (num >= (int )MEM_STATUS_BITS_NUM) {
          return ONIGERR_GROUP_NUMBER_OVER_FOR_CAPTURE_HISTORY;
        }
        ENCLOSURE_(*np)->m.regnum = num;
        MEM_STATUS_ON_SIMPLE(env->capture_history, num);
      }
      else {
        return ONIGERR_UNDEFINED_GROUP_OPTION;
      }
      break;

#ifdef USE_POSIXLINE_OPTION
    case 'p':
#endif
    case '-': case 'i': case 'm': case 's': case 'x':
    case 'W': case 'D': case 'S': case 'P':
      {
        int neg = 0;

        while (1) {
          switch (c) {
          case ':':
          case ')':
            break;

          case '-':  neg = 1; break;
          case 'x':  OPTION_NEGATE(option, ONIG_OPTION_EXTEND,     neg); break;
          case 'i':  OPTION_NEGATE(option, ONIG_OPTION_IGNORECASE, neg); break;
          case 's':
            if (IS_SYNTAX_OP2(env->syntax, ONIG_SYN_OP2_OPTION_PERL)) {
              OPTION_NEGATE(option, ONIG_OPTION_MULTILINE,  neg);
            }
            else
              return ONIGERR_UNDEFINED_GROUP_OPTION;
            break;

          case 'm':
            if (IS_SYNTAX_OP2(env->syntax, ONIG_SYN_OP2_OPTION_PERL)) {
              OPTION_NEGATE(option, ONIG_OPTION_SINGLELINE, (neg == 0 ? 1 : 0));
            }
            else if (IS_SYNTAX_OP2(env->syntax, ONIG_SYN_OP2_OPTION_RUBY)) {
              OPTION_NEGATE(option, ONIG_OPTION_MULTILINE,  neg);
            }
            else
              return ONIGERR_UNDEFINED_GROUP_OPTION;
            break;
#ifdef USE_POSIXLINE_OPTION
          case 'p':
            OPTION_NEGATE(option, ONIG_OPTION_MULTILINE|ONIG_OPTION_SINGLELINE, neg);
            break;
#endif
          case 'W': OPTION_NEGATE(option, ONIG_OPTION_WORD_IS_ASCII, neg); break;
          case 'D': OPTION_NEGATE(option, ONIG_OPTION_DIGIT_IS_ASCII, neg); break;
          case 'S': OPTION_NEGATE(option, ONIG_OPTION_SPACE_IS_ASCII, neg); break;
          case 'P': OPTION_NEGATE(option, ONIG_OPTION_POSIX_IS_ASCII, neg); break;

          default:
            return ONIGERR_UNDEFINED_GROUP_OPTION;
          }

          if (c == ')') {
            *np = node_new_option(option);
            CHECK_NULL_RETURN_MEMERR(*np);
            *src = p;
            return 2; /* option only */
          }
          else if (c == ':') {
            OnigOptionType prev = env->options;

            env->options = option;
            r = fetch_token(tok, &p, end, env);
            if (r < 0) return r;
            r = parse_subexp(&target, tok, term, &p, end, env);
            env->options = prev;
            if (r < 0) {
              onig_node_free(target);
              return r;
            }
            *np = node_new_option(option);
            CHECK_NULL_RETURN_MEMERR(*np);
            NODE_BODY(*np) = target;
            *src = p;
            return 0;
          }

          if (PEND) return ONIGERR_END_PATTERN_IN_GROUP;
          PFETCH(c);
        }
      }
      break;

    default:
      return ONIGERR_UNDEFINED_GROUP_OPTION;
    }
  }
#ifdef USE_CALLOUT
  else if (c == '*' &&
           IS_SYNTAX_OP2(env->syntax, ONIG_SYN_OP2_ASTERISK_CALLOUT_NAME)) {
    PINC;
    r = parse_callout_of_name(np, ')', &p, end, env);
    if (r != 0) return r;

    goto end;
  }
#endif
  else {
    if (ONIG_IS_OPTION_ON(env->options, ONIG_OPTION_DONT_CAPTURE_GROUP))
      goto group;

    *np = node_new_memory(0);
    CHECK_NULL_RETURN_MEMERR(*np);
    num = scan_env_add_mem_entry(env);
    if (num < 0) return num;
    ENCLOSURE_(*np)->m.regnum = num;
  }

  CHECK_NULL_RETURN_MEMERR(*np);
  r = fetch_token(tok, &p, end, env);
  if (r < 0) return r;
  r = parse_subexp(&target, tok, term, &p, end, env);
  if (r < 0) {
    onig_node_free(target);
    return r;
  }

  NODE_BODY(*np) = target;

  if (NODE_TYPE(*np) == NODE_ENCLOSURE) {
    if (ENCLOSURE_(*np)->type == ENCLOSURE_MEMORY) {
      /* Don't move this to previous of parse_subexp() */
      r = scan_env_set_mem_node(env, ENCLOSURE_(*np)->m.regnum, *np);
      if (r != 0) return r;
    }
  }

 end:
  *src = p;
  return 0;
}


node_new_option(OnigOptionType option)
{
  Node* node = node_new_enclosure(ENCLOSURE_OPTION);
  CHECK_NULL_RETURN(node);
  ENCLOSURE_(node)->o.options = option;
  return node;
}


node_new_general_newline(Node** node, ScanEnv* env)
{
  int r;
  int dlen, alen;
  UChar buf[ONIGENC_CODE_TO_MBC_MAXLEN * 2];
  Node* crnl;
  Node* ncc;
  Node* x;
  CClassNode* cc;

  dlen = ONIGENC_CODE_TO_MBC(env->enc, 0x0d, buf);
  if (dlen < 0) return dlen;
  alen = ONIGENC_CODE_TO_MBC(env->enc, 0x0a, buf + dlen);
  if (alen < 0) return alen;

  crnl = node_new_str_raw(buf, buf + dlen + alen);
  CHECK_NULL_RETURN_MEMERR(crnl);

  ncc = node_new_cclass();
  if (IS_NULL(ncc)) goto err2;

  cc = CCLASS_(ncc);
  if (dlen == 1) {
    bitset_set_range(cc->bs, 0x0a, 0x0d);
  }
  else {
    r = add_code_range(&(cc->mbuf), env, 0x0a, 0x0d);
    if (r != 0) {
    err1:
      onig_node_free(ncc);
    err2:
      onig_node_free(crnl);
      return ONIGERR_MEMORY;
    }
  }

  if (ONIGENC_IS_UNICODE_ENCODING(env->enc)) {
    r = add_code_range(&(cc->mbuf), env, 0x85, 0x85);
    if (r != 0) goto err1;
    r = add_code_range(&(cc->mbuf), env, 0x2028, 0x2029);
    if (r != 0) goto err1;
  }

  x = node_new_enclosure_if_else(crnl, 0, ncc);
  if (IS_NULL(x)) goto err1;

  *node = x;
  return 0;
}


make_call_zero_body(Node* node, ScanEnv* env, Node** rnode)
{
  int r;

  Node* x = node_new_memory(0 /* 0: is not named */);
  CHECK_NULL_RETURN_MEMERR(x);

  NODE_BODY(x) = node;
  ENCLOSURE_(x)->m.regnum = 0;
  r = scan_env_set_mem_node(env, 0, x);
  if (r != 0) {
    onig_node_free(x);
    return r;
  }

  *rnode = x;
  return 0;
}


onig_parse_tree(Node** root, const UChar* pattern, const UChar* end,
                regex_t* reg, ScanEnv* env)
{
  int r;
  UChar* p;
#ifdef USE_CALLOUT
  RegexExt* ext;
#endif

  names_clear(reg);

  scan_env_clear(env);
  env->options        = reg->options;
  env->case_fold_flag = reg->case_fold_flag;
  env->enc            = reg->enc;
  env->syntax         = reg->syntax;
  env->pattern        = (UChar* )pattern;
  env->pattern_end    = (UChar* )end;
  env->reg            = reg;

  *root = NULL;

  if (! ONIGENC_IS_VALID_MBC_STRING(env->enc, pattern, end))
    return ONIGERR_INVALID_WIDE_CHAR_VALUE;

  p = (UChar* )pattern;
  r = parse_regexp(root, &p, (UChar* )end, env);

#ifdef USE_CALL
  if (r != 0) return r;

  if (env->has_call_zero != 0) {
    Node* zero_node;
    r = make_call_zero_body(*root, env, &zero_node);
    if (r != 0) return r;

    *root = zero_node;
  }
#endif

  reg->num_mem = env->num_mem;

#ifdef USE_CALLOUT
  ext = REG_EXTP(reg);
  if (IS_NOT_NULL(ext) && ext->callout_num > 0) {
    r = setup_ext_callout_list_values(reg);
  }
#endif

  return r;
}


parse_callout_of_name(Node** np, int cterm, UChar** src, UChar* end, ScanEnv* env)
{
  int r;
  int i;
  int in;
  int num;
  int name_id;
  int arg_num;
  int max_arg_num;
  int opt_arg_num;
  int is_not_single;
  OnigCodePoint c;
  UChar* name_start;
  UChar* name_end;
  UChar* tag_start;
  UChar* tag_end;
  Node*  node;
  CalloutListEntry* e;
  RegexExt* ext;
  unsigned int types[ONIG_CALLOUT_MAX_ARGS_NUM];
  OnigValue    vals[ONIG_CALLOUT_MAX_ARGS_NUM];
  OnigEncoding enc = env->enc;
  UChar* p = *src;

  //PFETCH_READY;
  if (PEND) return ONIGERR_INVALID_CALLOUT_PATTERN;

  node = 0;
  name_start = p;
  while (1) {
    if (PEND) return ONIGERR_END_PATTERN_IN_GROUP;
    name_end = p;
    PFETCH_S(c);
    if (c == cterm || c == '[' || c == '{') break;
  }

  if (! is_allowed_callout_name(enc, name_start, name_end))
    return ONIGERR_INVALID_CALLOUT_NAME;

  if (c == '[') {
    if (PEND) return ONIGERR_END_PATTERN_IN_GROUP;
    tag_start = p;
    while (! PEND) {
      if (PEND) return ONIGERR_END_PATTERN_IN_GROUP;
      tag_end = p;
      PFETCH_S(c);
      if (c == ']') break;
    }
    if (! is_allowed_callout_tag_name(enc, tag_start, tag_end))
      return ONIGERR_INVALID_CALLOUT_TAG_NAME;

    if (PEND) return ONIGERR_END_PATTERN_IN_GROUP;
    PFETCH_S(c);
  }
  else {
    tag_start = tag_end = 0;
  }

  if (c == '{') {
    UChar* save;

    if (PEND) return ONIGERR_END_PATTERN_IN_GROUP;

    /* read for single check only */
    save = p;
    arg_num = parse_callout_args(1, '}', &p, end, 0, 0, env);
    if (arg_num < 0) return arg_num;

    is_not_single = PPEEK_IS(cterm) ?  0 : 1;
    p = save;
    r = get_callout_name_id_by_name(enc, is_not_single, name_start, name_end,
                                    &name_id);
    if (r != ONIG_NORMAL) return r;

    max_arg_num = get_callout_arg_num_by_name_id(name_id);
    for (i = 0; i < max_arg_num; i++) {
      types[i] = get_callout_arg_type_by_name_id(name_id, i);
    }

    arg_num = parse_callout_args(0, '}', &p, end, types, vals, env);
    if (arg_num < 0) return arg_num;

    if (PEND) return ONIGERR_END_PATTERN_IN_GROUP;
    PFETCH_S(c);
  }
  else {
    arg_num = 0;

    is_not_single = 0;
    r = get_callout_name_id_by_name(enc, is_not_single, name_start, name_end,
                                      &name_id);
    if (r != ONIG_NORMAL) return r;

    max_arg_num = get_callout_arg_num_by_name_id(name_id);
    for (i = 0; i < max_arg_num; i++) {
      types[i] = get_callout_arg_type_by_name_id(name_id, i);
    }
  }

  in = onig_get_callout_in_by_name_id(name_id);
  opt_arg_num = get_callout_opt_arg_num_by_name_id(name_id);
  if (arg_num > max_arg_num || arg_num < (max_arg_num - opt_arg_num))
    return ONIGERR_INVALID_CALLOUT_ARG;

  if (c != cterm)
    return ONIGERR_INVALID_CALLOUT_PATTERN;

  r = reg_callout_list_entry(env, &num);
  if (r != 0) return r;

  ext = onig_get_regex_ext(env->reg);
  if (IS_NULL(ext->pattern)) {
    r = onig_ext_set_pattern(env->reg, env->pattern, env->pattern_end);
    if (r != ONIG_NORMAL) return r;
  }

  if (tag_start != tag_end) {
    r = callout_tag_entry(env->reg, tag_start, tag_end, num);
    if (r != ONIG_NORMAL) return r;
  }

  r = node_new_callout(&node, ONIG_CALLOUT_OF_NAME, num, name_id, env);
  if (r != ONIG_NORMAL) return r;

  e = onig_reg_callout_list_at(env->reg, num);
  e->of         = ONIG_CALLOUT_OF_NAME;
  e->in         = in;
  e->name_id    = name_id;
  e->type       = onig_get_callout_type_by_name_id(name_id);
  e->start_func = onig_get_callout_start_func_by_name_id(name_id);
  e->end_func   = onig_get_callout_end_func_by_name_id(name_id);
  e->u.arg.num        = max_arg_num;
  e->u.arg.passed_num = arg_num;
  for (i = 0; i < max_arg_num; i++) {
    e->u.arg.types[i] = types[i];
    if (i < arg_num)
      e->u.arg.vals[i] = vals[i];
    else
      e->u.arg.vals[i] = get_callout_opt_default_by_name_id(name_id, i);
  }

  *np = node;
  *src = p;
  return 0;
}


find_str_position(OnigCodePoint s[], int n, UChar* from, UChar* to,
                  UChar **next, OnigEncoding enc)
{
  int i;
  OnigCodePoint x;
  UChar *q;
  UChar *p = from;

  while (p < to) {
    x = ONIGENC_MBC_TO_CODE(enc, p, to);
    q = p + enclen(enc, p);
    if (x == s[0]) {
      for (i = 1; i < n && q < to; i++) {
        x = ONIGENC_MBC_TO_CODE(enc, q, to);
        if (x != s[i]) break;
        q += enclen(enc, q);
      }
      if (i >= n) {
        if (IS_NOT_NULL(next))
          *next = q;
        return p;
      }
    }
    p = q;
  }
  return NULL_UCHARP;
}


str_node_split_last_char(StrNode* sn, OnigEncoding enc)
{
  const UChar *p;
  Node* n = NULL_NODE;

  if (sn->end > sn->s) {
    p = onigenc_get_prev_char_head(enc, sn->s, sn->end);
    if (p && p > sn->s) { /* can be split. */
      n = node_new_str(p, sn->end);
      if ((sn->flag & STRING_RAW) != 0)
        NODE_STRING_SET_RAW(n);

      sn->end = (UChar* )p;
    }
  }
  return n;
}


pdf_filter_BMC(fz_context *ctx, pdf_processor *proc, const char *tag)
{
	/* Create a tag, and push it onto pending_tags. If it gets
	 * flushed to the stream, it'll be moved from there onto
	 * current_tags. */
	pdf_filter_processor *p = (pdf_filter_processor*)proc;
	tag_record *bmc = fz_malloc_struct(ctx, tag_record);

	fz_try(ctx)
		bmc->tag = fz_strdup(ctx, tag);
	fz_catch(ctx)
	{
		fz_free(ctx, bmc);
		fz_rethrow(ctx);
	}
	bmc->prev = p->pending_tags;
	p->pending_tags = bmc;
}


pdf_filter_MP(fz_context *ctx, pdf_processor *proc, const char *tag)
{
	pdf_filter_processor *p = (pdf_filter_processor*)proc;
	filter_flush(ctx, p, 0);
	if (p->chain->op_MP)
		p->chain->op_MP(ctx, p->chain, tag);
}


pdf_filter_gs_begin(fz_context *ctx, pdf_processor *proc, const char *name, pdf_obj *extgstate)
{
	pdf_filter_processor *p = (pdf_filter_processor*)proc;
	filter_flush(ctx, p, FLUSH_ALL);
	if (p->chain->op_gs_begin)
		p->chain->op_gs_begin(ctx, p->chain, name, extgstate);
	copy_resource(ctx, p, PDF_NAME(ExtGState), name);
}


adjust_text(fz_context *ctx, pdf_filter_processor *p, float x, float y)
{
	float skip_dist = p->tos.fontdesc->wmode == 1 ? -y : -x;
	skip_dist = skip_dist / p->gstate->pending.text.size;
	p->Tm_adjust += skip_dist;
}


pdf_filter_Tw(fz_context *ctx, pdf_processor *proc, float wordspace)
{
	pdf_filter_processor *p = (pdf_filter_processor*)proc;
	filter_flush(ctx, p, 0);
	p->gstate->pending.text.word_space = wordspace;
}


pdf_filter_l(fz_context *ctx, pdf_processor *proc, float x, float y)
{
	pdf_filter_processor *p = (pdf_filter_processor*)proc;
	filter_flush(ctx, p, FLUSH_CTM);
	if (p->chain->op_l)
		p->chain->op_l(ctx, p->chain, x, y);
}


TEST_F(ConnectionHandlerTest, ContinueOnListenerFilterTimeout) {
  InSequence s;

  TestListener* test_listener =
      addListener(1, true, false, "test_listener", Network::Address::SocketType::Stream,
                  std::chrono::milliseconds(15000), true);
  Network::MockListener* listener = new Network::MockListener();
  Network::ListenerCallbacks* listener_callbacks;
  EXPECT_CALL(dispatcher_, createListener_(_, _, _))
      .WillOnce(
          Invoke([&](Network::Socket&, Network::ListenerCallbacks& cb, bool) -> Network::Listener* {
            listener_callbacks = &cb;
            return listener;
          }));
  EXPECT_CALL(test_listener->socket_, localAddress());
  handler_->addListener(*test_listener);

  Network::MockListenerFilter* test_filter = new NiceMock<Network::MockListenerFilter>();
  EXPECT_CALL(factory_, createListenerFilterChain(_))
      .WillRepeatedly(Invoke([&](Network::ListenerFilterManager& manager) -> bool {
        manager.addAcceptFilter(Network::ListenerFilterPtr{test_filter});
        return true;
      }));
  EXPECT_CALL(*test_filter, onAccept(_))
      .WillOnce(Invoke([&](Network::ListenerFilterCallbacks&) -> Network::FilterStatus {
        return Network::FilterStatus::StopIteration;
      }));
  Network::MockConnectionSocket* accepted_socket = new NiceMock<Network::MockConnectionSocket>();
  Network::IoSocketHandleImpl io_handle{42};
  EXPECT_CALL(*accepted_socket, ioHandle()).WillRepeatedly(ReturnRef(io_handle));
  Event::MockTimer* timeout = new Event::MockTimer(&dispatcher_);
  EXPECT_CALL(*timeout, enableTimer(std::chrono::milliseconds(15000), _));
  listener_callbacks->onAccept(Network::ConnectionSocketPtr{accepted_socket});
  Stats::Gauge& downstream_pre_cx_active =
      stats_store_.gauge("downstream_pre_cx_active", Stats::Gauge::ImportMode::Accumulate);
  EXPECT_EQ(1UL, downstream_pre_cx_active.value());
  EXPECT_CALL(*test_filter, destroy_());
  // Barrier: test_filter must be destructed before findFilterChain
  EXPECT_CALL(manager_, findFilterChain(_)).WillOnce(Return(nullptr));
  EXPECT_CALL(*timeout, disableTimer());
  timeout->invokeCallback();
  dispatcher_.clearDeferredDeleteList();
  EXPECT_EQ(0UL, downstream_pre_cx_active.value());
  EXPECT_EQ(1UL, stats_store_.counter("downstream_pre_cx_timeout").value());

  // Make sure we continued to try create connection.
  EXPECT_EQ(1UL, stats_store_.counter("no_filter_chain_match").value());

  EXPECT_CALL(*listener, onDestroy());
}


TEST_F(ConnectionHandlerTest, TransportProtocolDefault) {
  TestListener* test_listener = addListener(1, true, false, "test_listener");
  Network::MockListener* listener = new Network::MockListener();
  Network::ListenerCallbacks* listener_callbacks;
  EXPECT_CALL(dispatcher_, createListener_(_, _, _))
      .WillOnce(
          Invoke([&](Network::Socket&, Network::ListenerCallbacks& cb, bool) -> Network::Listener* {
            listener_callbacks = &cb;
            return listener;
          }));
  EXPECT_CALL(test_listener->socket_, localAddress());
  handler_->addListener(*test_listener);

  Network::MockConnectionSocket* accepted_socket = new NiceMock<Network::MockConnectionSocket>();
  EXPECT_CALL(*accepted_socket, detectedTransportProtocol())
      .WillOnce(Return(absl::string_view("")));
  EXPECT_CALL(*accepted_socket, setDetectedTransportProtocol(absl::string_view("raw_buffer")));
  EXPECT_CALL(manager_, findFilterChain(_)).WillOnce(Return(nullptr));
  listener_callbacks->onAccept(Network::ConnectionSocketPtr{accepted_socket});

  EXPECT_CALL(*listener, onDestroy());
}


void ConnectionHandlerImpl::decNumConnections() {
  ASSERT(num_handler_connections_ > 0);
  --num_handler_connections_;
}


TEST_F(ConnectionHandlerTest, FallbackToWildcardListener) {
  TestListener* test_listener1 = addListener(1, true, true, "test_listener1");
  Network::MockListener* listener1 = new Network::MockListener();
  Network::ListenerCallbacks* listener_callbacks1;
  EXPECT_CALL(dispatcher_, createListener_(_, _, _))
      .WillOnce(
          Invoke([&](Network::Socket&, Network::ListenerCallbacks& cb, bool) -> Network::Listener* {
            listener_callbacks1 = &cb;
            return listener1;
          }));
  Network::Address::InstanceConstSharedPtr normal_address(
      new Network::Address::Ipv4Instance("127.0.0.1", 10001));
  EXPECT_CALL(test_listener1->socket_, localAddress()).WillRepeatedly(ReturnRef(normal_address));
  handler_->addListener(*test_listener1);

  TestListener* test_listener2 = addListener(1, false, false, "test_listener2");
  Network::MockListener* listener2 = new Network::MockListener();
  Network::ListenerCallbacks* listener_callbacks2;
  EXPECT_CALL(dispatcher_, createListener_(_, _, _))
      .WillOnce(
          Invoke([&](Network::Socket&, Network::ListenerCallbacks& cb, bool) -> Network::Listener* {
            listener_callbacks2 = &cb;
            return listener2;
          }));
  Network::Address::InstanceConstSharedPtr any_address = Network::Utility::getIpv4AnyAddress();
  EXPECT_CALL(test_listener2->socket_, localAddress()).WillRepeatedly(ReturnRef(any_address));
  handler_->addListener(*test_listener2);

  Network::MockListenerFilter* test_filter = new Network::MockListenerFilter();
  EXPECT_CALL(*test_filter, destroy_());
  Network::MockConnectionSocket* accepted_socket = new NiceMock<Network::MockConnectionSocket>();
  bool redirected = false;
  EXPECT_CALL(factory_, createListenerFilterChain(_))
      .WillRepeatedly(Invoke([&](Network::ListenerFilterManager& manager) -> bool {
        // Insert the Mock filter.
        if (!redirected) {
          manager.addAcceptFilter(Network::ListenerFilterPtr{test_filter});
          redirected = true;
        }
        return true;
      }));
  // Zero port to match the port of AnyAddress
  Network::Address::InstanceConstSharedPtr alt_address(
      new Network::Address::Ipv4Instance("127.0.0.2", 0));
  EXPECT_CALL(*test_filter, onAccept(_))
      .WillOnce(Invoke([&](Network::ListenerFilterCallbacks& cb) -> Network::FilterStatus {
        cb.socket().restoreLocalAddress(alt_address);
        return Network::FilterStatus::Continue;
      }));
  EXPECT_CALL(*accepted_socket, restoreLocalAddress(alt_address));
  EXPECT_CALL(*accepted_socket, localAddressRestored()).WillOnce(Return(true));
  EXPECT_CALL(*accepted_socket, localAddress()).WillRepeatedly(ReturnRef(alt_address));
  EXPECT_CALL(manager_, findFilterChain(_)).WillOnce(Return(filter_chain_.get()));
  Network::MockConnection* connection = new NiceMock<Network::MockConnection>();
  EXPECT_CALL(dispatcher_, createServerConnection_()).WillOnce(Return(connection));
  EXPECT_CALL(factory_, createNetworkFilterChain(_, _)).WillOnce(Return(true));
  listener_callbacks1->onAccept(Network::ConnectionSocketPtr{accepted_socket});
  EXPECT_EQ(1UL, handler_->numConnections());

  EXPECT_CALL(*listener2, onDestroy());
  EXPECT_CALL(*listener1, onDestroy());
}


Network::FilterChainManager& filterChainManager() override { return parent_.manager_; }