Fix #718

<|original_code|>
#define LIBM_DD(name) \
static jv f_ ## name(jq_state *jq, jv input) { \
  if (jv_get_kind(input) != JV_KIND_NUMBER) { \
    return type_error(input, "number required"); \
  } \
  jv ret = jv_number(name(jv_number_value(input))); \
  jv_free(input); \
  return ret; \
}
#include "libm.h"
#undef LIBM_DD

static jv f_negate(jq_state *jq, jv input) {
  if (jv_get_kind(input) != JV_KIND_NUMBER) {
    return type_error(input, "cannot be negated");
  }
  jv ret = jv_number(-jv_number_value(input));
  jv_free(input);
  return ret;
}

static jv f_startswith(jq_state *jq, jv a, jv b) {
  int alen = jv_string_length_bytes(jv_copy(a));
  int blen = jv_string_length_bytes(jv_copy(b));
  jv ret;

  if (blen <= alen && memcmp(jv_string_value(a), jv_string_value(b), blen) == 0)
    ret = jv_true();
  else
    ret = jv_false();
  jv_free(a);
  jv_free(b);
  return ret;
}

static jv f_endswith(jq_state *jq, jv a, jv b) {
  const char *astr = jv_string_value(a);
  const char *bstr = jv_string_value(b);
  size_t alen = jv_string_length_bytes(jv_copy(a));
  size_t blen = jv_string_length_bytes(jv_copy(b));
  jv ret;;

  if (alen < blen ||
     memcmp(astr + (alen - blen), bstr, blen) != 0)
    ret = jv_false();
  else
    ret = jv_true();
  jv_free(a);
  jv_free(b);
  return ret;
}

static jv f_ltrimstr(jq_state *jq, jv input, jv left) {
  if (jv_get_kind(f_startswith(jq, jv_copy(input), jv_copy(left))) != JV_KIND_TRUE) {
    jv_free(left);
    return input;
  }
  /*
   * FIXME It'd be better to share the suffix with the original input --
   * that we could do, we just can't share prefixes.
<|edits_diff|>
--- builtin.c
+++ builtin.c
@@ -20,6 +20,8 @@
 }

 static jv f_startswith(jq_state *jq, jv a, jv b) {
+  if (jv_get_kind(a) != JV_KIND_STRING || jv_get_kind(b) != JV_KIND_STRING)
+    return jv_invalid_with_msg(jv_string("startswith() requires string inputs"));
   int alen = jv_string_length_bytes(jv_copy(a));
   int blen = jv_string_length_bytes(jv_copy(b));
   jv ret;
<|current_version|>
#define LIBM_DD(name) \
static jv f_ ## name(jq_state *jq, jv input) { \
  if (jv_get_kind(input) != JV_KIND_NUMBER) { \
    return type_error(input, "number required"); \
  } \
  jv ret = jv_number(name(jv_number_value(input))); \
  jv_free(input); \
  return ret; \
}
#include "libm.h"
#undef LIBM_DD

static jv f_negate(jq_state *jq, jv input) {
  if (jv_get_kind(input) != JV_KIND_NUMBER) {
    return type_error(input, "cannot be negated");
  }
  jv ret = jv_number(-jv_number_value(input));
  jv_free(input);
  return ret;
}

static jv f_startswith(jq_state *jq, jv a, jv b) {
  if (jv_get_kind(a) != JV_KIND_STRING || jv_get_kind(b) != JV_KIND_STRING)
    return jv_invalid_with_msg(jv_string("startswith() requires string inputs"));
  int alen = jv_string_length_bytes(jv_copy(a));
  int blen = jv_string_length_bytes(jv_copy(b));
  jv ret;

  if (blen <= alen && memcmp(jv_string_value(a), jv_string_value(b), blen) == 0)
    ret = jv_true();
  else
    ret = jv_false();
  jv_free(a);
  jv_free(b);
  return ret;
}

static jv f_endswith(jq_state *jq, jv a, jv b) {
  const char *astr = jv_string_value(a);
  const char *bstr = jv_string_value(b);
  size_t alen = jv_string_length_bytes(jv_copy(a));
  size_t blen = jv_string_length_bytes(jv_copy(b));
  jv ret;;

  if (alen < blen ||
     memcmp(astr + (alen - blen), bstr, blen) != 0)
    ret = jv_false();
  else
    ret = jv_true();
  jv_free(a);
  jv_free(b);
  return ret;
}

static jv f_ltrimstr(jq_state *jq, jv input, jv left) {
  if (jv_get_kind(f_startswith(jq, jv_copy(input), jv_copy(left))) != JV_KIND_TRUE) {
    jv_free(left);
    return input;
  }
  /*
   * FIXME It'd be better to share the suffix with the original input --
   * that we could do, we just can't share prefixes.
<|next_version|>
#define LIBM_DD(name) \
static jv f_ ## name(jq_state *jq, jv input) { \
  if (jv_get_kind(input) != JV_KIND_NUMBER) { \
    return type_error(input, "number required"); \
  } \
  jv ret = jv_number(name(jv_number_value(input))); \
  jv_free(input); \
  return ret; \
}
#include "libm.h"
#undef LIBM_DD

static jv f_negate(jq_state *jq, jv input) {
  if (jv_get_kind(input) != JV_KIND_NUMBER) {
    return type_error(input, "cannot be negated");
  }
  jv ret = jv_number(-jv_number_value(input));
  jv_free(input);
  return ret;
}

static jv f_startswith(jq_state *jq, jv a, jv b) {
  if (jv_get_kind(a) != JV_KIND_STRING || jv_get_kind(b) != JV_KIND_STRING)
    return jv_invalid_with_msg(jv_string("startswith() requires string inputs"));
  int alen = jv_string_length_bytes(jv_copy(a));
  int blen = jv_string_length_bytes(jv_copy(b));
  jv ret;

  if (blen <= alen && memcmp(jv_string_value(a), jv_string_value(b), blen) == 0)
    ret = jv_true();
  else
    ret = jv_false();
  jv_free(a);
  jv_free(b);
  return ret;
}

static jv f_endswith(jq_state *jq, jv a, jv b) {
  if (jv_get_kind(a) != JV_KIND_STRING || jv_get_kind(b) != JV_KIND_STRING)
    return jv_invalid_with_msg(jv_string("endswith() requires string inputs"));
  const char *astr = jv_string_value(a);
  const char *bstr = jv_string_value(b);
  size_t alen = jv_string_length_bytes(jv_copy(a));
  size_t blen = jv_string_length_bytes(jv_copy(b));
  jv ret;;

  if (alen < blen ||
     memcmp(astr + (alen - blen), bstr, blen) != 0)
    ret = jv_false();
  else
    ret = jv_true();
  jv_free(a);
  jv_free(b);
  return ret;
}

static jv f_ltrimstr(jq_state *jq, jv input, jv left) {
  if (jv_get_kind(f_startswith(jq, jv_copy(input), jv_copy(left))) != JV_KIND_TRUE) {
    jv_free(left);
    return input;
  }
  /*
   * FIXME It'd be better to share the suffix with the original input --
   * that we could do, we just can't share prefixes.


Fix some confusion between "null" and "invalid".

<|original_code|>

void run_tests() {
  FILE* testdata = fopen("testdata","r");
  char buf[4096];
  int tests = 0, passed = 0;

  while (1) {
    if (!fgets(buf, sizeof(buf), testdata)) break;
    if (skipline(buf)) continue;
    printf("Testing %s\n", buf);
    int pass = 1;
    block program = compile(buf);
    block_append(&program, gen_op_simple(YIELD));
    block_append(&program, gen_op_simple(BACKTRACK));
    program = gen_cbinding(&builtins, program);
    struct bytecode* bc = block_compile(program);
    block_free(program);
    printf("Disassembly:\n");
    dump_disassembly(2, bc);
    printf("\n");
    fgets(buf, sizeof(buf), testdata);
    jv input = jv_parse(buf);
    jq_init(bc, input);

    while (fgets(buf, sizeof(buf), testdata)) {
      if (skipline(buf)) break;
      jv expected = jv_parse(buf);
      jv actual = jq_next();
      if (!jv_is_valid(actual)) {
        printf("Insufficient results\n");
        pass = 0;
        break;
      } else if (!jv_equal(expected, actual)) {
        printf("Expected ");
        jv_dump(expected);
        printf(", but got ");
        jv_dump(actual);
        printf("\n");
        pass = 0;
      }
    }
    if (pass) {
      jv extra = jq_next();
      if (jv_is_valid(extra)) {
        printf("Superfluous result: ");
        jv_dump(extra);
        printf("\n");
        pass = 0;
      }
    }
    jq_teardown();
<|edits_diff|>
--- c/main.c
+++ c/main.c
@@ -20,6 +20,7 @@
     printf("\n");
     fgets(buf, sizeof(buf), testdata);
     jv input = jv_parse(buf);
+    assert(jv_is_valid(input));
     jq_init(bc, input);

     while (fgets(buf, sizeof(buf), testdata)) {
<|current_version|>

void run_tests() {
  FILE* testdata = fopen("testdata","r");
  char buf[4096];
  int tests = 0, passed = 0;

  while (1) {
    if (!fgets(buf, sizeof(buf), testdata)) break;
    if (skipline(buf)) continue;
    printf("Testing %s\n", buf);
    int pass = 1;
    block program = compile(buf);
    block_append(&program, gen_op_simple(YIELD));
    block_append(&program, gen_op_simple(BACKTRACK));
    program = gen_cbinding(&builtins, program);
    struct bytecode* bc = block_compile(program);
    block_free(program);
    printf("Disassembly:\n");
    dump_disassembly(2, bc);
    printf("\n");
    fgets(buf, sizeof(buf), testdata);
    jv input = jv_parse(buf);
    assert(jv_is_valid(input));
    jq_init(bc, input);

    while (fgets(buf, sizeof(buf), testdata)) {
      if (skipline(buf)) break;
      jv expected = jv_parse(buf);
      jv actual = jq_next();
      if (!jv_is_valid(actual)) {
        printf("Insufficient results\n");
        pass = 0;
        break;
      } else if (!jv_equal(expected, actual)) {
        printf("Expected ");
        jv_dump(expected);
        printf(", but got ");
        jv_dump(actual);
        printf("\n");
        pass = 0;
      }
    }
    if (pass) {
      jv extra = jq_next();
      if (jv_is_valid(extra)) {
        printf("Superfluous result: ");
        jv_dump(extra);
        printf("\n");
        pass = 0;
      }
    }
    jq_teardown();
<|next_version|>

void run_tests() {
  FILE* testdata = fopen("testdata","r");
  char buf[4096];
  int tests = 0, passed = 0;

  while (1) {
    if (!fgets(buf, sizeof(buf), testdata)) break;
    if (skipline(buf)) continue;
    printf("Testing %s\n", buf);
    int pass = 1;
    block program = compile(buf);
    block_append(&program, gen_op_simple(YIELD));
    block_append(&program, gen_op_simple(BACKTRACK));
    program = gen_cbinding(&builtins, program);
    struct bytecode* bc = block_compile(program);
    block_free(program);
    printf("Disassembly:\n");
    dump_disassembly(2, bc);
    printf("\n");
    fgets(buf, sizeof(buf), testdata);
    jv input = jv_parse(buf);
    assert(jv_is_valid(input));
    jq_init(bc, input);

    while (fgets(buf, sizeof(buf), testdata)) {
      if (skipline(buf)) break;
      jv expected = jv_parse(buf);
      assert(jv_is_valid(expected));
      jv actual = jq_next();
      if (!jv_is_valid(actual)) {
        printf("Insufficient results\n");
        pass = 0;
        break;
      } else if (!jv_equal(expected, actual)) {
        printf("Expected ");
        jv_dump(expected);
        printf(", but got ");
        jv_dump(actual);
        printf("\n");
        pass = 0;
      }
    }
    if (pass) {
      jv extra = jq_next();
      if (jv_is_valid(extra)) {
        printf("Superfluous result: ");
        jv_dump(extra);
        printf("\n");
        pass = 0;
      }
    }
    jq_teardown();


Support "null" in JQ programs

<|original_code|>
#include "builtin.h"

static void f_false(jv input[], jv output[]) {
  jv_free(input[0]);
  output[0] = jv_false();
}

static void f_true(jv input[], jv output[]) {
  jv_free(input[0]);
  output[0] = jv_true();
}

static void f_plus(jv input[], jv output[]) {
  jv_free(input[0]);
  jv a = input[2];
  jv b = input[1];
  if (jv_get_kind(a) == JV_KIND_NUMBER && jv_get_kind(b) == JV_KIND_NUMBER) {
    output[0] = jv_number(jv_number_value(a) +
                          jv_number_value(b));
  } else if (jv_get_kind(a) == JV_KIND_ARRAY && jv_get_kind(b) == JV_KIND_ARRAY) {
    output[0] = jv_array_concat(a, b);
  } else {
    output[0] = jv_string("wtf gaize");
    jv_free(a);
    jv_free(b);
  }
}

struct cfunction function_list[] = {
  {f_true, "true", CALL_BUILTIN_1_1},
  {f_false, "false", CALL_BUILTIN_1_1},
  {f_plus, "_plus", CALL_BUILTIN_3_1},
};
struct symbol_table builtins = {function_list, sizeof(function_list)/sizeof(function_list[0])};

<|edits_diff|>
--- c/builtin.c
+++ c/builtin.c
@@ -10,6 +10,10 @@
   output[0] = jv_true();
 }

+static void f_null(jv input[], jv output[]) {
+  jv_free(input[0]);
+  output[0] = jv_null();
+}

 static void f_plus(jv input[], jv output[]) {
   jv_free(input[0]);
<|current_version|>
#include "builtin.h"

static void f_false(jv input[], jv output[]) {
  jv_free(input[0]);
  output[0] = jv_false();
}

static void f_true(jv input[], jv output[]) {
  jv_free(input[0]);
  output[0] = jv_true();
}

static void f_null(jv input[], jv output[]) {
  jv_free(input[0]);
  output[0] = jv_null();
}

static void f_plus(jv input[], jv output[]) {
  jv_free(input[0]);
  jv a = input[2];
  jv b = input[1];
  if (jv_get_kind(a) == JV_KIND_NUMBER && jv_get_kind(b) == JV_KIND_NUMBER) {
    output[0] = jv_number(jv_number_value(a) +
                          jv_number_value(b));
  } else if (jv_get_kind(a) == JV_KIND_ARRAY && jv_get_kind(b) == JV_KIND_ARRAY) {
    output[0] = jv_array_concat(a, b);
  } else {
    output[0] = jv_string("wtf gaize");
    jv_free(a);
    jv_free(b);
  }
}

struct cfunction function_list[] = {
  {f_true, "true", CALL_BUILTIN_1_1},
  {f_false, "false", CALL_BUILTIN_1_1},
  {f_plus, "_plus", CALL_BUILTIN_3_1},
};
struct symbol_table builtins = {function_list, sizeof(function_list)/sizeof(function_list[0])};

<|next_version|>
#include "builtin.h"

static void f_false(jv input[], jv output[]) {
  jv_free(input[0]);
  output[0] = jv_false();
}

static void f_true(jv input[], jv output[]) {
  jv_free(input[0]);
  output[0] = jv_true();
}

static void f_null(jv input[], jv output[]) {
  jv_free(input[0]);
  output[0] = jv_null();
}

static void f_plus(jv input[], jv output[]) {
  jv_free(input[0]);
  jv a = input[2];
  jv b = input[1];
  if (jv_get_kind(a) == JV_KIND_NUMBER && jv_get_kind(b) == JV_KIND_NUMBER) {
    output[0] = jv_number(jv_number_value(a) +
                          jv_number_value(b));
  } else if (jv_get_kind(a) == JV_KIND_ARRAY && jv_get_kind(b) == JV_KIND_ARRAY) {
    output[0] = jv_array_concat(a, b);
  } else {
    output[0] = jv_string("wtf gaize");
    jv_free(a);
    jv_free(b);
  }
}

struct cfunction function_list[] = {
  {f_true, "true", CALL_BUILTIN_1_1},
  {f_false, "false", CALL_BUILTIN_1_1},
  {f_null, "null", CALL_BUILTIN_1_1},
  {f_plus, "_plus", CALL_BUILTIN_3_1},
};
struct symbol_table builtins = {function_list, sizeof(function_list)/sizeof(function_list[0])};


update tests and project files for stb_ds

<|original_code|>
               int dist3 = abs(out1[k][2] - out2[k][2]);
               ++count;
               if (out1[k][1] > out2[k][1])
                  ++bigcount;
            }
         }
      }
      printf("So far: %d (%d big) of %d\n", count, bigcount, total);
   }
   printf("Final: %d (%d big) of %d\n", count, bigcount, total);
}
#endif

float hdr_data[200][200][3];

void dummy_write(void *context, void *data, int len)
{
   static char dummy[1024];
   if (len > 1024) len = 1024;
   memcpy(dummy, data, len);
}

int main(int argc, char **argv)
{
   int w,h;
   //test_ycbcr();

   #if 0
   // test hdr asserts
   for (h=0; h < 100; h += 2)
      for (w=0; w < 200; ++w)
         hdr_data[h][w][0] = (float) rand(),
         hdr_data[h][w][1] = (float) rand(),
         hdr_data[h][w][2] = (float) rand();

   stbi_write_hdr("output/test.hdr", 200,200,3,hdr_data[0][0]);
   #endif

   if (argc > 1) {
      int i, n;

      for (i=1; i < argc; ++i) {
         int res;
         int w2,h2,n2;
         unsigned char *data;
         printf("%s\n", argv[i]);
         res = stbi_info(argv[i], &w2, &h2, &n2);
         data = stbi_load(argv[i], &w, &h, &n, 4); if (data) free(data); else printf("Failed &n\n");
         data = stbi_load(argv[i], &w, &h,  0, 1); if (data) free(data); else printf("Failed 1\n");
         data = stbi_load(argv[i], &w, &h,  0, 2); if (data) free(data); else printf("Failed 2\n");
<|edits_diff|>
--- tests/image_test.c
+++ tests/image_test.c
@@ -19,6 +19,8 @@
    if (len > 1024) len = 1024;
    memcpy(dummy, data, len);
 }
+
+extern void image_write_test(void);

 int main(int argc, char **argv)
 {
<|current_version|>
               int dist3 = abs(out1[k][2] - out2[k][2]);
               ++count;
               if (out1[k][1] > out2[k][1])
                  ++bigcount;
            }
         }
      }
      printf("So far: %d (%d big) of %d\n", count, bigcount, total);
   }
   printf("Final: %d (%d big) of %d\n", count, bigcount, total);
}
#endif

float hdr_data[200][200][3];

void dummy_write(void *context, void *data, int len)
{
   static char dummy[1024];
   if (len > 1024) len = 1024;
   memcpy(dummy, data, len);
}

extern void image_write_test(void);

int main(int argc, char **argv)
{
   int w,h;
   //test_ycbcr();

   #if 0
   // test hdr asserts
   for (h=0; h < 100; h += 2)
      for (w=0; w < 200; ++w)
         hdr_data[h][w][0] = (float) rand(),
         hdr_data[h][w][1] = (float) rand(),
         hdr_data[h][w][2] = (float) rand();

   stbi_write_hdr("output/test.hdr", 200,200,3,hdr_data[0][0]);
   #endif

   if (argc > 1) {
      int i, n;

      for (i=1; i < argc; ++i) {
         int res;
         int w2,h2,n2;
         unsigned char *data;
         printf("%s\n", argv[i]);
         res = stbi_info(argv[i], &w2, &h2, &n2);
         data = stbi_load(argv[i], &w, &h, &n, 4); if (data) free(data); else printf("Failed &n\n");
         data = stbi_load(argv[i], &w, &h,  0, 1); if (data) free(data); else printf("Failed 1\n");
         data = stbi_load(argv[i], &w, &h,  0, 2); if (data) free(data); else printf("Failed 2\n");
<|next_version|>
               int dist3 = abs(out1[k][2] - out2[k][2]);
               ++count;
               if (out1[k][1] > out2[k][1])
                  ++bigcount;
            }
         }
      }
      printf("So far: %d (%d big) of %d\n", count, bigcount, total);
   }
   printf("Final: %d (%d big) of %d\n", count, bigcount, total);
}
#endif

float hdr_data[200][200][3];

void dummy_write(void *context, void *data, int len)
{
   static char dummy[1024];
   if (len > 1024) len = 1024;
   memcpy(dummy, data, len);
}

extern void image_write_test(void);

int main(int argc, char **argv)
{
   int w,h;
   //test_ycbcr();

   image_write_test();

   #if 0
   // test hdr asserts
   for (h=0; h < 100; h += 2)
      for (w=0; w < 200; ++w)
         hdr_data[h][w][0] = (float) rand(),
         hdr_data[h][w][1] = (float) rand(),
         hdr_data[h][w][2] = (float) rand();

   stbi_write_hdr("output/test.hdr", 200,200,3,hdr_data[0][0]);
   #endif

   if (argc > 1) {
      int i, n;

      for (i=1; i < argc; ++i) {
         int res;
         int w2,h2,n2;
         unsigned char *data;
         printf("%s\n", argv[i]);
         res = stbi_info(argv[i], &w2, &h2, &n2);
         data = stbi_load(argv[i], &w, &h, &n, 4); if (data) free(data); else printf("Failed &n\n");
         data = stbi_load(argv[i], &w, &h,  0, 1); if (data) free(data); else printf("Failed 1\n");
         data = stbi_load(argv[i], &w, &h,  0, 2); if (data) free(data); else printf("Failed 2\n");


fix bug where we couldn't rewind a file that reached EOF, which can happen with < 92-byte PIC,PNM,HDR,TGA

<|original_code|>
float hdr_data[200][200][3];

int main(int argc, char **argv)
{
   int w,h;
   //test_ycbcr();

   #if 0
   // test hdr asserts
   for (h=0; h < 100; h += 2)
      for (w=0; w < 200; ++w)
         hdr_data[h][w][0] = (float) rand(),
         hdr_data[h][w][1] = (float) rand(),
         hdr_data[h][w][2] = (float) rand();

   stbi_write_hdr("output/test.hdr", 200,200,3,hdr_data[0][0]);
   #endif

   if (argc > 1) {
      int i, n;

      for (i=1; i < argc; ++i) {
         unsigned char *data;
         printf("%s\n", argv[i]);
         data = stbi_load(argv[i], &w, &h, &n, 4); if (data) free(data); else printf("Failed &n\n");
         data = stbi_load(argv[i], &w, &h,  0, 1); if (data) free(data); else printf("Failed 1\n");
         data = stbi_load(argv[i], &w, &h,  0, 2); if (data) free(data); else printf("Failed 2\n");
         data = stbi_load(argv[i], &w, &h,  0, 3); if (data) free(data); else printf("Failed 3\n");
         data = stbi_load(argv[i], &w, &h,  0, 4);
         assert(data);
         if (data) {
            char fname[512];
            stb_splitpath(fname, argv[i], STB_FILE);
            stbi_write_png(stb_sprintf("output/%s.png", fname), w, h, 4, data, w*4);
            free(data);
         } else
            printf("FAILED 4\n");
      }
   } else {
      int i, nope=0;
      #ifdef PNGSUITE_PRIMARY
      char **files = stb_readdir_files("pngsuite/primary");
      #else
      char **files = stb_readdir_files("images");
      #endif
      for (i=0; i < stb_arr_len(files); ++i) {
         int n;
         char **failed = NULL;
         unsigned char *data;
         printf(".");
         //printf("%s\n", files[i]);
         data = stbi_load(files[i], &w, &h, &n, 0); if (data) free(data); else stb_arr_push(failed, "&n");
         data = stbi_load(files[i], &w, &h,  0, 1); if (data) free(data); else stb_arr_push(failed, "1");
         data = stbi_load(files[i], &w, &h,  0, 2); if (data) free(data); else stb_arr_push(failed, "2");
<|edits_diff|>
--- tests/image_test.c
+++ tests/image_test.c
@@ -20,8 +20,10 @@
       int i, n;

       for (i=1; i < argc; ++i) {
+         int res;
          unsigned char *data;
          printf("%s\n", argv[i]);
+         res = stbi_info(argv[1], &w, &h, &n);
          data = stbi_load(argv[i], &w, &h, &n, 4); if (data) free(data); else printf("Failed &n\n");
          data = stbi_load(argv[i], &w, &h,  0, 1); if (data) free(data); else printf("Failed 1\n");
          data = stbi_load(argv[i], &w, &h,  0, 2); if (data) free(data); else printf("Failed 2\n");
<|current_version|>
float hdr_data[200][200][3];

int main(int argc, char **argv)
{
   int w,h;
   //test_ycbcr();

   #if 0
   // test hdr asserts
   for (h=0; h < 100; h += 2)
      for (w=0; w < 200; ++w)
         hdr_data[h][w][0] = (float) rand(),
         hdr_data[h][w][1] = (float) rand(),
         hdr_data[h][w][2] = (float) rand();

   stbi_write_hdr("output/test.hdr", 200,200,3,hdr_data[0][0]);
   #endif

   if (argc > 1) {
      int i, n;

      for (i=1; i < argc; ++i) {
         int res;
         unsigned char *data;
         printf("%s\n", argv[i]);
         res = stbi_info(argv[1], &w, &h, &n);
         data = stbi_load(argv[i], &w, &h, &n, 4); if (data) free(data); else printf("Failed &n\n");
         data = stbi_load(argv[i], &w, &h,  0, 1); if (data) free(data); else printf("Failed 1\n");
         data = stbi_load(argv[i], &w, &h,  0, 2); if (data) free(data); else printf("Failed 2\n");
         data = stbi_load(argv[i], &w, &h,  0, 3); if (data) free(data); else printf("Failed 3\n");
         data = stbi_load(argv[i], &w, &h,  0, 4);
         assert(data);
         if (data) {
            char fname[512];
            stb_splitpath(fname, argv[i], STB_FILE);
            stbi_write_png(stb_sprintf("output/%s.png", fname), w, h, 4, data, w*4);
            free(data);
         } else
            printf("FAILED 4\n");
      }
   } else {
      int i, nope=0;
      #ifdef PNGSUITE_PRIMARY
      char **files = stb_readdir_files("pngsuite/primary");
      #else
      char **files = stb_readdir_files("images");
      #endif
      for (i=0; i < stb_arr_len(files); ++i) {
         int n;
         char **failed = NULL;
         unsigned char *data;
         printf(".");
         //printf("%s\n", files[i]);
         data = stbi_load(files[i], &w, &h, &n, 0); if (data) free(data); else stb_arr_push(failed, "&n");
         data = stbi_load(files[i], &w, &h,  0, 1); if (data) free(data); else stb_arr_push(failed, "1");
         data = stbi_load(files[i], &w, &h,  0, 2); if (data) free(data); else stb_arr_push(failed, "2");
<|next_version|>
float hdr_data[200][200][3];

int main(int argc, char **argv)
{
   int w,h;
   //test_ycbcr();

   #if 0
   // test hdr asserts
   for (h=0; h < 100; h += 2)
      for (w=0; w < 200; ++w)
         hdr_data[h][w][0] = (float) rand(),
         hdr_data[h][w][1] = (float) rand(),
         hdr_data[h][w][2] = (float) rand();

   stbi_write_hdr("output/test.hdr", 200,200,3,hdr_data[0][0]);
   #endif

   if (argc > 1) {
      int i, n;

      for (i=1; i < argc; ++i) {
         int res;
         unsigned char *data;
         printf("%s\n", argv[i]);
         res = stbi_info(argv[1], &w, &h, &n);
         data = stbi_load(argv[i], &w, &h, &n, 4); if (data) free(data); else printf("Failed &n\n");
         data = stbi_load(argv[i], &w, &h,  0, 1); if (data) free(data); else printf("Failed 1\n");
         data = stbi_load(argv[i], &w, &h,  0, 2); if (data) free(data); else printf("Failed 2\n");
         data = stbi_load(argv[i], &w, &h,  0, 3); if (data) free(data); else printf("Failed 3\n");
         data = stbi_load(argv[i], &w, &h,  0, 4);
         assert(data);
         assert(res);
         if (data) {
            char fname[512];
            stb_splitpath(fname, argv[i], STB_FILE);
            stbi_write_png(stb_sprintf("output/%s.png", fname), w, h, 4, data, w*4);
            free(data);
         } else
            printf("FAILED 4\n");
      }
   } else {
      int i, nope=0;
      #ifdef PNGSUITE_PRIMARY
      char **files = stb_readdir_files("pngsuite/primary");
      #else
      char **files = stb_readdir_files("images");
      #endif
      for (i=0; i < stb_arr_len(files); ++i) {
         int n;
         char **failed = NULL;
         unsigned char *data;
         printf(".");
         //printf("%s\n", files[i]);
         data = stbi_load(files[i], &w, &h, &n, 0); if (data) free(data); else stb_arr_push(failed, "&n");
         data = stbi_load(files[i], &w, &h,  0, 1); if (data) free(data); else stb_arr_push(failed, "1");
         data = stbi_load(files[i], &w, &h,  0, 2); if (data) free(data); else stb_arr_push(failed, "2");


QUIC: fixed anti-amplification with explicit send.

<|original_code|>
            path->expires = ngx_current_msec + pto;
            return NGX_OK;
        }

        /* rc == NGX_DECLINED */
    }

    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
                   "quic path seq:%uL expired mtu:%uz",
                   path->seqnum, path->mtud);

    path->max_mtu = path->mtud;

    ngx_quic_discover_path_mtu(c, path);

    return NGX_OK;
}

static ngx_int_t
ngx_quic_send_path_mtu_probe(ngx_connection_t *c, ngx_quic_path_t *path)
{
    ngx_int_t               rc;
    ngx_uint_t              log_error;
    ngx_quic_frame_t        frame;
    ngx_quic_send_ctx_t    *ctx;
    ngx_quic_connection_t  *qc;

    ngx_memzero(&frame, sizeof(ngx_quic_frame_t));

    frame.level = ssl_encryption_application;
    frame.type = NGX_QUIC_FT_PING;

    qc = ngx_quic_get_connection(c);
    ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
    path->mtu_pnum[path->tries] = ctx->pnum;

    ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
                   "quic path seq:%uL send probe "
                   "mtu:%uz pnum:%uL tries:%ui",
                   path->seqnum, path->mtud, ctx->pnum, path->tries);

    log_error = c->log_error;
    c->log_error = NGX_ERROR_IGNORE_EMSGSIZE;

    rc = ngx_quic_frame_sendto(c, &frame, path->mtud, path);
    c->log_error = log_error;

    if (rc == NGX_ERROR) {
        if (c->write->error) {
            c->write->error = 0;

            ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
                           "quic path seq:%uL rejected mtu:%uz",
                           path->seqnum, path->mtud);

            return NGX_DECLINED;
        }

        return NGX_ERROR;
    }

    return NGX_OK;
}

ngx_int_t
ngx_quic_handle_path_mtu(ngx_connection_t *c, ngx_quic_path_t *path,
    uint64_t min, uint64_t max)
{
<|edits_diff|>
--- src/event/quic/ngx_event_quic_migration.c
+++ src/event/quic/ngx_event_quic_migration.c
@@ -20,6 +20,7 @@
 static ngx_int_t
 ngx_quic_send_path_mtu_probe(ngx_connection_t *c, ngx_quic_path_t *path)
 {
+    size_t                  mtu;
     ngx_int_t               rc;
     ngx_uint_t              log_error;
     ngx_quic_frame_t        frame;
@@ -42,6 +43,9 @@

     log_error = c->log_error;
     c->log_error = NGX_ERROR_IGNORE_EMSGSIZE;
+
+    mtu = path->mtu;
+    path->mtu = path->mtud;

     rc = ngx_quic_frame_sendto(c, &frame, path->mtud, path);
     c->log_error = log_error;
<|current_version|>
            path->expires = ngx_current_msec + pto;
            return NGX_OK;
        }

        /* rc == NGX_DECLINED */
    }

    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
                   "quic path seq:%uL expired mtu:%uz",
                   path->seqnum, path->mtud);

    path->max_mtu = path->mtud;

    ngx_quic_discover_path_mtu(c, path);

    return NGX_OK;
}

static ngx_int_t
ngx_quic_send_path_mtu_probe(ngx_connection_t *c, ngx_quic_path_t *path)
{
    size_t                  mtu;
    ngx_int_t               rc;
    ngx_uint_t              log_error;
    ngx_quic_frame_t        frame;
    ngx_quic_send_ctx_t    *ctx;
    ngx_quic_connection_t  *qc;

    ngx_memzero(&frame, sizeof(ngx_quic_frame_t));

    frame.level = ssl_encryption_application;
    frame.type = NGX_QUIC_FT_PING;

    qc = ngx_quic_get_connection(c);
    ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
    path->mtu_pnum[path->tries] = ctx->pnum;

    ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
                   "quic path seq:%uL send probe "
                   "mtu:%uz pnum:%uL tries:%ui",
                   path->seqnum, path->mtud, ctx->pnum, path->tries);

    log_error = c->log_error;
    c->log_error = NGX_ERROR_IGNORE_EMSGSIZE;

    mtu = path->mtu;
    path->mtu = path->mtud;

    rc = ngx_quic_frame_sendto(c, &frame, path->mtud, path);
    c->log_error = log_error;

    if (rc == NGX_ERROR) {
        if (c->write->error) {
            c->write->error = 0;

            ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
                           "quic path seq:%uL rejected mtu:%uz",
                           path->seqnum, path->mtud);

            return NGX_DECLINED;
        }

        return NGX_ERROR;
    }

    return NGX_OK;
}

ngx_int_t
ngx_quic_handle_path_mtu(ngx_connection_t *c, ngx_quic_path_t *path,
    uint64_t min, uint64_t max)
{
<|next_version|>
            path->expires = ngx_current_msec + pto;
            return NGX_OK;
        }

        /* rc == NGX_DECLINED */
    }

    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
                   "quic path seq:%uL expired mtu:%uz",
                   path->seqnum, path->mtud);

    path->max_mtu = path->mtud;

    ngx_quic_discover_path_mtu(c, path);

    return NGX_OK;
}

static ngx_int_t
ngx_quic_send_path_mtu_probe(ngx_connection_t *c, ngx_quic_path_t *path)
{
    size_t                  mtu;
    ngx_int_t               rc;
    ngx_uint_t              log_error;
    ngx_quic_frame_t        frame;
    ngx_quic_send_ctx_t    *ctx;
    ngx_quic_connection_t  *qc;

    ngx_memzero(&frame, sizeof(ngx_quic_frame_t));

    frame.level = ssl_encryption_application;
    frame.type = NGX_QUIC_FT_PING;

    qc = ngx_quic_get_connection(c);
    ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
    path->mtu_pnum[path->tries] = ctx->pnum;

    ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
                   "quic path seq:%uL send probe "
                   "mtu:%uz pnum:%uL tries:%ui",
                   path->seqnum, path->mtud, ctx->pnum, path->tries);

    log_error = c->log_error;
    c->log_error = NGX_ERROR_IGNORE_EMSGSIZE;

    mtu = path->mtu;
    path->mtu = path->mtud;

    rc = ngx_quic_frame_sendto(c, &frame, path->mtud, path);

    path->mtu = mtu;
    c->log_error = log_error;

    if (rc == NGX_ERROR) {
        if (c->write->error) {
            c->write->error = 0;

            ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
                           "quic path seq:%uL rejected mtu:%uz",
                           path->seqnum, path->mtud);

            return NGX_DECLINED;
        }

        return NGX_ERROR;
    }

    return NGX_OK;
}

ngx_int_t
ngx_quic_handle_path_mtu(ngx_connection_t *c, ngx_quic_path_t *path,
    uint64_t min, uint64_t max)
{


Mail: parsing of the PROXY protocol from clients.

<|original_code|>
{
    ngx_uint_t           i;
    ngx_mail_in_addr_t  *addrs;
    struct sockaddr_in  *sin;

    mport->addrs = ngx_pcalloc(cf->pool,
                               mport->naddrs * sizeof(ngx_mail_in_addr_t));
    if (mport->addrs == NULL) {
        return NGX_ERROR;
    }

    addrs = mport->addrs;

    for (i = 0; i < mport->naddrs; i++) {

        sin = (struct sockaddr_in *) addr[i].opt.sockaddr;
        addrs[i].addr = sin->sin_addr.s_addr;

        addrs[i].conf.ctx = addr[i].opt.ctx;
#if (NGX_MAIL_SSL)
        addrs[i].conf.ssl = addr[i].opt.ssl;
#endif
        addrs[i].conf.addr_text = addr[i].opt.addr_text;
    }

    return NGX_OK;
}

#if (NGX_HAVE_INET6)

static ngx_int_t
ngx_mail_add_addrs6(ngx_conf_t *cf, ngx_mail_port_t *mport,
    ngx_mail_conf_addr_t *addr)
{
    ngx_uint_t            i;
    ngx_mail_in6_addr_t  *addrs6;
    struct sockaddr_in6  *sin6;

    mport->addrs = ngx_pcalloc(cf->pool,
                               mport->naddrs * sizeof(ngx_mail_in6_addr_t));
    if (mport->addrs == NULL) {
        return NGX_ERROR;
    }

    addrs6 = mport->addrs;

    for (i = 0; i < mport->naddrs; i++) {

        sin6 = (struct sockaddr_in6 *) addr[i].opt.sockaddr;
        addrs6[i].addr6 = sin6->sin6_addr;

        addrs6[i].conf.ctx = addr[i].opt.ctx;
#if (NGX_MAIL_SSL)
        addrs6[i].conf.ssl = addr[i].opt.ssl;
#endif
        addrs6[i].conf.addr_text = addr[i].opt.addr_text;
    }

    return NGX_OK;
}

#endif

static ngx_int_t
ngx_mail_cmp_conf_addrs(const void *one, const void *two)
{
    ngx_mail_conf_addr_t  *first, *second;

    first = (ngx_mail_conf_addr_t *) one;
    second = (ngx_mail_conf_addr_t *) two;

    if (first->opt.wildcard) {
        /* a wildcard must be the last resort, shift it to the end */
        return 1;
    }

    if (second->opt.wildcard) {
        /* a wildcard must be the last resort, shift it to the end */
<|edits_diff|>
--- src/mail/ngx_mail.c
+++ src/mail/ngx_mail.c
@@ -20,6 +20,7 @@
 #if (NGX_MAIL_SSL)
         addrs[i].conf.ssl = addr[i].opt.ssl;
 #endif
+        addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;
         addrs[i].conf.addr_text = addr[i].opt.addr_text;
     }

<|current_version|>
{
    ngx_uint_t           i;
    ngx_mail_in_addr_t  *addrs;
    struct sockaddr_in  *sin;

    mport->addrs = ngx_pcalloc(cf->pool,
                               mport->naddrs * sizeof(ngx_mail_in_addr_t));
    if (mport->addrs == NULL) {
        return NGX_ERROR;
    }

    addrs = mport->addrs;

    for (i = 0; i < mport->naddrs; i++) {

        sin = (struct sockaddr_in *) addr[i].opt.sockaddr;
        addrs[i].addr = sin->sin_addr.s_addr;

        addrs[i].conf.ctx = addr[i].opt.ctx;
#if (NGX_MAIL_SSL)
        addrs[i].conf.ssl = addr[i].opt.ssl;
#endif
        addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;
        addrs[i].conf.addr_text = addr[i].opt.addr_text;
    }

    return NGX_OK;
}

#if (NGX_HAVE_INET6)

static ngx_int_t
ngx_mail_add_addrs6(ngx_conf_t *cf, ngx_mail_port_t *mport,
    ngx_mail_conf_addr_t *addr)
{
    ngx_uint_t            i;
    ngx_mail_in6_addr_t  *addrs6;
    struct sockaddr_in6  *sin6;

    mport->addrs = ngx_pcalloc(cf->pool,
                               mport->naddrs * sizeof(ngx_mail_in6_addr_t));
    if (mport->addrs == NULL) {
        return NGX_ERROR;
    }

    addrs6 = mport->addrs;

    for (i = 0; i < mport->naddrs; i++) {

        sin6 = (struct sockaddr_in6 *) addr[i].opt.sockaddr;
        addrs6[i].addr6 = sin6->sin6_addr;

        addrs6[i].conf.ctx = addr[i].opt.ctx;
#if (NGX_MAIL_SSL)
        addrs6[i].conf.ssl = addr[i].opt.ssl;
#endif
        addrs6[i].conf.addr_text = addr[i].opt.addr_text;
    }

    return NGX_OK;
}

#endif

static ngx_int_t
ngx_mail_cmp_conf_addrs(const void *one, const void *two)
{
    ngx_mail_conf_addr_t  *first, *second;

    first = (ngx_mail_conf_addr_t *) one;
    second = (ngx_mail_conf_addr_t *) two;

    if (first->opt.wildcard) {
        /* a wildcard must be the last resort, shift it to the end */
        return 1;
    }

    if (second->opt.wildcard) {
        /* a wildcard must be the last resort, shift it to the end */
<|next_version|>
{
    ngx_uint_t           i;
    ngx_mail_in_addr_t  *addrs;
    struct sockaddr_in  *sin;

    mport->addrs = ngx_pcalloc(cf->pool,
                               mport->naddrs * sizeof(ngx_mail_in_addr_t));
    if (mport->addrs == NULL) {
        return NGX_ERROR;
    }

    addrs = mport->addrs;

    for (i = 0; i < mport->naddrs; i++) {

        sin = (struct sockaddr_in *) addr[i].opt.sockaddr;
        addrs[i].addr = sin->sin_addr.s_addr;

        addrs[i].conf.ctx = addr[i].opt.ctx;
#if (NGX_MAIL_SSL)
        addrs[i].conf.ssl = addr[i].opt.ssl;
#endif
        addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;
        addrs[i].conf.addr_text = addr[i].opt.addr_text;
    }

    return NGX_OK;
}

#if (NGX_HAVE_INET6)

static ngx_int_t
ngx_mail_add_addrs6(ngx_conf_t *cf, ngx_mail_port_t *mport,
    ngx_mail_conf_addr_t *addr)
{
    ngx_uint_t            i;
    ngx_mail_in6_addr_t  *addrs6;
    struct sockaddr_in6  *sin6;

    mport->addrs = ngx_pcalloc(cf->pool,
                               mport->naddrs * sizeof(ngx_mail_in6_addr_t));
    if (mport->addrs == NULL) {
        return NGX_ERROR;
    }

    addrs6 = mport->addrs;

    for (i = 0; i < mport->naddrs; i++) {

        sin6 = (struct sockaddr_in6 *) addr[i].opt.sockaddr;
        addrs6[i].addr6 = sin6->sin6_addr;

        addrs6[i].conf.ctx = addr[i].opt.ctx;
#if (NGX_MAIL_SSL)
        addrs6[i].conf.ssl = addr[i].opt.ssl;
#endif
        addrs6[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;
        addrs6[i].conf.addr_text = addr[i].opt.addr_text;
    }

    return NGX_OK;
}

#endif

static ngx_int_t
ngx_mail_cmp_conf_addrs(const void *one, const void *two)
{
    ngx_mail_conf_addr_t  *first, *second;

    first = (ngx_mail_conf_addr_t *) one;
    second = (ngx_mail_conf_addr_t *) two;

    if (first->opt.wildcard) {
        /* a wildcard must be the last resort, shift it to the end */
        return 1;
    }

    if (second->opt.wildcard) {
        /* a wildcard must be the last resort, shift it to the end */


QUIC: fixed memory leak in ngx_quic_send_frames().

<|original_code|>

    q = ngx_queue_head(frames);
    start = ngx_queue_data(q, ngx_quic_frame_t, queue);

    ngx_memzero(&pkt, sizeof(ngx_quic_header_t));

    now = ngx_current_msec;

    p = src;
    out.data = src;

    for (q = ngx_queue_head(frames);
         q != ngx_queue_sentinel(frames);
         q = ngx_queue_next(q))
    {
        f = ngx_queue_data(q, ngx_quic_frame_t, queue);

        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
                       "quic frame out: %s", f->info);

        len = ngx_quic_create_frame(p, f);
        if (len == -1) {
            return NGX_ERROR;
        }

        if (f->need_ack) {
            pkt.need_ack = 1;
        }

        p += len;
        f->pnum = ctx->pnum;
        f->last = now;
        f->plen = 0;
    }

    out.len = p - out.data;

    while (out.len < 4) {
        *p++ = NGX_QUIC_FT_PADDING;
        out.len++;
    }

    qc = c->quic;

    keys = &c->quic->keys[start->level];

    pkt.secret = &keys->server;

    pkt.flags = NGX_QUIC_PKT_FIXED_BIT;

    if (start->level == ssl_encryption_initial) {
        pkt.flags |= NGX_QUIC_PKT_LONG | NGX_QUIC_PKT_INITIAL;
        pkt.token = initial_token;

    } else if (start->level == ssl_encryption_handshake) {
        pkt.flags |= NGX_QUIC_PKT_LONG | NGX_QUIC_PKT_HANDSHAKE;

    } else {
        if (c->quic->key_phase) {
            pkt.flags |= NGX_QUIC_PKT_KPHASE;
        }
    }

    ngx_quic_set_packet_number(&pkt, ctx);

    pkt.log = c->log;
    pkt.level = start->level;
    pkt.dcid = qc->scid;
    pkt.scid = qc->dcid;
    pkt.payload = out;

    res.data = dst;

    ngx_log_debug6(NGX_LOG_DEBUG_EVENT, c->log, 0,
                   "quic packet ready: %ui bytes at level %d"
                   " need_ack: %d number: %L encoded %d:0x%xD",
                   out.len, start->level, pkt.need_ack, pkt.number,
                   pkt.num_len, pkt.trunc);

    if (ngx_quic_encrypt(&pkt, ssl_conn, &res) != NGX_OK) {
        return NGX_ERROR;
    }

    len = c->send(c, res.data, res.len);
    if (len == NGX_ERROR || (size_t) len != res.len) {
        return NGX_ERROR;
    }

    /* len == NGX_OK || NGX_AGAIN */
    ctx->pnum++;

    if (pkt.need_ack) {
        /* move frames into the sent queue to wait for ack */

        if (qc->closing) {
            /* if we are closing, any ack will be discarded */
            ngx_quic_free_frames(c, frames);

        } else {
            ngx_queue_add(&ctx->sent, frames);
            if (qc->pto.timer_set) {
                ngx_del_timer(&qc->pto);
            }
            ngx_add_timer(&qc->pto, ngx_quic_pto(c, ctx));

            start->plen = len;
        }

        qc->congestion.in_flight += len;
<|edits_diff|>
--- src/event/ngx_event_quic.c
+++ src/event/ngx_event_quic.c
@@ -20,6 +20,7 @@

         len = ngx_quic_create_frame(p, f);
         if (len == -1) {
+            ngx_quic_free_frames(c, frames);
             return NGX_ERROR;
         }

@@ -78,6 +79,7 @@
                    pkt.num_len, pkt.trunc);

     if (ngx_quic_encrypt(&pkt, ssl_conn, &res) != NGX_OK) {
+        ngx_quic_free_frames(c, frames);
         return NGX_ERROR;
     }

<|current_version|>

    q = ngx_queue_head(frames);
    start = ngx_queue_data(q, ngx_quic_frame_t, queue);

    ngx_memzero(&pkt, sizeof(ngx_quic_header_t));

    now = ngx_current_msec;

    p = src;
    out.data = src;

    for (q = ngx_queue_head(frames);
         q != ngx_queue_sentinel(frames);
         q = ngx_queue_next(q))
    {
        f = ngx_queue_data(q, ngx_quic_frame_t, queue);

        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
                       "quic frame out: %s", f->info);

        len = ngx_quic_create_frame(p, f);
        if (len == -1) {
            ngx_quic_free_frames(c, frames);
            return NGX_ERROR;
        }

        if (f->need_ack) {
            pkt.need_ack = 1;
        }

        p += len;
        f->pnum = ctx->pnum;
        f->last = now;
        f->plen = 0;
    }

    out.len = p - out.data;

    while (out.len < 4) {
        *p++ = NGX_QUIC_FT_PADDING;
        out.len++;
    }

    qc = c->quic;

    keys = &c->quic->keys[start->level];

    pkt.secret = &keys->server;

    pkt.flags = NGX_QUIC_PKT_FIXED_BIT;

    if (start->level == ssl_encryption_initial) {
        pkt.flags |= NGX_QUIC_PKT_LONG | NGX_QUIC_PKT_INITIAL;
        pkt.token = initial_token;

    } else if (start->level == ssl_encryption_handshake) {
        pkt.flags |= NGX_QUIC_PKT_LONG | NGX_QUIC_PKT_HANDSHAKE;

    } else {
        if (c->quic->key_phase) {
            pkt.flags |= NGX_QUIC_PKT_KPHASE;
        }
    }

    ngx_quic_set_packet_number(&pkt, ctx);

    pkt.log = c->log;
    pkt.level = start->level;
    pkt.dcid = qc->scid;
    pkt.scid = qc->dcid;
    pkt.payload = out;

    res.data = dst;

    ngx_log_debug6(NGX_LOG_DEBUG_EVENT, c->log, 0,
                   "quic packet ready: %ui bytes at level %d"
                   " need_ack: %d number: %L encoded %d:0x%xD",
                   out.len, start->level, pkt.need_ack, pkt.number,
                   pkt.num_len, pkt.trunc);

    if (ngx_quic_encrypt(&pkt, ssl_conn, &res) != NGX_OK) {
        ngx_quic_free_frames(c, frames);
        return NGX_ERROR;
    }

    len = c->send(c, res.data, res.len);
    if (len == NGX_ERROR || (size_t) len != res.len) {
        return NGX_ERROR;
    }

    /* len == NGX_OK || NGX_AGAIN */
    ctx->pnum++;

    if (pkt.need_ack) {
        /* move frames into the sent queue to wait for ack */

        if (qc->closing) {
            /* if we are closing, any ack will be discarded */
            ngx_quic_free_frames(c, frames);

        } else {
            ngx_queue_add(&ctx->sent, frames);
            if (qc->pto.timer_set) {
                ngx_del_timer(&qc->pto);
            }
            ngx_add_timer(&qc->pto, ngx_quic_pto(c, ctx));

            start->plen = len;
        }

        qc->congestion.in_flight += len;
<|next_version|>

    q = ngx_queue_head(frames);
    start = ngx_queue_data(q, ngx_quic_frame_t, queue);

    ngx_memzero(&pkt, sizeof(ngx_quic_header_t));

    now = ngx_current_msec;

    p = src;
    out.data = src;

    for (q = ngx_queue_head(frames);
         q != ngx_queue_sentinel(frames);
         q = ngx_queue_next(q))
    {
        f = ngx_queue_data(q, ngx_quic_frame_t, queue);

        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
                       "quic frame out: %s", f->info);

        len = ngx_quic_create_frame(p, f);
        if (len == -1) {
            ngx_quic_free_frames(c, frames);
            return NGX_ERROR;
        }

        if (f->need_ack) {
            pkt.need_ack = 1;
        }

        p += len;
        f->pnum = ctx->pnum;
        f->last = now;
        f->plen = 0;
    }

    out.len = p - out.data;

    while (out.len < 4) {
        *p++ = NGX_QUIC_FT_PADDING;
        out.len++;
    }

    qc = c->quic;

    keys = &c->quic->keys[start->level];

    pkt.secret = &keys->server;

    pkt.flags = NGX_QUIC_PKT_FIXED_BIT;

    if (start->level == ssl_encryption_initial) {
        pkt.flags |= NGX_QUIC_PKT_LONG | NGX_QUIC_PKT_INITIAL;
        pkt.token = initial_token;

    } else if (start->level == ssl_encryption_handshake) {
        pkt.flags |= NGX_QUIC_PKT_LONG | NGX_QUIC_PKT_HANDSHAKE;

    } else {
        if (c->quic->key_phase) {
            pkt.flags |= NGX_QUIC_PKT_KPHASE;
        }
    }

    ngx_quic_set_packet_number(&pkt, ctx);

    pkt.log = c->log;
    pkt.level = start->level;
    pkt.dcid = qc->scid;
    pkt.scid = qc->dcid;
    pkt.payload = out;

    res.data = dst;

    ngx_log_debug6(NGX_LOG_DEBUG_EVENT, c->log, 0,
                   "quic packet ready: %ui bytes at level %d"
                   " need_ack: %d number: %L encoded %d:0x%xD",
                   out.len, start->level, pkt.need_ack, pkt.number,
                   pkt.num_len, pkt.trunc);

    if (ngx_quic_encrypt(&pkt, ssl_conn, &res) != NGX_OK) {
        ngx_quic_free_frames(c, frames);
        return NGX_ERROR;
    }

    len = c->send(c, res.data, res.len);
    if (len == NGX_ERROR || (size_t) len != res.len) {
        ngx_quic_free_frames(c, frames);
        return NGX_ERROR;
    }

    /* len == NGX_OK || NGX_AGAIN */
    ctx->pnum++;

    if (pkt.need_ack) {
        /* move frames into the sent queue to wait for ack */

        if (qc->closing) {
            /* if we are closing, any ack will be discarded */
            ngx_quic_free_frames(c, frames);

        } else {
            ngx_queue_add(&ctx->sent, frames);
            if (qc->pto.timer_set) {
                ngx_del_timer(&qc->pto);
            }
            ngx_add_timer(&qc->pto, ngx_quic_pto(c, ctx));

            start->plen = len;
        }

        qc->congestion.in_flight += len;


QUIC: added "quic" listen parameter in Stream.

<|original_code|>
{
    ngx_uint_t             i;
    struct sockaddr_in    *sin;
    ngx_stream_in_addr_t  *addrs;

    stport->addrs = ngx_pcalloc(cf->pool,
                                stport->naddrs * sizeof(ngx_stream_in_addr_t));
    if (stport->addrs == NULL) {
        return NGX_ERROR;
    }

    addrs = stport->addrs;

    for (i = 0; i < stport->naddrs; i++) {

        sin = (struct sockaddr_in *) addr[i].opt.sockaddr;
        addrs[i].addr = sin->sin_addr.s_addr;

        addrs[i].conf.ctx = addr[i].opt.ctx;
#if (NGX_STREAM_SSL)
        addrs[i].conf.ssl = addr[i].opt.ssl;
#endif
        addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;
        addrs[i].conf.addr_text = addr[i].opt.addr_text;
    }

    return NGX_OK;
}

#if (NGX_HAVE_INET6)

static ngx_int_t
ngx_stream_add_addrs6(ngx_conf_t *cf, ngx_stream_port_t *stport,
    ngx_stream_conf_addr_t *addr)
{
    ngx_uint_t              i;
    struct sockaddr_in6    *sin6;
    ngx_stream_in6_addr_t  *addrs6;

    stport->addrs = ngx_pcalloc(cf->pool,
                                stport->naddrs * sizeof(ngx_stream_in6_addr_t));
    if (stport->addrs == NULL) {
        return NGX_ERROR;
    }

    addrs6 = stport->addrs;

    for (i = 0; i < stport->naddrs; i++) {

        sin6 = (struct sockaddr_in6 *) addr[i].opt.sockaddr;
        addrs6[i].addr6 = sin6->sin6_addr;

        addrs6[i].conf.ctx = addr[i].opt.ctx;
#if (NGX_STREAM_SSL)
        addrs6[i].conf.ssl = addr[i].opt.ssl;
#endif
        addrs6[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;
        addrs6[i].conf.addr_text = addr[i].opt.addr_text;
    }

    return NGX_OK;
}

#endif

static ngx_int_t
ngx_stream_cmp_conf_addrs(const void *one, const void *two)
{
    ngx_stream_conf_addr_t  *first, *second;

    first = (ngx_stream_conf_addr_t *) one;
    second = (ngx_stream_conf_addr_t *) two;

    if (first->opt.wildcard) {
        /* a wildcard must be the last resort, shift it to the end */
        return 1;
    }

    if (second->opt.wildcard) {
<|edits_diff|>
--- src/stream/ngx_stream.c
+++ src/stream/ngx_stream.c
@@ -19,6 +19,9 @@
         addrs[i].conf.ctx = addr[i].opt.ctx;
 #if (NGX_STREAM_SSL)
         addrs[i].conf.ssl = addr[i].opt.ssl;
+#endif
+#if (NGX_STREAM_QUIC)
+        addrs[i].conf.quic = addr[i].opt.quic;
 #endif
         addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;
         addrs[i].conf.addr_text = addr[i].opt.addr_text;
<|current_version|>
{
    ngx_uint_t             i;
    struct sockaddr_in    *sin;
    ngx_stream_in_addr_t  *addrs;

    stport->addrs = ngx_pcalloc(cf->pool,
                                stport->naddrs * sizeof(ngx_stream_in_addr_t));
    if (stport->addrs == NULL) {
        return NGX_ERROR;
    }

    addrs = stport->addrs;

    for (i = 0; i < stport->naddrs; i++) {

        sin = (struct sockaddr_in *) addr[i].opt.sockaddr;
        addrs[i].addr = sin->sin_addr.s_addr;

        addrs[i].conf.ctx = addr[i].opt.ctx;
#if (NGX_STREAM_SSL)
        addrs[i].conf.ssl = addr[i].opt.ssl;
#endif
#if (NGX_STREAM_QUIC)
        addrs[i].conf.quic = addr[i].opt.quic;
#endif
        addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;
        addrs[i].conf.addr_text = addr[i].opt.addr_text;
    }

    return NGX_OK;
}

#if (NGX_HAVE_INET6)

static ngx_int_t
ngx_stream_add_addrs6(ngx_conf_t *cf, ngx_stream_port_t *stport,
    ngx_stream_conf_addr_t *addr)
{
    ngx_uint_t              i;
    struct sockaddr_in6    *sin6;
    ngx_stream_in6_addr_t  *addrs6;

    stport->addrs = ngx_pcalloc(cf->pool,
                                stport->naddrs * sizeof(ngx_stream_in6_addr_t));
    if (stport->addrs == NULL) {
        return NGX_ERROR;
    }

    addrs6 = stport->addrs;

    for (i = 0; i < stport->naddrs; i++) {

        sin6 = (struct sockaddr_in6 *) addr[i].opt.sockaddr;
        addrs6[i].addr6 = sin6->sin6_addr;

        addrs6[i].conf.ctx = addr[i].opt.ctx;
#if (NGX_STREAM_SSL)
        addrs6[i].conf.ssl = addr[i].opt.ssl;
#endif
        addrs6[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;
        addrs6[i].conf.addr_text = addr[i].opt.addr_text;
    }

    return NGX_OK;
}

#endif

static ngx_int_t
ngx_stream_cmp_conf_addrs(const void *one, const void *two)
{
    ngx_stream_conf_addr_t  *first, *second;

    first = (ngx_stream_conf_addr_t *) one;
    second = (ngx_stream_conf_addr_t *) two;

    if (first->opt.wildcard) {
        /* a wildcard must be the last resort, shift it to the end */
        return 1;
    }

    if (second->opt.wildcard) {
<|next_version|>
{
    ngx_uint_t             i;
    struct sockaddr_in    *sin;
    ngx_stream_in_addr_t  *addrs;

    stport->addrs = ngx_pcalloc(cf->pool,
                                stport->naddrs * sizeof(ngx_stream_in_addr_t));
    if (stport->addrs == NULL) {
        return NGX_ERROR;
    }

    addrs = stport->addrs;

    for (i = 0; i < stport->naddrs; i++) {

        sin = (struct sockaddr_in *) addr[i].opt.sockaddr;
        addrs[i].addr = sin->sin_addr.s_addr;

        addrs[i].conf.ctx = addr[i].opt.ctx;
#if (NGX_STREAM_SSL)
        addrs[i].conf.ssl = addr[i].opt.ssl;
#endif
#if (NGX_STREAM_QUIC)
        addrs[i].conf.quic = addr[i].opt.quic;
#endif
        addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;
        addrs[i].conf.addr_text = addr[i].opt.addr_text;
    }

    return NGX_OK;
}

#if (NGX_HAVE_INET6)

static ngx_int_t
ngx_stream_add_addrs6(ngx_conf_t *cf, ngx_stream_port_t *stport,
    ngx_stream_conf_addr_t *addr)
{
    ngx_uint_t              i;
    struct sockaddr_in6    *sin6;
    ngx_stream_in6_addr_t  *addrs6;

    stport->addrs = ngx_pcalloc(cf->pool,
                                stport->naddrs * sizeof(ngx_stream_in6_addr_t));
    if (stport->addrs == NULL) {
        return NGX_ERROR;
    }

    addrs6 = stport->addrs;

    for (i = 0; i < stport->naddrs; i++) {

        sin6 = (struct sockaddr_in6 *) addr[i].opt.sockaddr;
        addrs6[i].addr6 = sin6->sin6_addr;

        addrs6[i].conf.ctx = addr[i].opt.ctx;
#if (NGX_STREAM_SSL)
        addrs6[i].conf.ssl = addr[i].opt.ssl;
#endif
#if (NGX_STREAM_QUIC)
        addrs6[i].conf.quic = addr[i].opt.quic;
#endif
        addrs6[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;
        addrs6[i].conf.addr_text = addr[i].opt.addr_text;
    }

    return NGX_OK;
}

#endif

static ngx_int_t
ngx_stream_cmp_conf_addrs(const void *one, const void *two)
{
    ngx_stream_conf_addr_t  *first, *second;

    first = (ngx_stream_conf_addr_t *) one;
    second = (ngx_stream_conf_addr_t *) two;

    if (first->opt.wildcard) {
        /* a wildcard must be the last resort, shift it to the end */
        return 1;
    }

    if (second->opt.wildcard) {


Status: introduced the "ngx_stat_waiting" counter.

<|original_code|>
            }

        } else {
            level = NGX_LOG_CRIT;
        }

        /* we use ngx_cycle->log because c->log was in c->pool */

        ngx_log_error(level, ngx_cycle->log, err,
                      ngx_close_socket_n " %d failed", fd);
    }
}

void
ngx_reusable_connection(ngx_connection_t *c, ngx_uint_t reusable)
{
    ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
                   "reusable connection: %ui", reusable);

    if (c->reusable) {
        ngx_queue_remove(&c->queue);
    }

    c->reusable = reusable;

    if (reusable) {
        /* need cast as ngx_cycle is volatile */

        ngx_queue_insert_head(
            (ngx_queue_t *) &ngx_cycle->reusable_connections_queue, &c->queue);
    }
}

static void
ngx_drain_connections(void)
{
    ngx_int_t          i;
    ngx_queue_t       *q;
    ngx_connection_t  *c;

    for (i = 0; i < 32; i++) {
        if (ngx_queue_empty(&ngx_cycle->reusable_connections_queue)) {
            break;
        }

        q = ngx_queue_last(&ngx_cycle->reusable_connections_queue);
        c = ngx_queue_data(q, ngx_connection_t, queue);

        ngx_log_debug0(NGX_LOG_DEBUG_CORE, c->log, 0,
                       "reusing connection");

        c->close = 1;
        c->read->handler(c->read);
<|edits_diff|>
--- src/core/ngx_connection.c
+++ src/core/ngx_connection.c
@@ -20,6 +20,10 @@

     if (c->reusable) {
         ngx_queue_remove(&c->queue);
+
+#if (NGX_STAT_STUB)
+        (void) ngx_atomic_fetch_add(ngx_stat_waiting, -1);
+#endif
     }

     c->reusable = reusable;
<|current_version|>
            }

        } else {
            level = NGX_LOG_CRIT;
        }

        /* we use ngx_cycle->log because c->log was in c->pool */

        ngx_log_error(level, ngx_cycle->log, err,
                      ngx_close_socket_n " %d failed", fd);
    }
}

void
ngx_reusable_connection(ngx_connection_t *c, ngx_uint_t reusable)
{
    ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
                   "reusable connection: %ui", reusable);

    if (c->reusable) {
        ngx_queue_remove(&c->queue);

#if (NGX_STAT_STUB)
        (void) ngx_atomic_fetch_add(ngx_stat_waiting, -1);
#endif
    }

    c->reusable = reusable;

    if (reusable) {
        /* need cast as ngx_cycle is volatile */

        ngx_queue_insert_head(
            (ngx_queue_t *) &ngx_cycle->reusable_connections_queue, &c->queue);
    }
}

static void
ngx_drain_connections(void)
{
    ngx_int_t          i;
    ngx_queue_t       *q;
    ngx_connection_t  *c;

    for (i = 0; i < 32; i++) {
        if (ngx_queue_empty(&ngx_cycle->reusable_connections_queue)) {
            break;
        }

        q = ngx_queue_last(&ngx_cycle->reusable_connections_queue);
        c = ngx_queue_data(q, ngx_connection_t, queue);

        ngx_log_debug0(NGX_LOG_DEBUG_CORE, c->log, 0,
                       "reusing connection");

        c->close = 1;
        c->read->handler(c->read);
<|next_version|>
            }

        } else {
            level = NGX_LOG_CRIT;
        }

        /* we use ngx_cycle->log because c->log was in c->pool */

        ngx_log_error(level, ngx_cycle->log, err,
                      ngx_close_socket_n " %d failed", fd);
    }
}

void
ngx_reusable_connection(ngx_connection_t *c, ngx_uint_t reusable)
{
    ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
                   "reusable connection: %ui", reusable);

    if (c->reusable) {
        ngx_queue_remove(&c->queue);

#if (NGX_STAT_STUB)
        (void) ngx_atomic_fetch_add(ngx_stat_waiting, -1);
#endif
    }

    c->reusable = reusable;

    if (reusable) {
        /* need cast as ngx_cycle is volatile */

        ngx_queue_insert_head(
            (ngx_queue_t *) &ngx_cycle->reusable_connections_queue, &c->queue);

#if (NGX_STAT_STUB)
        (void) ngx_atomic_fetch_add(ngx_stat_waiting, 1);
#endif
    }
}

static void
ngx_drain_connections(void)
{
    ngx_int_t          i;
    ngx_queue_t       *q;
    ngx_connection_t  *c;

    for (i = 0; i < 32; i++) {
        if (ngx_queue_empty(&ngx_cycle->reusable_connections_queue)) {
            break;
        }

        q = ngx_queue_last(&ngx_cycle->reusable_connections_queue);
        c = ngx_queue_data(q, ngx_connection_t, queue);

        ngx_log_debug0(NGX_LOG_DEBUG_CORE, c->log, 0,
                       "reusing connection");

        c->close = 1;
        c->read->handler(c->read);


Added escaping of double quotes in ngx_escape_html().

<|original_code|>
    u_char      ch;
    ngx_uint_t  len;

    if (dst == NULL) {

        len = 0;

        while (size) {
            switch (*src++) {

            case '<':
                len += sizeof("<") - 2;
                break;

            case '>':
                len += sizeof(">") - 2;
                break;

            case '&':
                len += sizeof("&") - 2;
                break;

            default:
                break;
            }
            size--;
        }

        return (uintptr_t) len;
    }

    while (size) {
        ch = *src++;

        switch (ch) {

        case '<':
            *dst++ = '&'; *dst++ = 'l'; *dst++ = 't'; *dst++ = ';';
            break;

        case '>':
            *dst++ = '&'; *dst++ = 'g'; *dst++ = 't'; *dst++ = ';';
            break;

        case '&':
            *dst++ = '&'; *dst++ = 'a'; *dst++ = 'm'; *dst++ = 'p';
            *dst++ = ';';
            break;

        default:
            *dst++ = ch;
            break;
        }
        size--;
    }

    return (uintptr_t) dst;
}

void
ngx_str_rbtree_insert_value(ngx_rbtree_node_t *temp,
    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
{
    ngx_str_node_t      *n, *t;
    ngx_rbtree_node_t  **p;

    for ( ;; ) {

        n = (ngx_str_node_t *) node;
        t = (ngx_str_node_t *) temp;
<|edits_diff|>
--- src/core/ngx_string.c
+++ src/core/ngx_string.c
@@ -18,6 +18,10 @@

             case '&':
                 len += sizeof("&") - 2;
+                break;
+
+            case '"':
+                len += sizeof(""") - 2;
                 break;

             default:
<|current_version|>
    u_char      ch;
    ngx_uint_t  len;

    if (dst == NULL) {

        len = 0;

        while (size) {
            switch (*src++) {

            case '<':
                len += sizeof("<") - 2;
                break;

            case '>':
                len += sizeof(">") - 2;
                break;

            case '&':
                len += sizeof("&") - 2;
                break;

            case '"':
                len += sizeof(""") - 2;
                break;

            default:
                break;
            }
            size--;
        }

        return (uintptr_t) len;
    }

    while (size) {
        ch = *src++;

        switch (ch) {

        case '<':
            *dst++ = '&'; *dst++ = 'l'; *dst++ = 't'; *dst++ = ';';
            break;

        case '>':
            *dst++ = '&'; *dst++ = 'g'; *dst++ = 't'; *dst++ = ';';
            break;

        case '&':
            *dst++ = '&'; *dst++ = 'a'; *dst++ = 'm'; *dst++ = 'p';
            *dst++ = ';';
            break;

        default:
            *dst++ = ch;
            break;
        }
        size--;
    }

    return (uintptr_t) dst;
}

void
ngx_str_rbtree_insert_value(ngx_rbtree_node_t *temp,
    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
{
    ngx_str_node_t      *n, *t;
    ngx_rbtree_node_t  **p;

    for ( ;; ) {

        n = (ngx_str_node_t *) node;
        t = (ngx_str_node_t *) temp;
<|next_version|>
    u_char      ch;
    ngx_uint_t  len;

    if (dst == NULL) {

        len = 0;

        while (size) {
            switch (*src++) {

            case '<':
                len += sizeof("<") - 2;
                break;

            case '>':
                len += sizeof(">") - 2;
                break;

            case '&':
                len += sizeof("&") - 2;
                break;

            case '"':
                len += sizeof(""") - 2;
                break;

            default:
                break;
            }
            size--;
        }

        return (uintptr_t) len;
    }

    while (size) {
        ch = *src++;

        switch (ch) {

        case '<':
            *dst++ = '&'; *dst++ = 'l'; *dst++ = 't'; *dst++ = ';';
            break;

        case '>':
            *dst++ = '&'; *dst++ = 'g'; *dst++ = 't'; *dst++ = ';';
            break;

        case '&':
            *dst++ = '&'; *dst++ = 'a'; *dst++ = 'm'; *dst++ = 'p';
            *dst++ = ';';
            break;

        case '"':
            *dst++ = '&'; *dst++ = 'q'; *dst++ = 'u'; *dst++ = 'o';
            *dst++ = 't'; *dst++ = ';';
            break;

        default:
            *dst++ = ch;
            break;
        }
        size--;
    }

    return (uintptr_t) dst;
}

void
ngx_str_rbtree_insert_value(ngx_rbtree_node_t *temp,
    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
{
    ngx_str_node_t      *n, *t;
    ngx_rbtree_node_t  **p;

    for ( ;; ) {

        n = (ngx_str_node_t *) node;
        t = (ngx_str_node_t *) temp;


nginx-0.0.1-2002-12-15-09:25:09 import

<|original_code|>

#include <ngx_http.h>

extern ngx_http_module_t ngx_http_write_filter_module;
extern ngx_http_module_t ngx_http_output_filter_module;
extern ngx_http_module_t ngx_http_core_module;
extern ngx_http_module_t ngx_http_index_module;

ngx_http_module_t *ngx_http_modules[] = {
    &ngx_http_write_filter_module,
    &ngx_http_output_filter_module,

    &ngx_http_index_module,
    &ngx_http_core_module,

    NULL
};

<|edits_diff|>
--- src/http/ngx_http_modules.c
+++ src/http/ngx_http_modules.c
@@ -1,8 +1,11 @@

 #include <ngx_http.h>

+extern ngx_http_module_t ngx_http_header_filter_module;
+
 extern ngx_http_module_t ngx_http_write_filter_module;
 extern ngx_http_module_t ngx_http_output_filter_module;
+
 extern ngx_http_module_t ngx_http_core_module;
 extern ngx_http_module_t ngx_http_index_module;

<|current_version|>

#include <ngx_http.h>

extern ngx_http_module_t ngx_http_header_filter_module;

extern ngx_http_module_t ngx_http_write_filter_module;
extern ngx_http_module_t ngx_http_output_filter_module;

extern ngx_http_module_t ngx_http_core_module;
extern ngx_http_module_t ngx_http_index_module;

ngx_http_module_t *ngx_http_modules[] = {
    &ngx_http_write_filter_module,
    &ngx_http_output_filter_module,

    &ngx_http_index_module,
    &ngx_http_core_module,

    NULL
};

<|next_version|>

#include <ngx_http.h>

extern ngx_http_module_t ngx_http_header_filter_module;

extern ngx_http_module_t ngx_http_write_filter_module;
extern ngx_http_module_t ngx_http_output_filter_module;

extern ngx_http_module_t ngx_http_core_module;
extern ngx_http_module_t ngx_http_index_module;

ngx_http_module_t *ngx_http_modules[] = {

    &ngx_http_header_filter_module,

    &ngx_http_write_filter_module,
    &ngx_http_output_filter_module,

    &ngx_http_index_module,
    &ngx_http_core_module,

    NULL
};


Add no-detach-on-destroy client option (useful for control mode clients). From laur dot aliste at gmail dot com, GitHub issue 4242.

<|original_code|>
server_client_set_flags(struct client *c, const char *flags)
{
	char	*s, *copy, *next;
	uint64_t flag;
	int	 not;

	s = copy = xstrdup(flags);
	while ((next = strsep(&s, ",")) != NULL) {
		not = (*next == '!');
		if (not)
			next++;

		if (c->flags & CLIENT_CONTROL)
			flag = server_client_control_flags(c, next);
		else
			flag = 0;
		if (strcmp(next, "read-only") == 0)
			flag = CLIENT_READONLY;
		else if (strcmp(next, "ignore-size") == 0)
			flag = CLIENT_IGNORESIZE;
		else if (strcmp(next, "active-pane") == 0)
			flag = CLIENT_ACTIVEPANE;
		if (flag == 0)
			continue;

		log_debug("client %s set flag %s", c->name, next);
		if (not) {
			if (c->flags & CLIENT_READONLY)
				flag &= ~CLIENT_READONLY;
			c->flags &= ~flag;
		} else
			c->flags |= flag;
		if (flag == CLIENT_CONTROL_NOOUTPUT)
			control_reset_offsets(c);
	}
	free(copy);
	proc_send(c->peer, MSG_FLAGS, -1, &c->flags, sizeof c->flags);
}

/* Get client flags. This is only flags useful to show to users. */
const char *
server_client_get_flags(struct client *c)
{
	static char	s[256];
	char	 	tmp[32];

	*s = '\0';
	if (c->flags & CLIENT_ATTACHED)
		strlcat(s, "attached,", sizeof s);
	if (c->flags & CLIENT_FOCUSED)
		strlcat(s, "focused,", sizeof s);
	if (c->flags & CLIENT_CONTROL)
		strlcat(s, "control-mode,", sizeof s);
	if (c->flags & CLIENT_IGNORESIZE)
		strlcat(s, "ignore-size,", sizeof s);
	if (c->flags & CLIENT_CONTROL_NOOUTPUT)
		strlcat(s, "no-output,", sizeof s);
	if (c->flags & CLIENT_CONTROL_WAITEXIT)
		strlcat(s, "wait-exit,", sizeof s);
	if (c->flags & CLIENT_CONTROL_PAUSEAFTER) {
		xsnprintf(tmp, sizeof tmp, "pause-after=%u,",
		    c->pause_age / 1000);
		strlcat(s, tmp, sizeof s);
	}
	if (c->flags & CLIENT_READONLY)
		strlcat(s, "read-only,", sizeof s);
	if (c->flags & CLIENT_ACTIVEPANE)
		strlcat(s, "active-pane,", sizeof s);
	if (c->flags & CLIENT_SUSPENDED)
		strlcat(s, "suspended,", sizeof s);
	if (c->flags & CLIENT_UTF8)
		strlcat(s, "UTF-8,", sizeof s);
	if (*s != '\0')
		s[strlen(s) - 1] = '\0';
	return (s);
}

/* Get client window. */
struct client_window *
<|edits_diff|>
--- server-client.c
+++ server-client.c
@@ -20,6 +20,8 @@
 			flag = CLIENT_IGNORESIZE;
 		else if (strcmp(next, "active-pane") == 0)
 			flag = CLIENT_ACTIVEPANE;
+		else if (strcmp(next, "no-detach-on-destroy") == 0)
+			flag = CLIENT_NO_DETACH_ON_DESTROY;
 		if (flag == 0)
 			continue;

<|current_version|>
server_client_set_flags(struct client *c, const char *flags)
{
	char	*s, *copy, *next;
	uint64_t flag;
	int	 not;

	s = copy = xstrdup(flags);
	while ((next = strsep(&s, ",")) != NULL) {
		not = (*next == '!');
		if (not)
			next++;

		if (c->flags & CLIENT_CONTROL)
			flag = server_client_control_flags(c, next);
		else
			flag = 0;
		if (strcmp(next, "read-only") == 0)
			flag = CLIENT_READONLY;
		else if (strcmp(next, "ignore-size") == 0)
			flag = CLIENT_IGNORESIZE;
		else if (strcmp(next, "active-pane") == 0)
			flag = CLIENT_ACTIVEPANE;
		else if (strcmp(next, "no-detach-on-destroy") == 0)
			flag = CLIENT_NO_DETACH_ON_DESTROY;
		if (flag == 0)
			continue;

		log_debug("client %s set flag %s", c->name, next);
		if (not) {
			if (c->flags & CLIENT_READONLY)
				flag &= ~CLIENT_READONLY;
			c->flags &= ~flag;
		} else
			c->flags |= flag;
		if (flag == CLIENT_CONTROL_NOOUTPUT)
			control_reset_offsets(c);
	}
	free(copy);
	proc_send(c->peer, MSG_FLAGS, -1, &c->flags, sizeof c->flags);
}

/* Get client flags. This is only flags useful to show to users. */
const char *
server_client_get_flags(struct client *c)
{
	static char	s[256];
	char	 	tmp[32];

	*s = '\0';
	if (c->flags & CLIENT_ATTACHED)
		strlcat(s, "attached,", sizeof s);
	if (c->flags & CLIENT_FOCUSED)
		strlcat(s, "focused,", sizeof s);
	if (c->flags & CLIENT_CONTROL)
		strlcat(s, "control-mode,", sizeof s);
	if (c->flags & CLIENT_IGNORESIZE)
		strlcat(s, "ignore-size,", sizeof s);
	if (c->flags & CLIENT_CONTROL_NOOUTPUT)
		strlcat(s, "no-output,", sizeof s);
	if (c->flags & CLIENT_CONTROL_WAITEXIT)
		strlcat(s, "wait-exit,", sizeof s);
	if (c->flags & CLIENT_CONTROL_PAUSEAFTER) {
		xsnprintf(tmp, sizeof tmp, "pause-after=%u,",
		    c->pause_age / 1000);
		strlcat(s, tmp, sizeof s);
	}
	if (c->flags & CLIENT_READONLY)
		strlcat(s, "read-only,", sizeof s);
	if (c->flags & CLIENT_ACTIVEPANE)
		strlcat(s, "active-pane,", sizeof s);
	if (c->flags & CLIENT_SUSPENDED)
		strlcat(s, "suspended,", sizeof s);
	if (c->flags & CLIENT_UTF8)
		strlcat(s, "UTF-8,", sizeof s);
	if (*s != '\0')
		s[strlen(s) - 1] = '\0';
	return (s);
}

/* Get client window. */
struct client_window *
<|next_version|>
server_client_set_flags(struct client *c, const char *flags)
{
	char	*s, *copy, *next;
	uint64_t flag;
	int	 not;

	s = copy = xstrdup(flags);
	while ((next = strsep(&s, ",")) != NULL) {
		not = (*next == '!');
		if (not)
			next++;

		if (c->flags & CLIENT_CONTROL)
			flag = server_client_control_flags(c, next);
		else
			flag = 0;
		if (strcmp(next, "read-only") == 0)
			flag = CLIENT_READONLY;
		else if (strcmp(next, "ignore-size") == 0)
			flag = CLIENT_IGNORESIZE;
		else if (strcmp(next, "active-pane") == 0)
			flag = CLIENT_ACTIVEPANE;
		else if (strcmp(next, "no-detach-on-destroy") == 0)
			flag = CLIENT_NO_DETACH_ON_DESTROY;
		if (flag == 0)
			continue;

		log_debug("client %s set flag %s", c->name, next);
		if (not) {
			if (c->flags & CLIENT_READONLY)
				flag &= ~CLIENT_READONLY;
			c->flags &= ~flag;
		} else
			c->flags |= flag;
		if (flag == CLIENT_CONTROL_NOOUTPUT)
			control_reset_offsets(c);
	}
	free(copy);
	proc_send(c->peer, MSG_FLAGS, -1, &c->flags, sizeof c->flags);
}

/* Get client flags. This is only flags useful to show to users. */
const char *
server_client_get_flags(struct client *c)
{
	static char	s[256];
	char	 	tmp[32];

	*s = '\0';
	if (c->flags & CLIENT_ATTACHED)
		strlcat(s, "attached,", sizeof s);
	if (c->flags & CLIENT_FOCUSED)
		strlcat(s, "focused,", sizeof s);
	if (c->flags & CLIENT_CONTROL)
		strlcat(s, "control-mode,", sizeof s);
	if (c->flags & CLIENT_IGNORESIZE)
		strlcat(s, "ignore-size,", sizeof s);
	if (c->flags & CLIENT_NO_DETACH_ON_DESTROY)
		strlcat(s, "no-detach-on-destroy,", sizeof s);
	if (c->flags & CLIENT_CONTROL_NOOUTPUT)
		strlcat(s, "no-output,", sizeof s);
	if (c->flags & CLIENT_CONTROL_WAITEXIT)
		strlcat(s, "wait-exit,", sizeof s);
	if (c->flags & CLIENT_CONTROL_PAUSEAFTER) {
		xsnprintf(tmp, sizeof tmp, "pause-after=%u,",
		    c->pause_age / 1000);
		strlcat(s, tmp, sizeof s);
	}
	if (c->flags & CLIENT_READONLY)
		strlcat(s, "read-only,", sizeof s);
	if (c->flags & CLIENT_ACTIVEPANE)
		strlcat(s, "active-pane,", sizeof s);
	if (c->flags & CLIENT_SUSPENDED)
		strlcat(s, "suspended,", sizeof s);
	if (c->flags & CLIENT_UTF8)
		strlcat(s, "UTF-8,", sizeof s);
	if (*s != '\0')
		s[strlen(s) - 1] = '\0';
	return (s);
}

/* Get client window. */
struct client_window *


Turn off scrollbar when pane is in alternate screen, from Michael Grant, GitHub issue 4231.

<|original_code|>

	screen_write_initctx(ctx, &ttyctx, 0);
	ttyctx.ptr = str;
	ttyctx.num = len;
	ttyctx.allow_invisible_panes = allow_invisible_panes;

	tty_write(tty_cmd_rawstring, &ttyctx);
}

/* Turn alternate screen on. */
void
screen_write_alternateon(struct screen_write_ctx *ctx, struct grid_cell *gc,
    int cursor)
{
	struct tty_ctx		 ttyctx;
	struct window_pane	*wp = ctx->wp;

	if (wp != NULL && !options_get_number(wp->options, "alternate-screen"))
		return;

	screen_write_collect_flush(ctx, 0, __func__);
	screen_alternate_on(ctx->s, gc, cursor);

	screen_write_initctx(ctx, &ttyctx, 1);
	if (ttyctx.redraw_cb != NULL)
		ttyctx.redraw_cb(&ttyctx);
}

/* Turn alternate screen off. */
void
screen_write_alternateoff(struct screen_write_ctx *ctx, struct grid_cell *gc,
    int cursor)
{
	struct tty_ctx		 ttyctx;
	struct window_pane	*wp = ctx->wp;

	if (wp != NULL && !options_get_number(wp->options, "alternate-screen"))
		return;

	screen_write_collect_flush(ctx, 0, __func__);
	screen_alternate_off(ctx->s, gc, cursor);

	screen_write_initctx(ctx, &ttyctx, 1);
	if (ttyctx.redraw_cb != NULL)
		ttyctx.redraw_cb(&ttyctx);
}

<|edits_diff|>
--- screen-write.c
+++ screen-write.c
@@ -20,6 +20,7 @@

 	screen_write_collect_flush(ctx, 0, __func__);
 	screen_alternate_on(ctx->s, gc, cursor);
+	layout_fix_panes(wp->window, NULL);

 	screen_write_initctx(ctx, &ttyctx, 1);
 	if (ttyctx.redraw_cb != NULL)
<|current_version|>

	screen_write_initctx(ctx, &ttyctx, 0);
	ttyctx.ptr = str;
	ttyctx.num = len;
	ttyctx.allow_invisible_panes = allow_invisible_panes;

	tty_write(tty_cmd_rawstring, &ttyctx);
}

/* Turn alternate screen on. */
void
screen_write_alternateon(struct screen_write_ctx *ctx, struct grid_cell *gc,
    int cursor)
{
	struct tty_ctx		 ttyctx;
	struct window_pane	*wp = ctx->wp;

	if (wp != NULL && !options_get_number(wp->options, "alternate-screen"))
		return;

	screen_write_collect_flush(ctx, 0, __func__);
	screen_alternate_on(ctx->s, gc, cursor);
	layout_fix_panes(wp->window, NULL);

	screen_write_initctx(ctx, &ttyctx, 1);
	if (ttyctx.redraw_cb != NULL)
		ttyctx.redraw_cb(&ttyctx);
}

/* Turn alternate screen off. */
void
screen_write_alternateoff(struct screen_write_ctx *ctx, struct grid_cell *gc,
    int cursor)
{
	struct tty_ctx		 ttyctx;
	struct window_pane	*wp = ctx->wp;

	if (wp != NULL && !options_get_number(wp->options, "alternate-screen"))
		return;

	screen_write_collect_flush(ctx, 0, __func__);
	screen_alternate_off(ctx->s, gc, cursor);

	screen_write_initctx(ctx, &ttyctx, 1);
	if (ttyctx.redraw_cb != NULL)
		ttyctx.redraw_cb(&ttyctx);
}

<|next_version|>

	screen_write_initctx(ctx, &ttyctx, 0);
	ttyctx.ptr = str;
	ttyctx.num = len;
	ttyctx.allow_invisible_panes = allow_invisible_panes;

	tty_write(tty_cmd_rawstring, &ttyctx);
}

/* Turn alternate screen on. */
void
screen_write_alternateon(struct screen_write_ctx *ctx, struct grid_cell *gc,
    int cursor)
{
	struct tty_ctx		 ttyctx;
	struct window_pane	*wp = ctx->wp;

	if (wp != NULL && !options_get_number(wp->options, "alternate-screen"))
		return;

	screen_write_collect_flush(ctx, 0, __func__);
	screen_alternate_on(ctx->s, gc, cursor);
	layout_fix_panes(wp->window, NULL);

	screen_write_initctx(ctx, &ttyctx, 1);
	if (ttyctx.redraw_cb != NULL)
		ttyctx.redraw_cb(&ttyctx);
}

/* Turn alternate screen off. */
void
screen_write_alternateoff(struct screen_write_ctx *ctx, struct grid_cell *gc,
    int cursor)
{
	struct tty_ctx		 ttyctx;
	struct window_pane	*wp = ctx->wp;

	if (wp != NULL && !options_get_number(wp->options, "alternate-screen"))
		return;

	screen_write_collect_flush(ctx, 0, __func__);
	screen_alternate_off(ctx->s, gc, cursor);
	layout_fix_panes(wp->window, NULL);

	screen_write_initctx(ctx, &ttyctx, 1);
	if (ttyctx.redraw_cb != NULL)
		ttyctx.redraw_cb(&ttyctx);
}


Check for NULL returns from bufferevent_new.

<|original_code|>

/* Initialize for control mode. */
void
control_start(struct client *c)
{
	struct control_state	*cs;

	if (c->flags & CLIENT_CONTROLCONTROL) {
		close(c->out_fd);
		c->out_fd = -1;
	} else
		setblocking(c->out_fd, 0);
	setblocking(c->fd, 0);

	cs = c->control_state = xcalloc(1, sizeof *cs);
	RB_INIT(&cs->panes);
	TAILQ_INIT(&cs->pending_list);
	TAILQ_INIT(&cs->all_blocks);
	RB_INIT(&cs->subs);

	cs->read_event = bufferevent_new(c->fd, control_read_callback,
	    control_write_callback, control_error_callback, c);

	if (c->flags & CLIENT_CONTROLCONTROL)
		cs->write_event = cs->read_event;
	else {
		cs->write_event = bufferevent_new(c->out_fd, NULL,
		    control_write_callback, control_error_callback, c);
	}
	bufferevent_setwatermark(cs->write_event, EV_WRITE, CONTROL_BUFFER_LOW,
	    0);

	if (c->flags & CLIENT_CONTROLCONTROL) {
		bufferevent_write(cs->write_event, "\033P1000p", 7);
		bufferevent_enable(cs->write_event, EV_WRITE);
	}
}

/* Control client ready. */
void
control_ready(struct client *c)
{
	bufferevent_enable(c->control_state->read_event, EV_READ);
}

/* Discard all output for a client. */
void
control_discard(struct client *c)
{
	struct control_state	*cs = c->control_state;
	struct control_pane	*cp;

<|edits_diff|>
--- control.c
+++ control.c
@@ -20,6 +20,8 @@

 	cs->read_event = bufferevent_new(c->fd, control_read_callback,
 	    control_write_callback, control_error_callback, c);
+	if (cs->read_event == NULL)
+		fatalx("out of memory");

 	if (c->flags & CLIENT_CONTROLCONTROL)
 		cs->write_event = cs->read_event;
<|current_version|>

/* Initialize for control mode. */
void
control_start(struct client *c)
{
	struct control_state	*cs;

	if (c->flags & CLIENT_CONTROLCONTROL) {
		close(c->out_fd);
		c->out_fd = -1;
	} else
		setblocking(c->out_fd, 0);
	setblocking(c->fd, 0);

	cs = c->control_state = xcalloc(1, sizeof *cs);
	RB_INIT(&cs->panes);
	TAILQ_INIT(&cs->pending_list);
	TAILQ_INIT(&cs->all_blocks);
	RB_INIT(&cs->subs);

	cs->read_event = bufferevent_new(c->fd, control_read_callback,
	    control_write_callback, control_error_callback, c);
	if (cs->read_event == NULL)
		fatalx("out of memory");

	if (c->flags & CLIENT_CONTROLCONTROL)
		cs->write_event = cs->read_event;
	else {
		cs->write_event = bufferevent_new(c->out_fd, NULL,
		    control_write_callback, control_error_callback, c);
	}
	bufferevent_setwatermark(cs->write_event, EV_WRITE, CONTROL_BUFFER_LOW,
	    0);

	if (c->flags & CLIENT_CONTROLCONTROL) {
		bufferevent_write(cs->write_event, "\033P1000p", 7);
		bufferevent_enable(cs->write_event, EV_WRITE);
	}
}

/* Control client ready. */
void
control_ready(struct client *c)
{
	bufferevent_enable(c->control_state->read_event, EV_READ);
}

/* Discard all output for a client. */
void
control_discard(struct client *c)
{
	struct control_state	*cs = c->control_state;
	struct control_pane	*cp;

<|next_version|>

/* Initialize for control mode. */
void
control_start(struct client *c)
{
	struct control_state	*cs;

	if (c->flags & CLIENT_CONTROLCONTROL) {
		close(c->out_fd);
		c->out_fd = -1;
	} else
		setblocking(c->out_fd, 0);
	setblocking(c->fd, 0);

	cs = c->control_state = xcalloc(1, sizeof *cs);
	RB_INIT(&cs->panes);
	TAILQ_INIT(&cs->pending_list);
	TAILQ_INIT(&cs->all_blocks);
	RB_INIT(&cs->subs);

	cs->read_event = bufferevent_new(c->fd, control_read_callback,
	    control_write_callback, control_error_callback, c);
	if (cs->read_event == NULL)
		fatalx("out of memory");

	if (c->flags & CLIENT_CONTROLCONTROL)
		cs->write_event = cs->read_event;
	else {
		cs->write_event = bufferevent_new(c->out_fd, NULL,
		    control_write_callback, control_error_callback, c);
		if (cs->write_event == NULL)
			fatalx("out of memory");
	}
	bufferevent_setwatermark(cs->write_event, EV_WRITE, CONTROL_BUFFER_LOW,
	    0);

	if (c->flags & CLIENT_CONTROLCONTROL) {
		bufferevent_write(cs->write_event, "\033P1000p", 7);
		bufferevent_enable(cs->write_event, EV_WRITE);
	}
}

/* Control client ready. */
void
control_ready(struct client *c)
{
	bufferevent_enable(c->control_state->read_event, EV_READ);
}

/* Discard all output for a client. */
void
control_discard(struct client *c)
{
	struct control_state	*cs = c->control_state;
	struct control_pane	*cp;


Fix validation of missing percentage arguments.

<|original_code|>
	free(formatted);
	if (errstr != NULL) {
		*cause = xstrdup(errstr);
		return (0);
	}

	*cause = NULL;
	return (ll);
}

/* Convert an argument to a number which may be a percentage. */
long long
args_percentage(struct args *args, u_char flag, long long minval,
    long long maxval, long long curval, char **cause)
{
	const char		*value;
	struct args_entry	*entry;

	if ((entry = args_find(args, flag)) == NULL) {
		*cause = xstrdup("missing");
		return (0);
	}
	value = TAILQ_LAST(&entry->values, args_values)->string;
	return (args_string_percentage(value, minval, maxval, curval, cause));
}

/* Convert a string to a number which may be a percentage. */
long long
args_string_percentage(const char *value, long long minval, long long maxval,
    long long curval, char **cause)
{
	const char	*errstr;
	long long	 ll;
	size_t		 valuelen = strlen(value);
	char		*copy;

	if (value[valuelen - 1] == '%') {
		copy = xstrdup(value);
		copy[valuelen - 1] = '\0';

		ll = strtonum(copy, 0, 100, &errstr);
		free(copy);
		if (errstr != NULL) {
			*cause = xstrdup(errstr);
			return (0);
		}
		ll = (curval * ll) / 100;
		if (ll < minval) {
			*cause = xstrdup("too small");
			return (0);
		}
		if (ll > maxval) {
			*cause = xstrdup("too large");
			return (0);
		}
	} else {
		ll = strtonum(value, minval, maxval, &errstr);
		if (errstr != NULL) {
			*cause = xstrdup(errstr);
			return (0);
		}
	}

	*cause = NULL;
	return (ll);
}

/*
 * Convert an argument to a number which may be a percentage, and expand
 * formats.
 */
long long
args_percentage_and_expand(struct args *args, u_char flag, long long minval,
    long long maxval, long long curval, struct cmdq_item *item, char **cause)
{
	const char		*value;
	struct args_entry	*entry;

	if ((entry = args_find(args, flag)) == NULL) {
		*cause = xstrdup("missing");
		return (0);
	}
	value = TAILQ_LAST(&entry->values, args_values)->string;
	return (args_string_percentage_and_expand(value, minval, maxval, curval,
		    item, cause));
}

/*
 * Convert a string to a number which may be a percentage, and expand formats.
 */
long long
args_string_percentage_and_expand(const char *value, long long minval,
    long long maxval, long long curval, struct cmdq_item *item, char **cause)
{
	const char	*errstr;
	long long	 ll;
	size_t		 valuelen = strlen(value);
	char		*copy, *f;

	if (value[valuelen - 1] == '%') {
		copy = xstrdup(value);
		copy[valuelen - 1] = '\0';

		f = format_single_from_target(item, copy);
		ll = strtonum(f, 0, 100, &errstr);
		free(f);
<|edits_diff|>
--- arguments.c
+++ arguments.c
@@ -20,6 +20,10 @@
 		*cause = xstrdup("missing");
 		return (0);
 	}
+	if (TAILQ_EMPTY(&entry->values)) {
+		*cause = xstrdup("empty");
+		return (0);
+	}
 	value = TAILQ_LAST(&entry->values, args_values)->string;
 	return (args_string_percentage(value, minval, maxval, curval, cause));
 }
@@ -34,6 +38,10 @@
 	size_t		 valuelen = strlen(value);
 	char		*copy;

+	if (valuelen == 0) {
+		*cause = xstrdup("empty");
+		return (0);
+	}
 	if (value[valuelen - 1] == '%') {
 		copy = xstrdup(value);
 		copy[valuelen - 1] = '\0';
<|current_version|>
	free(formatted);
	if (errstr != NULL) {
		*cause = xstrdup(errstr);
		return (0);
	}

	*cause = NULL;
	return (ll);
}

/* Convert an argument to a number which may be a percentage. */
long long
args_percentage(struct args *args, u_char flag, long long minval,
    long long maxval, long long curval, char **cause)
{
	const char		*value;
	struct args_entry	*entry;

	if ((entry = args_find(args, flag)) == NULL) {
		*cause = xstrdup("missing");
		return (0);
	}
	if (TAILQ_EMPTY(&entry->values)) {
		*cause = xstrdup("empty");
		return (0);
	}
	value = TAILQ_LAST(&entry->values, args_values)->string;
	return (args_string_percentage(value, minval, maxval, curval, cause));
}

/* Convert a string to a number which may be a percentage. */
long long
args_string_percentage(const char *value, long long minval, long long maxval,
    long long curval, char **cause)
{
	const char	*errstr;
	long long	 ll;
	size_t		 valuelen = strlen(value);
	char		*copy;

	if (valuelen == 0) {
		*cause = xstrdup("empty");
		return (0);
	}
	if (value[valuelen - 1] == '%') {
		copy = xstrdup(value);
		copy[valuelen - 1] = '\0';

		ll = strtonum(copy, 0, 100, &errstr);
		free(copy);
		if (errstr != NULL) {
			*cause = xstrdup(errstr);
			return (0);
		}
		ll = (curval * ll) / 100;
		if (ll < minval) {
			*cause = xstrdup("too small");
			return (0);
		}
		if (ll > maxval) {
			*cause = xstrdup("too large");
			return (0);
		}
	} else {
		ll = strtonum(value, minval, maxval, &errstr);
		if (errstr != NULL) {
			*cause = xstrdup(errstr);
			return (0);
		}
	}

	*cause = NULL;
	return (ll);
}

/*
 * Convert an argument to a number which may be a percentage, and expand
 * formats.
 */
long long
args_percentage_and_expand(struct args *args, u_char flag, long long minval,
    long long maxval, long long curval, struct cmdq_item *item, char **cause)
{
	const char		*value;
	struct args_entry	*entry;

	if ((entry = args_find(args, flag)) == NULL) {
		*cause = xstrdup("missing");
		return (0);
	}
	value = TAILQ_LAST(&entry->values, args_values)->string;
	return (args_string_percentage_and_expand(value, minval, maxval, curval,
		    item, cause));
}

/*
 * Convert a string to a number which may be a percentage, and expand formats.
 */
long long
args_string_percentage_and_expand(const char *value, long long minval,
    long long maxval, long long curval, struct cmdq_item *item, char **cause)
{
	const char	*errstr;
	long long	 ll;
	size_t		 valuelen = strlen(value);
	char		*copy, *f;

	if (value[valuelen - 1] == '%') {
		copy = xstrdup(value);
		copy[valuelen - 1] = '\0';

		f = format_single_from_target(item, copy);
		ll = strtonum(f, 0, 100, &errstr);
		free(f);
<|next_version|>
	free(formatted);
	if (errstr != NULL) {
		*cause = xstrdup(errstr);
		return (0);
	}

	*cause = NULL;
	return (ll);
}

/* Convert an argument to a number which may be a percentage. */
long long
args_percentage(struct args *args, u_char flag, long long minval,
    long long maxval, long long curval, char **cause)
{
	const char		*value;
	struct args_entry	*entry;

	if ((entry = args_find(args, flag)) == NULL) {
		*cause = xstrdup("missing");
		return (0);
	}
	if (TAILQ_EMPTY(&entry->values)) {
		*cause = xstrdup("empty");
		return (0);
	}
	value = TAILQ_LAST(&entry->values, args_values)->string;
	return (args_string_percentage(value, minval, maxval, curval, cause));
}

/* Convert a string to a number which may be a percentage. */
long long
args_string_percentage(const char *value, long long minval, long long maxval,
    long long curval, char **cause)
{
	const char	*errstr;
	long long	 ll;
	size_t		 valuelen = strlen(value);
	char		*copy;

	if (valuelen == 0) {
		*cause = xstrdup("empty");
		return (0);
	}
	if (value[valuelen - 1] == '%') {
		copy = xstrdup(value);
		copy[valuelen - 1] = '\0';

		ll = strtonum(copy, 0, 100, &errstr);
		free(copy);
		if (errstr != NULL) {
			*cause = xstrdup(errstr);
			return (0);
		}
		ll = (curval * ll) / 100;
		if (ll < minval) {
			*cause = xstrdup("too small");
			return (0);
		}
		if (ll > maxval) {
			*cause = xstrdup("too large");
			return (0);
		}
	} else {
		ll = strtonum(value, minval, maxval, &errstr);
		if (errstr != NULL) {
			*cause = xstrdup(errstr);
			return (0);
		}
	}

	*cause = NULL;
	return (ll);
}

/*
 * Convert an argument to a number which may be a percentage, and expand
 * formats.
 */
long long
args_percentage_and_expand(struct args *args, u_char flag, long long minval,
    long long maxval, long long curval, struct cmdq_item *item, char **cause)
{
	const char		*value;
	struct args_entry	*entry;

	if ((entry = args_find(args, flag)) == NULL) {
		*cause = xstrdup("missing");
		return (0);
	}
	if (TAILQ_EMPTY(&entry->values)) {
		*cause = xstrdup("empty");
		return (0);
	}
	value = TAILQ_LAST(&entry->values, args_values)->string;
	return (args_string_percentage_and_expand(value, minval, maxval, curval,
		    item, cause));
}

/*
 * Convert a string to a number which may be a percentage, and expand formats.
 */
long long
args_string_percentage_and_expand(const char *value, long long minval,
    long long maxval, long long curval, struct cmdq_item *item, char **cause)
{
	const char	*errstr;
	long long	 ll;
	size_t		 valuelen = strlen(value);
	char		*copy, *f;

	if (value[valuelen - 1] == '%') {
		copy = xstrdup(value);
		copy[valuelen - 1] = '\0';

		f = format_single_from_target(item, copy);
		ll = strtonum(f, 0, 100, &errstr);
		free(f);


Store time lines are scrolled into history and display in copy mode.

<|original_code|>
		return;
	for (yy = 0; yy < ny; yy++)
		grid_free_line(gd, gd->hsize + gd->sy - 1 - yy);
	gd->hsize -= ny;
}

/*
 * Scroll the entire visible screen, moving one line into the history. Just
 * allocate a new line at the bottom and move the history size indicator.
 */
void
grid_scroll_history(struct grid *gd, u_int bg)
{
	u_int	yy;

	yy = gd->hsize + gd->sy;
	gd->linedata = xreallocarray(gd->linedata, yy + 1,
	    sizeof *gd->linedata);
	grid_empty_line(gd, yy, bg);

	gd->hscrolled++;
	grid_compact_line(&gd->linedata[gd->hsize]);
	gd->hsize++;
}

/* Clear the history. */
void
grid_clear_history(struct grid *gd)
{
	grid_trim_history(gd, gd->hsize);

	gd->hscrolled = 0;
	gd->hsize = 0;

	gd->linedata = xreallocarray(gd->linedata, gd->sy,
	    sizeof *gd->linedata);
}

/* Scroll a region up, moving the top line into the history. */
void
grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower, u_int bg)
{
	struct grid_line	*gl_history, *gl_upper;
	u_int			 yy;

	/* Create a space for a new line. */
	yy = gd->hsize + gd->sy;
	gd->linedata = xreallocarray(gd->linedata, yy + 1,
	    sizeof *gd->linedata);

	/* Move the entire screen down to free a space for this line. */
	gl_history = &gd->linedata[gd->hsize];
	memmove(gl_history + 1, gl_history, gd->sy * sizeof *gl_history);

	/* Adjust the region and find its start and end. */
	upper++;
	gl_upper = &gd->linedata[upper];
	lower++;

	/* Move the line into the history. */
	memcpy(gl_history, gl_upper, sizeof *gl_history);

	/* Then move the region up and clear the bottom line. */
	memmove(gl_upper, gl_upper + 1, (lower - upper) * sizeof *gl_upper);
	grid_empty_line(gd, lower, bg);

	/* Move the history offset down over the line. */
	gd->hscrolled++;
	gd->hsize++;
}

/* Expand line to fit to cell. */
static void
grid_expand_line(struct grid *gd, u_int py, u_int sx, u_int bg)
{
	struct grid_line	*gl;
	u_int			 xx;

	gl = &gd->linedata[py];
	if (sx <= gl->cellsize)
		return;

	if (sx < gd->sx / 4)
		sx = gd->sx / 4;
	else if (sx < gd->sx / 2)
<|edits_diff|>
--- grid.c
+++ grid.c
@@ -20,6 +20,7 @@

 	gd->hscrolled++;
 	grid_compact_line(&gd->linedata[gd->hsize]);
+	gd->linedata[gd->hsize].time = current_time;
 	gd->hsize++;
 }

<|current_version|>
		return;
	for (yy = 0; yy < ny; yy++)
		grid_free_line(gd, gd->hsize + gd->sy - 1 - yy);
	gd->hsize -= ny;
}

/*
 * Scroll the entire visible screen, moving one line into the history. Just
 * allocate a new line at the bottom and move the history size indicator.
 */
void
grid_scroll_history(struct grid *gd, u_int bg)
{
	u_int	yy;

	yy = gd->hsize + gd->sy;
	gd->linedata = xreallocarray(gd->linedata, yy + 1,
	    sizeof *gd->linedata);
	grid_empty_line(gd, yy, bg);

	gd->hscrolled++;
	grid_compact_line(&gd->linedata[gd->hsize]);
	gd->linedata[gd->hsize].time = current_time;
	gd->hsize++;
}

/* Clear the history. */
void
grid_clear_history(struct grid *gd)
{
	grid_trim_history(gd, gd->hsize);

	gd->hscrolled = 0;
	gd->hsize = 0;

	gd->linedata = xreallocarray(gd->linedata, gd->sy,
	    sizeof *gd->linedata);
}

/* Scroll a region up, moving the top line into the history. */
void
grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower, u_int bg)
{
	struct grid_line	*gl_history, *gl_upper;
	u_int			 yy;

	/* Create a space for a new line. */
	yy = gd->hsize + gd->sy;
	gd->linedata = xreallocarray(gd->linedata, yy + 1,
	    sizeof *gd->linedata);

	/* Move the entire screen down to free a space for this line. */
	gl_history = &gd->linedata[gd->hsize];
	memmove(gl_history + 1, gl_history, gd->sy * sizeof *gl_history);

	/* Adjust the region and find its start and end. */
	upper++;
	gl_upper = &gd->linedata[upper];
	lower++;

	/* Move the line into the history. */
	memcpy(gl_history, gl_upper, sizeof *gl_history);

	/* Then move the region up and clear the bottom line. */
	memmove(gl_upper, gl_upper + 1, (lower - upper) * sizeof *gl_upper);
	grid_empty_line(gd, lower, bg);

	/* Move the history offset down over the line. */
	gd->hscrolled++;
	gd->hsize++;
}

/* Expand line to fit to cell. */
static void
grid_expand_line(struct grid *gd, u_int py, u_int sx, u_int bg)
{
	struct grid_line	*gl;
	u_int			 xx;

	gl = &gd->linedata[py];
	if (sx <= gl->cellsize)
		return;

	if (sx < gd->sx / 4)
		sx = gd->sx / 4;
	else if (sx < gd->sx / 2)
<|next_version|>
		return;
	for (yy = 0; yy < ny; yy++)
		grid_free_line(gd, gd->hsize + gd->sy - 1 - yy);
	gd->hsize -= ny;
}

/*
 * Scroll the entire visible screen, moving one line into the history. Just
 * allocate a new line at the bottom and move the history size indicator.
 */
void
grid_scroll_history(struct grid *gd, u_int bg)
{
	u_int	yy;

	yy = gd->hsize + gd->sy;
	gd->linedata = xreallocarray(gd->linedata, yy + 1,
	    sizeof *gd->linedata);
	grid_empty_line(gd, yy, bg);

	gd->hscrolled++;
	grid_compact_line(&gd->linedata[gd->hsize]);
	gd->linedata[gd->hsize].time = current_time;
	gd->hsize++;
}

/* Clear the history. */
void
grid_clear_history(struct grid *gd)
{
	grid_trim_history(gd, gd->hsize);

	gd->hscrolled = 0;
	gd->hsize = 0;

	gd->linedata = xreallocarray(gd->linedata, gd->sy,
	    sizeof *gd->linedata);
}

/* Scroll a region up, moving the top line into the history. */
void
grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower, u_int bg)
{
	struct grid_line	*gl_history, *gl_upper;
	u_int			 yy;

	/* Create a space for a new line. */
	yy = gd->hsize + gd->sy;
	gd->linedata = xreallocarray(gd->linedata, yy + 1,
	    sizeof *gd->linedata);

	/* Move the entire screen down to free a space for this line. */
	gl_history = &gd->linedata[gd->hsize];
	memmove(gl_history + 1, gl_history, gd->sy * sizeof *gl_history);

	/* Adjust the region and find its start and end. */
	upper++;
	gl_upper = &gd->linedata[upper];
	lower++;

	/* Move the line into the history. */
	memcpy(gl_history, gl_upper, sizeof *gl_history);
	gl_history->time = current_time;

	/* Then move the region up and clear the bottom line. */
	memmove(gl_upper, gl_upper + 1, (lower - upper) * sizeof *gl_upper);
	grid_empty_line(gd, lower, bg);

	/* Move the history offset down over the line. */
	gd->hscrolled++;
	gd->hsize++;
}

/* Expand line to fit to cell. */
static void
grid_expand_line(struct grid *gd, u_int py, u_int sx, u_int bg)
{
	struct grid_line	*gl;
	u_int			 xx;

	gl = &gd->linedata[py];
	if (sx <= gl->cellsize)
		return;

	if (sx < gd->sx / 4)
		sx = gd->sx / 4;
	else if (sx < gd->sx / 2)