Patch 7.4.1238
Problem:    Can't handle two messages right after each other.
Solution:   Find the end of the JSON.  Read more when incomplete.  Add a C
            test for the JSON decoding.
Files:      src/channel.c, src/json.c, src/proto/json.pro, src/eval.c,
            src/Makefile, src/json_test.c, src/memfile_test.c, src/structs.h


*** ../vim-7.4.1237/src/channel.c       2016-02-01 21:47:06.745437762 +0100
--- src/channel.c       2016-02-02 14:00:37.848551294 +0100
***************
*** 540,548 ****
      /* TODO: make reader work properly */
      /* reader.js_buf = channel_peek(ch_idx); */
      reader.js_buf = channel_get_all(ch_idx);
-     reader.js_eof = TRUE;
-     /* reader.js_eof = FALSE; */
      reader.js_used = 0;
      /* reader.js_fill = channel_fill; */
      reader.js_cookie = &ch_idx;
      if (json_decode(&reader, &listtv) == OK)
--- 540,547 ----
      /* TODO: make reader work properly */
      /* reader.js_buf = channel_peek(ch_idx); */
      reader.js_buf = channel_get_all(ch_idx);
      reader.js_used = 0;
+     reader.js_fill = NULL;
      /* reader.js_fill = channel_fill; */
      reader.js_cookie = &ch_idx;
      if (json_decode(&reader, &listtv) == OK)
*** ../vim-7.4.1237/src/json.c  2016-02-01 21:38:13.319011999 +0100
--- src/json.c  2016-02-02 18:11:39.371862724 +0100
***************
*** 17,23 ****
  
  #if defined(FEAT_EVAL) || defined(PROTO)
  static int json_encode_item(garray_T *gap, typval_T *val, int copyID);
! static void json_decode_item(js_read_T *reader, typval_T *res);
  
  /*
   * Encode "val" into a JSON format string.
--- 17,23 ----
  
  #if defined(FEAT_EVAL) || defined(PROTO)
  static int json_encode_item(garray_T *gap, typval_T *val, int copyID);
! static int json_decode_item(js_read_T *reader, typval_T *res);
  
  /*
   * Encode "val" into a JSON format string.
***************
*** 235,270 ****
  }
  
  /*
!  * Skip white space in "reader".
   */
      static void
! json_skip_white(js_read_T *reader)
  {
!     int c;
! 
!     while ((c = reader->js_buf[reader->js_used]) == ' '
!                                          || c == TAB || c == NL || c == CAR)
!       ++reader->js_used;
  }
  
  /*
!  * Make sure there are at least enough characters buffered to read a number.
   */
      static void
! json_fill_buffer(js_read_T *reader UNUSED)
  {
!     /* TODO */
  }
  
!     static void
  json_decode_array(js_read_T *reader, typval_T *res)
  {
      char_u    *p;
      typval_T  item;
      listitem_T        *li;
  
!     if (rettv_list_alloc(res) == FAIL)
!       goto failsilent;
      ++reader->js_used; /* consume the '[' */
  
      while (TRUE)
--- 235,293 ----
  }
  
  /*
!  * When "reader" has less than NUMBUFLEN bytes available, call the fill
!  * callback to get more.
   */
      static void
! fill_numbuflen(js_read_T *reader)
  {
!     if (reader->js_fill != NULL && (int)(reader->js_end - reader->js_buf)
!                                               - reader->js_used < NUMBUFLEN)
!     {
!       if (reader->js_fill(reader))
!           reader->js_end = reader->js_buf + STRLEN(reader->js_buf);
!     }
  }
  
  /*
!  * Skip white space in "reader".
!  * Also tops up readahead when needed.
   */
      static void
! json_skip_white(js_read_T *reader)
  {
!     int c;
! 
!     for (;;)
!     {
!       c = reader->js_buf[reader->js_used];
!       if (reader->js_fill != NULL && c == NUL)
!       {
!           if (reader->js_fill(reader))
!               reader->js_end = reader->js_buf + STRLEN(reader->js_buf);
!           continue;
!       }
!       if (c != ' ' && c != TAB && c != NL && c != CAR)
!           break;
!       ++reader->js_used;
!     }
!     fill_numbuflen(reader);
  }
  
!     static int
  json_decode_array(js_read_T *reader, typval_T *res)
  {
      char_u    *p;
      typval_T  item;
      listitem_T        *li;
+     int               ret;
  
!     if (res != NULL && rettv_list_alloc(res) == FAIL)
!     {
!       res->v_type = VAR_SPECIAL;
!       res->vval.v_number = VVAL_NONE;
!       return FAIL;
!     }
      ++reader->js_used; /* consume the '[' */
  
      while (TRUE)
***************
*** 272,309 ****
        json_skip_white(reader);
        p = reader->js_buf + reader->js_used;
        if (*p == NUL)
!           goto fail;
        if (*p == ']')
        {
            ++reader->js_used; /* consume the ']' */
!           return;
        }
  
!       if (!reader->js_eof && (int)(reader->js_end - p) < NUMBUFLEN)
!           json_fill_buffer(reader);
! 
!       json_decode_item(reader, &item);
!       li = listitem_alloc();
!       if (li == NULL)
!           return;
!       li->li_tv = item;
!       list_append(res->vval.v_list, li);
  
        json_skip_white(reader);
        p = reader->js_buf + reader->js_used;
        if (*p == ',')
            ++reader->js_used;
        else if (*p != ']')
!           goto fail;
      }
! fail:
!     EMSG(_(e_invarg));
! failsilent:
!     res->v_type = VAR_SPECIAL;
!     res->vval.v_number = VVAL_NONE;
  }
  
!     static void
  json_decode_object(js_read_T *reader, typval_T *res)
  {
      char_u    *p;
--- 295,337 ----
        json_skip_white(reader);
        p = reader->js_buf + reader->js_used;
        if (*p == NUL)
!           return MAYBE;
        if (*p == ']')
        {
            ++reader->js_used; /* consume the ']' */
!           break;
        }
  
!       ret = json_decode_item(reader, res == NULL ? NULL : &item);
!       if (ret != OK)
!           return ret;
!       if (res != NULL)
!       {
!           li = listitem_alloc();
!           if (li == NULL)
!           {
!               clear_tv(&item);
!               return FAIL;
!           }
!           li->li_tv = item;
!           list_append(res->vval.v_list, li);
!       }
  
        json_skip_white(reader);
        p = reader->js_buf + reader->js_used;
        if (*p == ',')
            ++reader->js_used;
        else if (*p != ']')
!       {
!           if (*p == NUL)
!               return MAYBE;
!           return FAIL;
!       }
      }
!     return OK;
  }
  
!     static int
  json_decode_object(js_read_T *reader, typval_T *res)
  {
      char_u    *p;
***************
*** 312,320 ****
      dictitem_T        *di;
      char_u    buf[NUMBUFLEN];
      char_u    *key;
  
!     if (rettv_dict_alloc(res) == FAIL)
!       goto failsilent;
      ++reader->js_used; /* consume the '{' */
  
      while (TRUE)
--- 340,353 ----
      dictitem_T        *di;
      char_u    buf[NUMBUFLEN];
      char_u    *key;
+     int               ret;
  
!     if (res != NULL && rettv_dict_alloc(res) == FAIL)
!     {
!       res->v_type = VAR_SPECIAL;
!       res->vval.v_number = VVAL_NONE;
!       return FAIL;
!     }
      ++reader->js_used; /* consume the '{' */
  
      while (TRUE)
***************
*** 322,564 ****
        json_skip_white(reader);
        p = reader->js_buf + reader->js_used;
        if (*p == NUL)
!           goto fail;
        if (*p == '}')
        {
            ++reader->js_used; /* consume the '}' */
!           return;
        }
  
!       if (!reader->js_eof && (int)(reader->js_end - p) < NUMBUFLEN)
!           json_fill_buffer(reader);
!       json_decode_item(reader, &tvkey);
!       key = get_tv_string_buf_chk(&tvkey, buf);
!       if (key == NULL || *key == NUL)
!       {
!           /* "key" is NULL when get_tv_string_buf_chk() gave an errmsg */
!           if (key != NULL)
!               EMSG(_(e_emptykey));
!           clear_tv(&tvkey);
!           goto failsilent;
        }
  
        json_skip_white(reader);
        p = reader->js_buf + reader->js_used;
        if (*p != ':')
        {
!           clear_tv(&tvkey);
!           goto fail;
        }
        ++reader->js_used;
        json_skip_white(reader);
  
!       if (!reader->js_eof && (int)(reader->js_end - p) < NUMBUFLEN)
!           json_fill_buffer(reader);
!       json_decode_item(reader, &item);
! 
!       di = dictitem_alloc(key);
!       clear_tv(&tvkey);
!       if (di == NULL)
!       {
!           clear_tv(&item);
!           goto fail;
!       }
!       di->di_tv = item;
!       if (dict_add(res->vval.v_dict, di) == FAIL)
!           dictitem_free(di);
  
        json_skip_white(reader);
        p = reader->js_buf + reader->js_used;
        if (*p == ',')
            ++reader->js_used;
        else if (*p != '}')
!           goto fail;
      }
! fail:
!     EMSG(_(e_invarg));
! failsilent:
!     res->v_type = VAR_SPECIAL;
!     res->vval.v_number = VVAL_NONE;
  }
  
!     static void
  json_decode_string(js_read_T *reader, typval_T *res)
  {
      garray_T    ga;
      int               len;
!     char_u    *p = reader->js_buf + reader->js_used + 1;
      int               c;
      long      nr;
      char_u    buf[NUMBUFLEN];
  
!     ga_init2(&ga, 1, 200);
  
!     /* TODO: fill buffer when needed. */
!     while (*p != NUL && *p != '"')
      {
        if (*p == '\\')
        {
            c = -1;
            switch (p[1])
            {
                case 'b': c = BS; break;
                case 't': c = TAB; break;
                case 'n': c = NL; break;
                case 'f': c = FF; break;
                case 'r': c = CAR; break;
                case 'u':
                    vim_str2nr(p + 2, NULL, &len,
                                     STR2NR_HEX + STR2NR_FORCE, &nr, NULL, 4);
                    p += len + 2;
  #ifdef FEAT_MBYTE
!                   buf[(*mb_char2bytes)((int)nr, buf)] = NUL;
!                   ga_concat(&ga, buf);
  #else
!                   ga_append(&ga, nr);
  #endif
                    break;
!               default: c = p[1]; break;
            }
            if (c > 0)
            {
                p += 2;
!               ga_append(&ga, c);
            }
        }
        else
        {
            len = MB_PTR2LEN(p);
!           if (ga_grow(&ga, len) == OK)
            {
                mch_memmove((char *)ga.ga_data + ga.ga_len, p, (size_t)len);
                ga.ga_len += len;
            }
            p += len;
        }
-       if (!reader->js_eof && (int)(reader->js_end - p) < NUMBUFLEN)
-       {
-           reader->js_used = (int)(p - reader->js_buf);
-           json_fill_buffer(reader);
-           p = reader->js_buf + reader->js_used;
-       }
      }
      reader->js_used = (int)(p - reader->js_buf);
      if (*p == '"')
      {
        ++reader->js_used;
!       res->v_type = VAR_STRING;
!       if (ga.ga_data == NULL)
!           res->vval.v_string = NULL;
!       else
!           res->vval.v_string = vim_strsave(ga.ga_data);
      }
!     else
      {
-       EMSG(_(e_invarg));
        res->v_type = VAR_SPECIAL;
        res->vval.v_number = VVAL_NONE;
      }
!     ga_clear(&ga);
  }
  
  /*
!  * Decode one item and put it in "result".
   * Must already have skipped white space.
   */
!     static void
  json_decode_item(js_read_T *reader, typval_T *res)
  {
!     char_u    *p = reader->js_buf + reader->js_used;
  
      switch (*p)
      {
        case '[': /* array */
!           json_decode_array(reader, res);
!           return;
  
        case '{': /* object */
!           json_decode_object(reader, res);
!           return;
  
        case '"': /* string */
!           json_decode_string(reader, res);
!           return;
  
        case ',': /* comma: empty item */
        case NUL: /* empty */
!           res->v_type = VAR_SPECIAL;
!           res->vval.v_number = VVAL_NONE;
!           return;
  
        default:
            if (VIM_ISDIGIT(*p) || *p == '-')
            {
-               int     len;
                char_u  *sp = p;
  #ifdef FEAT_FLOAT
                if (*sp == '-')
                    ++sp;
                sp = skipdigits(sp);
                if (*sp == '.' || *sp == 'e' || *sp == 'E')
                {
!                   res->v_type = VAR_FLOAT;
!                   len = string2float(p, &res->vval.v_float);
                }
                else
  #endif
                {
                    long nr;
  
-                   res->v_type = VAR_NUMBER;
                    vim_str2nr(reader->js_buf + reader->js_used,
                            NULL, &len, 0, /* what */
                            &nr, NULL, 0);
!                   res->vval.v_number = nr;
                }
                reader->js_used += len;
!               return;
            }
            if (STRNICMP((char *)p, "false", 5) == 0)
            {
                reader->js_used += 5;
!               res->v_type = VAR_SPECIAL;
!               res->vval.v_number = VVAL_FALSE;
!               return;
            }
            if (STRNICMP((char *)p, "true", 4) == 0)
            {
                reader->js_used += 4;
!               res->v_type = VAR_SPECIAL;
!               res->vval.v_number = VVAL_TRUE;
!               return;
            }
            if (STRNICMP((char *)p, "null", 4) == 0)
            {
                reader->js_used += 4;
!               res->v_type = VAR_SPECIAL;
!               res->vval.v_number = VVAL_NULL;
!               return;
            }
            break;
      }
  
!     EMSG(_(e_invarg));
!     res->v_type = VAR_SPECIAL;
!     res->vval.v_number = VVAL_NONE;
  }
  
  /*
   * Decode the JSON from "reader" and store the result in "res".
!  * Return OK or FAIL;
   */
      int
! json_decode(js_read_T *reader, typval_T *res)
  {
      json_skip_white(reader);
!     json_decode_item(reader, res);
      json_skip_white(reader);
      if (reader->js_buf[reader->js_used] != NUL)
        return FAIL;
      return OK;
  }
  #endif
--- 355,741 ----
        json_skip_white(reader);
        p = reader->js_buf + reader->js_used;
        if (*p == NUL)
!           return MAYBE;
        if (*p == '}')
        {
            ++reader->js_used; /* consume the '}' */
!           break;
        }
  
!       ret = json_decode_item(reader, res == NULL ? NULL : &tvkey);
!       if (ret != OK)
!           return ret;
!       if (res != NULL)
!       {
!           key = get_tv_string_buf_chk(&tvkey, buf);
!           if (key == NULL || *key == NUL)
!           {
!               clear_tv(&tvkey);
!               return FAIL;
!           }
        }
  
        json_skip_white(reader);
        p = reader->js_buf + reader->js_used;
        if (*p != ':')
        {
!           if (res != NULL)
!               clear_tv(&tvkey);
!           if (*p == NUL)
!               return MAYBE;
!           return FAIL;
        }
        ++reader->js_used;
        json_skip_white(reader);
  
!       ret = json_decode_item(reader, res == NULL ? NULL : &item);
!       if (ret != OK)
!       {
!           if (res != NULL)
!               clear_tv(&tvkey);
!           return ret;
!       }
! 
!       if (res != NULL)
!       {
!           di = dictitem_alloc(key);
!           clear_tv(&tvkey);
!           if (di == NULL)
!           {
!               clear_tv(&item);
!               return FAIL;
!           }
!           di->di_tv = item;
!           if (dict_add(res->vval.v_dict, di) == FAIL)
!           {
!               dictitem_free(di);
!               return FAIL;
!           }
!       }
  
        json_skip_white(reader);
        p = reader->js_buf + reader->js_used;
        if (*p == ',')
            ++reader->js_used;
        else if (*p != '}')
!       {
!           if (*p == NUL)
!               return MAYBE;
!           return FAIL;
!       }
      }
!     return OK;
  }
  
!     static int
  json_decode_string(js_read_T *reader, typval_T *res)
  {
      garray_T    ga;
      int               len;
!     char_u    *p;
      int               c;
      long      nr;
      char_u    buf[NUMBUFLEN];
  
!     if (res != NULL)
!       ga_init2(&ga, 1, 200);
  
!     p = reader->js_buf + reader->js_used + 1; /* skip over " */
!     while (*p != '"')
      {
+       if (*p == NUL || p[1] == NUL
+ #ifdef FEAT_MBYTE
+               || utf_ptr2len(p) < utf_byte2len(*p)
+ #endif
+               )
+       {
+           if (reader->js_fill == NULL)
+               break;
+           len = (int)(reader->js_end - p);
+           reader->js_used = (int)(p - reader->js_buf);
+           if (!reader->js_fill(reader))
+               break; /* didn't get more */
+           p = reader->js_buf + reader->js_used;
+           reader->js_end = reader->js_buf + STRLEN(reader->js_buf);
+           continue;
+       }
+ 
        if (*p == '\\')
        {
            c = -1;
            switch (p[1])
            {
+               case '\\': c = '\\'; break;
+               case '"': c = '"'; break;
                case 'b': c = BS; break;
                case 't': c = TAB; break;
                case 'n': c = NL; break;
                case 'f': c = FF; break;
                case 'r': c = CAR; break;
                case 'u':
+                   if (reader->js_fill != NULL
+                                    && (int)(reader->js_end - p) < NUMBUFLEN)
+                   {
+                       reader->js_used = (int)(p - reader->js_buf);
+                       if (reader->js_fill(reader))
+                       {
+                           p = reader->js_buf + reader->js_used;
+                           reader->js_end = reader->js_buf
+                                                    + STRLEN(reader->js_buf);
+                       }
+                   }
                    vim_str2nr(p + 2, NULL, &len,
                                     STR2NR_HEX + STR2NR_FORCE, &nr, NULL, 4);
                    p += len + 2;
+                   if (res != NULL)
+                   {
  #ifdef FEAT_MBYTE
!                       buf[(*mb_char2bytes)((int)nr, buf)] = NUL;
!                       ga_concat(&ga, buf);
  #else
!                       ga_append(&ga, nr);
  #endif
+                   }
                    break;
!               default:
!                   /* not a special char, skip over \ */
!                   ++p;
!                   continue;
            }
            if (c > 0)
            {
                p += 2;
!               if (res != NULL)
!                   ga_append(&ga, c);
            }
        }
        else
        {
            len = MB_PTR2LEN(p);
!           if (res != NULL)
            {
+               if (ga_grow(&ga, len) == FAIL)
+               {
+                   ga_clear(&ga);
+                   return FAIL;
+               }
                mch_memmove((char *)ga.ga_data + ga.ga_len, p, (size_t)len);
                ga.ga_len += len;
            }
            p += len;
        }
      }
+ 
      reader->js_used = (int)(p - reader->js_buf);
      if (*p == '"')
      {
        ++reader->js_used;
!       if (res != NULL)
!       {
!           res->v_type = VAR_STRING;
!           if (ga.ga_data == NULL)
!               res->vval.v_string = NULL;
!           else
!               res->vval.v_string = vim_strsave(ga.ga_data);
!       }
!       return OK;
      }
!     if (res != NULL)
      {
        res->v_type = VAR_SPECIAL;
        res->vval.v_number = VVAL_NONE;
+       ga_clear(&ga);
      }
!     return MAYBE;
  }
  
  /*
!  * Decode one item and put it in "res".  If "res" is NULL only advance.
   * Must already have skipped white space.
+  *
+  * Return FAIL for a decoding error.
+  * Return MAYBE for an incomplete message.
   */
!     static int
  json_decode_item(js_read_T *reader, typval_T *res)
  {
!     char_u    *p;
!     int               len;
  
+     fill_numbuflen(reader);
+     p = reader->js_buf + reader->js_used;
      switch (*p)
      {
        case '[': /* array */
!           return json_decode_array(reader, res);
  
        case '{': /* object */
!           return json_decode_object(reader, res);
  
        case '"': /* string */
!           return json_decode_string(reader, res);
  
        case ',': /* comma: empty item */
        case NUL: /* empty */
!           if (res != NULL)
!           {
!               res->v_type = VAR_SPECIAL;
!               res->vval.v_number = VVAL_NONE;
!           }
!           return OK;
  
        default:
            if (VIM_ISDIGIT(*p) || *p == '-')
            {
                char_u  *sp = p;
+ 
  #ifdef FEAT_FLOAT
                if (*sp == '-')
+               {
                    ++sp;
+                   if (*sp == NUL)
+                       return MAYBE;
+                   if (!VIM_ISDIGIT(*sp))
+                       return FAIL;
+               }
                sp = skipdigits(sp);
                if (*sp == '.' || *sp == 'e' || *sp == 'E')
                {
!                   if (res == NULL)
!                   {
!                       float_T f;
! 
!                       len = string2float(p, &f);
!                   }
!                   else
!                   {
!                       res->v_type = VAR_FLOAT;
!                       len = string2float(p, &res->vval.v_float);
!                   }
                }
                else
  #endif
                {
                    long nr;
  
                    vim_str2nr(reader->js_buf + reader->js_used,
                            NULL, &len, 0, /* what */
                            &nr, NULL, 0);
!                   if (res != NULL)
!                   {
!                       res->v_type = VAR_NUMBER;
!                       res->vval.v_number = nr;
!                   }
                }
                reader->js_used += len;
!               return OK;
            }
            if (STRNICMP((char *)p, "false", 5) == 0)
            {
                reader->js_used += 5;
!               if (res != NULL)
!               {
!                   res->v_type = VAR_SPECIAL;
!                   res->vval.v_number = VVAL_FALSE;
!               }
!               return OK;
            }
            if (STRNICMP((char *)p, "true", 4) == 0)
            {
                reader->js_used += 4;
!               if (res != NULL)
!               {
!                   res->v_type = VAR_SPECIAL;
!                   res->vval.v_number = VVAL_TRUE;
!               }
!               return OK;
            }
            if (STRNICMP((char *)p, "null", 4) == 0)
            {
                reader->js_used += 4;
!               if (res != NULL)
!               {
!                   res->v_type = VAR_SPECIAL;
!                   res->vval.v_number = VVAL_NULL;
!               }
!               return OK;
            }
+           /* check for truncated name */
+           len = (int)(reader->js_end - (reader->js_buf + reader->js_used));
+           if ((len < 5 && STRNICMP((char *)p, "false", len) == 0)
+                   || (len < 4 && (STRNICMP((char *)p, "true", len) == 0
+                              ||  STRNICMP((char *)p, "null", len) == 0)))
+               return MAYBE;
            break;
      }
  
!     if (res != NUL)
!     {
!       res->v_type = VAR_SPECIAL;
!       res->vval.v_number = VVAL_NONE;
!     }
!     return FAIL;
  }
  
  /*
   * Decode the JSON from "reader" and store the result in "res".
!  * Return FAIL if not the whole message was consumed.
   */
      int
! json_decode_all(js_read_T *reader, typval_T *res)
  {
+     int ret;
+ 
+     /* We get the end once, to avoid calling strlen() many times. */
+     reader->js_end = reader->js_buf + STRLEN(reader->js_buf);
      json_skip_white(reader);
!     ret = json_decode_item(reader, res);
!     if (ret != OK)
!       return FAIL;
      json_skip_white(reader);
      if (reader->js_buf[reader->js_used] != NUL)
        return FAIL;
      return OK;
  }
+ 
+ /*
+  * Decode the JSON from "reader" and store the result in "res".
+  * Return FAIL if the message has a decoding error or the message is
+  * truncated.  Consumes the message anyway.
+  */
+     int
+ json_decode(js_read_T *reader, typval_T *res)
+ {
+     int ret;
+ 
+     /* We get the end once, to avoid calling strlen() many times. */
+     reader->js_end = reader->js_buf + STRLEN(reader->js_buf);
+     json_skip_white(reader);
+     ret = json_decode_item(reader, res);
+     json_skip_white(reader);
+ 
+     return ret == OK ? OK : FAIL;
+ }
+ 
+ /*
+  * Decode the JSON from "reader" to find the end of the message.
+  * Return FAIL if the message has a decoding error.
+  * Return MAYBE if the message is truncated, need to read more.
+  * This only works reliable if the message contains an object, array or
+  * string.  A number might be trucated without knowing.
+  * Does not advance the reader.
+  */
+     int
+ json_find_end(js_read_T *reader)
+ {
+     int used_save = reader->js_used;
+     int ret;
+ 
+     /* We get the end once, to avoid calling strlen() many times. */
+     reader->js_end = reader->js_buf + STRLEN(reader->js_buf);
+     json_skip_white(reader);
+     ret = json_decode_item(reader, NULL);
+     reader->js_used = used_save;
+     return ret;
+ }
  #endif
*** ../vim-7.4.1237/src/proto/json.pro  2016-02-01 21:38:13.319011999 +0100
--- src/proto/json.pro  2016-02-02 16:18:16.110645577 +0100
***************
*** 1,5 ****
--- 1,7 ----
  /* json.c */
  char_u *json_encode(typval_T *val);
  char_u *json_encode_nr_expr(int nr, typval_T *val);
+ int json_decode_all(js_read_T *reader, typval_T *res);
  int json_decode(js_read_T *reader, typval_T *res);
+ int json_find_end(js_read_T *reader);
  /* vim: set ft=c : */
*** ../vim-7.4.1237/src/eval.c  2016-02-01 22:40:55.615627178 +0100
--- src/eval.c  2016-02-02 16:18:27.506526656 +0100
***************
*** 14100,14108 ****
      js_read_T reader;
  
      reader.js_buf = get_tv_string(&argvars[0]);
!     reader.js_eof = TRUE;
      reader.js_used = 0;
!     if (json_decode(&reader, rettv) == FAIL)
        EMSG(_(e_invarg));
  }
  
--- 14100,14108 ----
      js_read_T reader;
  
      reader.js_buf = get_tv_string(&argvars[0]);
!     reader.js_fill = NULL;
      reader.js_used = 0;
!     if (json_decode_all(&reader, rettv) != OK)
        EMSG(_(e_invarg));
  }
  
*** ../vim-7.4.1237/src/Makefile        2016-01-31 16:37:29.435752043 +0100
--- src/Makefile        2016-02-02 17:19:38.412272879 +0100
***************
*** 1545,1555 ****
            $(GRESOURCE_SRC)
  
  # Unittest files
  MEMFILE_TEST_SRC = memfile_test.c
  MEMFILE_TEST_TARGET = memfile_test$(EXEEXT)
  
! UNITTEST_SRC = $(MEMFILE_TEST_SRC)
! UNITTEST_TARGETS = $(MEMFILE_TEST_TARGET)
  
  # All sources, also the ones that are not configured
  ALL_SRC = $(BASIC_SRC) $(ALL_GUI_SRC) $(UNITTEST_SRC) $(EXTRA_SRC)
--- 1545,1557 ----
            $(GRESOURCE_SRC)
  
  # Unittest files
+ JSON_TEST_SRC = json_test.c
+ JSON_TEST_TARGET = json_test$(EXEEXT)
  MEMFILE_TEST_SRC = memfile_test.c
  MEMFILE_TEST_TARGET = memfile_test$(EXEEXT)
  
! UNITTEST_SRC = $(JSON_TEST_SRC) $(MEMFILE_TEST_SRC)
! UNITTEST_TARGETS = $(JSON_TEST_TARGET) $(MEMFILE_TEST_TARGET)
  
  # All sources, also the ones that are not configured
  ALL_SRC = $(BASIC_SRC) $(ALL_GUI_SRC) $(UNITTEST_SRC) $(EXTRA_SRC)
***************
*** 1588,1594 ****
        $(HANGULIN_OBJ) \
        objects/if_cscope.o \
        objects/if_xcmdsrv.o \
-       objects/json.o \
        objects/mark.o \
        objects/memline.o \
        objects/menu.o \
--- 1590,1595 ----
***************
*** 1632,1641 ****
--- 1633,1648 ----
        $(WSDEBUG_OBJ)
  
  OBJ = $(OBJ_COMMON) \
+       objects/json.o \
        objects/main.o \
        objects/memfile.o
  
+ JSON_TEST_OBJ = $(OBJ_COMMON) \
+       objects/json_test.o \
+       objects/memfile.o
+ 
  MEMFILE_TEST_OBJ = $(OBJ_COMMON) \
+       objects/json.o \
        objects/memfile_test.o
  
  PRO_AUTO = \
***************
*** 1914,1919 ****
--- 1921,1927 ----
        ctags --c-kinds=gstu -o- $(TAGS_SRC) $(TAGS_INCL) |\
                awk 'BEGIN{printf("syntax keyword Type\t")}\
                        {printf("%s ", $$1)}END{print ""}' > $@
+       echo "syn keyword Constant OK FAIL TRUE FALSE MAYBE" >> $@
  
  # Execute the test scripts.  Run these after compiling Vim, before installing.
  # This doesn't depend on $(VIMTARGET), because that won't work when configure
***************
*** 1948,1953 ****
--- 1956,1967 ----
                ./$$t || exit 1; echo $$t passed; \
        done
  
+ run_json_test: $(JSON_TEST_TARGET)
+       ./$(JSON_TEST_TARGET)
+ 
+ run_memfile_test: $(MEMFILE_TEST_TARGET)
+       ./$(MEMFILE_TEST_TARGET)
+ 
  # Run individual OLD style test, assuming that Vim was already compiled.
  test1 \
        test_autocmd_option \
***************
*** 2040,2045 ****
--- 2054,2066 ----
  
  # Unittests
  # It's build just like Vim to satisfy all dependencies.
+ $(JSON_TEST_TARGET): auto/config.mk objects $(JSON_TEST_OBJ)
+       $(CCC) version.c -o objects/version.o
+       @LINK="$(PURIFY) $(SHRPENV) $(CClink) $(ALL_LIB_DIRS) $(LDFLAGS) \
+               -o $(JSON_TEST_TARGET) $(JSON_TEST_OBJ) $(ALL_LIBS)" \
+               MAKE="$(MAKE)" LINK_AS_NEEDED=$(LINK_AS_NEEDED) \
+               sh $(srcdir)/link.sh
+ 
  $(MEMFILE_TEST_TARGET): auto/config.mk objects $(MEMFILE_TEST_OBJ)
        $(CCC) version.c -o objects/version.o
        @LINK="$(PURIFY) $(SHRPENV) $(CClink) $(ALL_LIB_DIRS) $(LDFLAGS) \
***************
*** 2811,2816 ****
--- 2832,2840 ----
  objects/json.o: json.c
        $(CCC) -o $@ json.c
  
+ objects/json_test.o: json_test.c
+       $(CCC) -o $@ json_test.c
+ 
  objects/main.o: main.c
        $(CCC) -o $@ main.c
  
***************
*** 3301,3306 ****
--- 3325,3334 ----
  objects/pty.o: pty.c vim.h auto/config.h feature.h os_unix.h auto/osdef.h 
ascii.h \
   keymap.h term.h macros.h option.h structs.h regexp.h gui.h gui_beval.h \
   proto/gui_beval.pro alloc.h ex_cmds.h proto.h globals.h farsi.h arabic.h
+ objects/json_test.o: json_test.c main.c vim.h auto/config.h feature.h 
os_unix.h \
+  auto/osdef.h ascii.h keymap.h term.h macros.h option.h structs.h \
+  regexp.h gui.h gui_beval.h proto/gui_beval.pro alloc.h ex_cmds.h proto.h \
+  globals.h farsi.h arabic.h farsi.c arabic.c json.c
  objects/memfile_test.o: memfile_test.c main.c vim.h auto/config.h feature.h \
   os_unix.h auto/osdef.h ascii.h keymap.h term.h macros.h option.h \
   structs.h regexp.h gui.h gui_beval.h proto/gui_beval.pro alloc.h \
*** ../vim-7.4.1237/src/json_test.c     2016-02-02 18:16:45.848683505 +0100
--- src/json_test.c     2016-02-02 18:00:13.398975516 +0100
***************
*** 0 ****
--- 1,193 ----
+ /* vi:set ts=8 sts=4 sw=4:
+  *
+  * VIM - Vi IMproved  by Bram Moolenaar
+  *
+  * Do ":help uganda"  in Vim to read copying and usage conditions.
+  * Do ":help credits" in Vim to see a list of people who contributed.
+  * See README.txt for an overview of the Vim source code.
+  */
+ 
+ /*
+  * json_test.c: Unittests for json.c
+  */
+ 
+ #undef NDEBUG
+ #include <assert.h>
+ 
+ /* Must include main.c because it contains much more than just main() */
+ #define NO_VIM_MAIN
+ #include "main.c"
+ 
+ /* This file has to be included because the tested functions are static */
+ #include "json.c"
+ 
+ /*
+  * Test json_find_end() with imcomplete items.
+  */
+     static void
+ test_decode_find_end(void)
+ {
+     js_read_T reader;
+ 
+     reader.js_fill = NULL;
+     reader.js_used = 0;
+ 
+     /* string and incomplete string */
+     reader.js_buf = (char_u *)"\"hello\"";
+     assert(json_find_end(&reader) == OK);
+     reader.js_buf = (char_u *)"  \"hello\" ";
+     assert(json_find_end(&reader) == OK);
+     reader.js_buf = (char_u *)"\"hello";
+     assert(json_find_end(&reader) == MAYBE);
+ 
+     /* number and dash (incomplete number) */
+     reader.js_buf = (char_u *)"123";
+     assert(json_find_end(&reader) == OK);
+     reader.js_buf = (char_u *)"-";
+     assert(json_find_end(&reader) == MAYBE);
+ 
+     /* false, true and null, also incomplete */
+     reader.js_buf = (char_u *)"false";
+     assert(json_find_end(&reader) == OK);
+     reader.js_buf = (char_u *)"f";
+     assert(json_find_end(&reader) == MAYBE);
+     reader.js_buf = (char_u *)"fa";
+     assert(json_find_end(&reader) == MAYBE);
+     reader.js_buf = (char_u *)"fal";
+     assert(json_find_end(&reader) == MAYBE);
+     reader.js_buf = (char_u *)"fals";
+     assert(json_find_end(&reader) == MAYBE);
+ 
+     reader.js_buf = (char_u *)"true";
+     assert(json_find_end(&reader) == OK);
+     reader.js_buf = (char_u *)"t";
+     assert(json_find_end(&reader) == MAYBE);
+     reader.js_buf = (char_u *)"tr";
+     assert(json_find_end(&reader) == MAYBE);
+     reader.js_buf = (char_u *)"tru";
+     assert(json_find_end(&reader) == MAYBE);
+ 
+     reader.js_buf = (char_u *)"null";
+     assert(json_find_end(&reader) == OK);
+     reader.js_buf = (char_u *)"n";
+     assert(json_find_end(&reader) == MAYBE);
+     reader.js_buf = (char_u *)"nu";
+     assert(json_find_end(&reader) == MAYBE);
+     reader.js_buf = (char_u *)"nul";
+     assert(json_find_end(&reader) == MAYBE);
+ 
+     /* object without white space */
+     reader.js_buf = (char_u *)"{\"a\":123}";
+     assert(json_find_end(&reader) == OK);
+     reader.js_buf = (char_u *)"{\"a\":123";
+     assert(json_find_end(&reader) == MAYBE);
+     reader.js_buf = (char_u *)"{\"a\":";
+     assert(json_find_end(&reader) == MAYBE);
+     reader.js_buf = (char_u *)"{\"a\"";
+     assert(json_find_end(&reader) == MAYBE);
+     reader.js_buf = (char_u *)"{\"a";
+     assert(json_find_end(&reader) == MAYBE);
+     reader.js_buf = (char_u *)"{\"";
+     assert(json_find_end(&reader) == MAYBE);
+     reader.js_buf = (char_u *)"{";
+     assert(json_find_end(&reader) == MAYBE);
+ 
+     /* object with white space */
+     reader.js_buf = (char_u *)"  {  \"a\"  :  123  }  ";
+     assert(json_find_end(&reader) == OK);
+     reader.js_buf = (char_u *)"  {  \"a\"  :  123  ";
+     assert(json_find_end(&reader) == MAYBE);
+     reader.js_buf = (char_u *)"  {  \"a\"  :  ";
+     assert(json_find_end(&reader) == MAYBE);
+     reader.js_buf = (char_u *)"  {  \"a\"  ";
+     assert(json_find_end(&reader) == MAYBE);
+     reader.js_buf = (char_u *)"  {  \"a  ";
+     assert(json_find_end(&reader) == MAYBE);
+     reader.js_buf = (char_u *)"  {   ";
+     assert(json_find_end(&reader) == MAYBE);
+ 
+     /* array without white space */
+     reader.js_buf = (char_u *)"[\"a\",123]";
+     assert(json_find_end(&reader) == OK);
+     reader.js_buf = (char_u *)"[\"a\",123";
+     assert(json_find_end(&reader) == MAYBE);
+     reader.js_buf = (char_u *)"[\"a\",";
+     assert(json_find_end(&reader) == MAYBE);
+     reader.js_buf = (char_u *)"[\"a\"";
+     assert(json_find_end(&reader) == MAYBE);
+     reader.js_buf = (char_u *)"[\"a";
+     assert(json_find_end(&reader) == MAYBE);
+     reader.js_buf = (char_u *)"[\"";
+     assert(json_find_end(&reader) == MAYBE);
+     reader.js_buf = (char_u *)"[";
+     assert(json_find_end(&reader) == MAYBE);
+ 
+     /* array with white space */
+     reader.js_buf = (char_u *)"  [  \"a\"  ,  123  ]  ";
+     assert(json_find_end(&reader) == OK);
+     reader.js_buf = (char_u *)"  [  \"a\"  ,  123  ";
+     assert(json_find_end(&reader) == MAYBE);
+     reader.js_buf = (char_u *)"  [  \"a\"  ,  ";
+     assert(json_find_end(&reader) == MAYBE);
+     reader.js_buf = (char_u *)"  [  \"a\"  ";
+     assert(json_find_end(&reader) == MAYBE);
+     reader.js_buf = (char_u *)"  [  \"a  ";
+     assert(json_find_end(&reader) == MAYBE);
+     reader.js_buf = (char_u *)"  [  ";
+     assert(json_find_end(&reader) == MAYBE);
+ }
+ 
+     static int
+ fill_from_cookie(js_read_T *reader)
+ {
+     reader->js_buf = reader->js_cookie;
+     return TRUE;
+ }
+ 
+ /*
+  * Test json_find_end with an incomplete array, calling the fill function.
+  */
+     static void
+ test_fill_called_on_find_end(void)
+ {
+     js_read_T reader;
+ 
+     reader.js_fill = fill_from_cookie;
+     reader.js_used = 0;
+     reader.js_buf = (char_u *)"  [  \"a\"  ,  123  ";
+     reader.js_cookie =        "  [  \"a\"  ,  123  ]  ";
+     assert(json_find_end(&reader) == OK);
+     reader.js_buf = (char_u *)"  [  \"a\"  ,  ";
+     assert(json_find_end(&reader) == OK);
+     reader.js_buf = (char_u *)"  [  \"a\"  ";
+     assert(json_find_end(&reader) == OK);
+     reader.js_buf = (char_u *)"  [  \"a";
+     assert(json_find_end(&reader) == OK);
+     reader.js_buf = (char_u *)"  [  ";
+     assert(json_find_end(&reader) == OK);
+ }
+ 
+ /*
+  * Test json_find_end with an incomplete string, calling the fill function.
+  */
+     static void
+ test_fill_called_on_string(void)
+ {
+     js_read_T reader;
+ 
+     reader.js_fill = fill_from_cookie;
+     reader.js_used = 0;
+     reader.js_buf = (char_u *)" \"foo";
+     reader.js_end = reader.js_buf + STRLEN(reader.js_buf);
+     reader.js_cookie =        " \"foobar\"  ";
+     assert(json_decode_string(&reader, NULL) == OK);
+ }
+ 
+     int
+ main(void)
+ {
+     test_decode_find_end();
+     test_fill_called_on_find_end();
+     test_fill_called_on_string();
+     return 0;
+ }
*** ../vim-7.4.1237/src/memfile_test.c  2016-01-30 18:51:05.240231931 +0100
--- src/memfile_test.c  2016-02-02 16:33:20.249211514 +0100
***************
*** 25,32 ****
  #define index_to_key(i) ((i) ^ 15167)
  #define TEST_COUNT 50000
  
- static void test_mf_hash(void);
- 
  /*
   * Test mf_hash_*() functions.
   */
--- 25,30 ----
*** ../vim-7.4.1237/src/structs.h       2016-02-01 21:38:13.319011999 +0100
--- src/structs.h       2016-02-02 17:48:19.358381477 +0100
***************
*** 2687,2698 ****
  /*
   * Structure used for reading in json_decode().
   */
! typedef struct
  {
      char_u    *js_buf;        /* text to be decoded */
!     char_u    *js_end;        /* NUL in js_buf when js_eof is FALSE */
      int               js_used;        /* bytes used from js_buf */
!     int               js_eof;         /* when TRUE js_buf is all there is */
!     int               (*js_fill)(void *); /* function to fill the buffer */
!     void      *js_cookie;     /* passed to js_fill */
! } js_read_T;
--- 2687,2700 ----
  /*
   * Structure used for reading in json_decode().
   */
! struct js_reader
  {
      char_u    *js_buf;        /* text to be decoded */
!     char_u    *js_end;        /* NUL in js_buf */
      int               js_used;        /* bytes used from js_buf */
!     int               (*js_fill)(struct js_reader *);
!                               /* function to fill the buffer or NULL;
!                                  * return TRUE when the buffer was filled */
!     void      *js_cookie;     /* can be used by js_fill */
! };
! typedef struct js_reader js_read_T;
*** ../vim-7.4.1237/src/version.c       2016-02-02 12:37:57.972109556 +0100
--- src/version.c       2016-02-02 18:02:36.641490557 +0100
***************
*** 744,745 ****
--- 744,747 ----
  {   /* Add new patch number below this line */
+ /**/
+     1238,
  /**/

-- 
hundred-and-one symptoms of being an internet addict:
105. When someone asks you for your address, you tell them your URL.

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\  an exciting new programming language -- http://www.Zimbu.org        ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Raspunde prin e-mail lui