Merge branch 'rs/export-strbuf-addchars'
authorJunio C Hamano <gitster@pobox.com>
Fri, 19 Sep 2014 18:38:39 +0000 (11:38 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 19 Sep 2014 18:38:39 +0000 (11:38 -0700)
Code clean-up.

* rs/export-strbuf-addchars:
strbuf: use strbuf_addchars() for adding a char multiple times
strbuf: export strbuf_addchars()

1  2 
Documentation/technical/api-strbuf.txt
merge-recursive.c
pretty.c
strbuf.c
strbuf.h
utf8.c
index 430302c2f4ee8c3881d663b1f395234fd8cad0a3,1d8336211664be114925a7b55ddd988908cc8bf8..cca6543234a5dd15e415b9e640f518097c1ec4fd
@@@ -7,10 -7,10 +7,10 @@@ use the mem* functions than a str* one 
  Though, one has to be careful about the fact that str* functions often
  stop on NULs and that strbufs may have embedded NULs.
  
 -An strbuf is NUL terminated for convenience, but no function in the
 +A strbuf is NUL terminated for convenience, but no function in the
  strbuf API actually relies on the string being free of NULs.
  
 -strbufs has some invariants that are very important to keep in mind:
 +strbufs have some invariants that are very important to keep in mind:
  
  . The `buf` member is never NULL, so it can be used in any usual C
  string operations safely. strbuf's _have_ to be initialized either by
@@@ -56,8 -56,8 +56,8 @@@ Data structure
  * `struct strbuf`
  
  This is the string buffer structure. The `len` member can be used to
 -determine the current length of the string, and `buf` member provides access to
 -the string itself.
 +determine the current length of the string, and `buf` member provides
 +access to the string itself.
  
  Functions
  ---------
  
  * Related to the contents of the buffer
  
 +`strbuf_trim`::
 +
 +      Strip whitespace from the beginning and end of a string.
 +      Equivalent to performing `strbuf_rtrim()` followed by `strbuf_ltrim()`.
 +
  `strbuf_rtrim`::
  
        Strip whitespace from the end of a string.
  
 +`strbuf_ltrim`::
 +
 +      Strip whitespace from the beginning of a string.
 +
 +`strbuf_reencode`::
 +
 +      Replace the contents of the strbuf with a reencoded form.  Returns -1
 +      on error, 0 on success.
 +
 +`strbuf_tolower`::
 +
 +      Lowercase each character in the buffer using `tolower`.
 +
  `strbuf_cmp`::
  
        Compare two buffers. Returns an integer less than, equal to, or greater
@@@ -160,6 -142,10 +160,10 @@@ then they will free() it
  
        Add a single character to the buffer.
  
+ `strbuf_addchars`::
+       Add a character the specified number of times to the buffer.
  `strbuf_insert`::
  
        Insert data to the given position of the buffer. The remaining contents
@@@ -202,7 -188,7 +206,7 @@@ strbuf_addstr(sb, "immediate string")
  
  `strbuf_addbuf`::
  
 -      Copy the contents of an other buffer at the end of the current one.
 +      Copy the contents of another buffer at the end of the current one.
  
  `strbuf_adddup`::
  
@@@ -307,16 -293,6 +311,16 @@@ same behaviour as well
        use it unless you need the correct position in the file
        descriptor.
  
 +`strbuf_getcwd`::
 +
 +      Set the buffer to the path of the current working directory.
 +
 +`strbuf_add_absolute_path`
 +
 +      Add a path to a buffer, converting a relative path to an
 +      absolute one in the process.  Symbolic links are not
 +      resolved.
 +
  `stripspace`::
  
        Strip whitespace from a buffer. The second parameter controls if
diff --combined merge-recursive.c
index 8ab944c44cf1afd516505fe9bb80dbc06708d1af,ec0fef8d7d2e5ebb18cfd8ce4468e8f4e94755e1..22315c370aaef56222ba96e6aef7fe33bb17cd9e
@@@ -40,7 -40,7 +40,7 @@@ static struct tree *shift_tree_object(s
  
  static struct commit *make_virtual_commit(struct tree *tree, const char *comment)
  {
 -      struct commit *commit = xcalloc(1, sizeof(struct commit));
 +      struct commit *commit = alloc_commit_node();
        struct merge_remote_desc *desc = xmalloc(sizeof(*desc));
  
        desc->name = comment;
@@@ -163,15 -163,13 +163,13 @@@ static void output(struct merge_option
        if (!show(o, v))
                return;
  
-       strbuf_grow(&o->obuf, o->call_depth * 2 + 2);
-       memset(o->obuf.buf + o->obuf.len, ' ', o->call_depth * 2);
-       strbuf_setlen(&o->obuf, o->obuf.len + o->call_depth * 2);
+       strbuf_addchars(&o->obuf, ' ', o->call_depth * 2);
  
        va_start(ap, fmt);
        strbuf_vaddf(&o->obuf, fmt, ap);
        va_end(ap);
  
 -      strbuf_add(&o->obuf, "\n", 1);
 +      strbuf_addch(&o->obuf, '\n');
        if (!o->buffer_output)
                flush_output(o);
  }
@@@ -190,11 -188,9 +188,11 @@@ static void output_commit_title(struct 
                        printf(_("(bad commit)\n"));
                else {
                        const char *title;
 -                      int len = find_commit_subject(commit->buffer, &title);
 +                      const char *msg = get_commit_buffer(commit, NULL);
 +                      int len = find_commit_subject(msg, &title);
                        if (len)
                                printf("%.*s\n", len, title);
 +                      unuse_commit_buffer(commit, msg);
                }
        }
  }
@@@ -203,9 -199,7 +201,9 @@@ static int add_cacheinfo(unsigned int m
                const char *path, int stage, int refresh, int options)
  {
        struct cache_entry *ce;
 -      ce = make_cache_entry(mode, sha1 ? sha1 : null_sha1, path, stage, refresh);
 +      ce = make_cache_entry(mode, sha1 ? sha1 : null_sha1, path, stage,
 +                            (refresh ? (CE_MATCH_REFRESH |
 +                                        CE_MATCH_IGNORE_MISSING) : 0 ));
        if (!ce)
                return error(_("addinfo_cache failed for path '%s'"), path);
        return add_cache_entry(ce, options);
@@@ -267,7 -261,9 +265,7 @@@ struct tree *write_tree_from_memory(str
                active_cache_tree = cache_tree();
  
        if (!cache_tree_fully_valid(active_cache_tree) &&
 -          cache_tree_update(active_cache_tree,
 -                            (const struct cache_entry * const *)active_cache,
 -                            active_nr, 0) < 0)
 +          cache_tree_update(&the_index, 0) < 0)
                die(_("error building trees"));
  
        result = lookup_tree(active_cache_tree->sha1);
@@@ -589,48 -585,31 +587,48 @@@ static int remove_file(struct merge_opt
                        return -1;
        }
        if (update_working_directory) {
 +              if (ignore_case) {
 +                      struct cache_entry *ce;
 +                      ce = cache_file_exists(path, strlen(path), ignore_case);
 +                      if (ce && ce_stage(ce) == 0)
 +                              return 0;
 +              }
                if (remove_path(path))
                        return -1;
        }
        return 0;
  }
  
 +/* add a string to a strbuf, but converting "/" to "_" */
 +static void add_flattened_path(struct strbuf *out, const char *s)
 +{
 +      size_t i = out->len;
 +      strbuf_addstr(out, s);
 +      for (; i < out->len; i++)
 +              if (out->buf[i] == '/')
 +                      out->buf[i] = '_';
 +}
 +
  static char *unique_path(struct merge_options *o, const char *path, const char *branch)
  {
 -      char *newpath = xmalloc(strlen(path) + 1 + strlen(branch) + 8 + 1);
 +      struct strbuf newpath = STRBUF_INIT;
        int suffix = 0;
        struct stat st;
 -      char *p = newpath + strlen(path);
 -      strcpy(newpath, path);
 -      *(p++) = '~';
 -      strcpy(p, branch);
 -      for (; *p; ++p)
 -              if ('/' == *p)
 -                      *p = '_';
 -      while (string_list_has_string(&o->current_file_set, newpath) ||
 -             string_list_has_string(&o->current_directory_set, newpath) ||
 -             lstat(newpath, &st) == 0)
 -              sprintf(p, "_%d", suffix++);
 -
 -      string_list_insert(&o->current_file_set, newpath);
 -      return newpath;
 +      size_t base_len;
 +
 +      strbuf_addf(&newpath, "%s~", path);
 +      add_flattened_path(&newpath, branch);
 +
 +      base_len = newpath.len;
 +      while (string_list_has_string(&o->current_file_set, newpath.buf) ||
 +             string_list_has_string(&o->current_directory_set, newpath.buf) ||
 +             lstat(newpath.buf, &st) == 0) {
 +              strbuf_setlen(&newpath, base_len);
 +              strbuf_addf(&newpath, "_%d", suffix++);
 +      }
 +
 +      string_list_insert(&o->current_file_set, newpath.buf);
 +      return strbuf_detach(&newpath, NULL);
  }
  
  static int dir_in_way(const char *path, int check_working_copy)
@@@ -712,7 -691,7 +710,7 @@@ static int make_room_for_path(struct me
        /* Make sure leading directories are created */
        status = safe_create_leading_directories_const(path);
        if (status) {
 -              if (status == -3) {
 +              if (status == SCLD_EXISTS) {
                        /* something else exists */
                        error(msg, path, _(": perhaps a D/F conflict?"));
                        return -1;
@@@ -980,10 -959,14 +978,10 @@@ merge_file_special_markers(struct merge
        char *side2 = NULL;
        struct merge_file_info mfi;
  
 -      if (filename1) {
 -              side1 = xmalloc(strlen(branch1) + strlen(filename1) + 2);
 -              sprintf(side1, "%s:%s", branch1, filename1);
 -      }
 -      if (filename2) {
 -              side2 = xmalloc(strlen(branch2) + strlen(filename2) + 2);
 -              sprintf(side2, "%s:%s", branch2, filename2);
 -      }
 +      if (filename1)
 +              side1 = xstrfmt("%s:%s", branch1, filename1);
 +      if (filename2)
 +              side2 = xstrfmt("%s:%s", branch2, filename2);
  
        mfi = merge_file_1(o, one, a, b,
                           side1 ? side1 : branch1, side2 ? side2 : branch2);
@@@ -1999,7 -1982,7 +1997,7 @@@ int merge_recursive_generic(struct merg
                            const unsigned char **base_list,
                            struct commit **result)
  {
 -      int clean, index_fd;
 +      int clean;
        struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
        struct commit *head_commit = get_ref(head, o->branch1);
        struct commit *next_commit = get_ref(merge, o->branch2);
                }
        }
  
 -      index_fd = hold_locked_index(lock, 1);
 +      hold_locked_index(lock, 1);
        clean = merge_recursive(o, head_commit, next_commit, ca,
                        result);
        if (active_cache_changed &&
 -                      (write_cache(index_fd, active_cache, active_nr) ||
 -                       commit_locked_index(lock)))
 +          write_locked_index(&the_index, lock, COMMIT_LOCK))
                return error(_("Unable to write index."));
  
        return clean ? 0 : 1;
  }
  
 -static int merge_recursive_config(const char *var, const char *value, void *cb)
 +static void merge_recursive_config(struct merge_options *o)
  {
 -      struct merge_options *o = cb;
 -      if (!strcmp(var, "merge.verbosity")) {
 -              o->verbosity = git_config_int(var, value);
 -              return 0;
 -      }
 -      if (!strcmp(var, "diff.renamelimit")) {
 -              o->diff_rename_limit = git_config_int(var, value);
 -              return 0;
 -      }
 -      if (!strcmp(var, "merge.renamelimit")) {
 -              o->merge_rename_limit = git_config_int(var, value);
 -              return 0;
 -      }
 -      return git_xmerge_config(var, value, cb);
 +      git_config_get_int("merge.verbosity", &o->verbosity);
 +      git_config_get_int("diff.renamelimit", &o->diff_rename_limit);
 +      git_config_get_int("merge.renamelimit", &o->merge_rename_limit);
 +      git_config(git_xmerge_config, NULL);
  }
  
  void init_merge_options(struct merge_options *o)
        o->diff_rename_limit = -1;
        o->merge_rename_limit = -1;
        o->renormalize = 0;
 -      git_config(merge_recursive_config, o);
 +      merge_recursive_config(o);
        if (getenv("GIT_MERGE_VERBOSITY"))
                o->verbosity =
                        strtol(getenv("GIT_MERGE_VERBOSITY"), NULL, 10);
        if (o->verbosity >= 5)
                o->buffer_output = 0;
        strbuf_init(&o->obuf, 0);
 -      memset(&o->current_file_set, 0, sizeof(struct string_list));
 -      o->current_file_set.strdup_strings = 1;
 -      memset(&o->current_directory_set, 0, sizeof(struct string_list));
 -      o->current_directory_set.strdup_strings = 1;
 -      memset(&o->df_conflict_file_set, 0, sizeof(struct string_list));
 -      o->df_conflict_file_set.strdup_strings = 1;
 +      string_list_init(&o->current_file_set, 1);
 +      string_list_init(&o->current_directory_set, 1);
 +      string_list_init(&o->df_conflict_file_set, 1);
  }
  
  int parse_merge_opt(struct merge_options *o, const char *s)
  {
 +      const char *arg;
 +
        if (!s || !*s)
                return -1;
        if (!strcmp(s, "ours"))
                o->recursive_variant = MERGE_RECURSIVE_THEIRS;
        else if (!strcmp(s, "subtree"))
                o->subtree_shift = "";
 -      else if (!prefixcmp(s, "subtree="))
 -              o->subtree_shift = s + strlen("subtree=");
 +      else if (skip_prefix(s, "subtree=", &arg))
 +              o->subtree_shift = arg;
        else if (!strcmp(s, "patience"))
                o->xdl_opts = DIFF_WITH_ALG(o, PATIENCE_DIFF);
        else if (!strcmp(s, "histogram"))
                o->xdl_opts = DIFF_WITH_ALG(o, HISTOGRAM_DIFF);
 -      else if (!prefixcmp(s, "diff-algorithm=")) {
 -              long value = parse_algorithm_value(s + strlen("diff-algorithm="));
 +      else if (skip_prefix(s, "diff-algorithm=", &arg)) {
 +              long value = parse_algorithm_value(arg);
                if (value < 0)
                        return -1;
                /* clear out previous settings */
                o->renormalize = 1;
        else if (!strcmp(s, "no-renormalize"))
                o->renormalize = 0;
 -      else if (!prefixcmp(s, "rename-threshold=")) {
 -              const char *score = s + strlen("rename-threshold=");
 -              if ((o->rename_score = parse_rename_score(&score)) == -1 || *score != 0)
 +      else if (skip_prefix(s, "rename-threshold=", &arg)) {
 +              if ((o->rename_score = parse_rename_score(&arg)) == -1 || *arg != 0)
                        return -1;
        }
        else
diff --combined pretty.c
index 63e03b67e932786e588c7742279d1c6d8ac9df0e,c2198a335f56a1737799cee112d0f68574d23440..31f2edef1106200761aff49758545972000d9c28
+++ b/pretty.c
@@@ -24,11 -24,6 +24,11 @@@ static size_t commit_formats_len
  static size_t commit_formats_alloc;
  static struct cmt_fmt_map *find_commit_format(const char *sought);
  
 +int commit_format_is_empty(enum cmit_fmt fmt)
 +{
 +      return fmt == CMIT_FMT_USERFORMAT && !*user_format;
 +}
 +
  static void save_user_format(struct rev_info *rev, const char *cp, int is_tformat)
  {
        free(user_format);
@@@ -45,9 -40,10 +45,9 @@@ static int git_pretty_formats_config(co
        const char *fmt;
        int i;
  
 -      if (prefixcmp(var, "pretty."))
 +      if (!skip_prefix(var, "pretty.", &name))
                return 0;
  
 -      name = var + strlen("pretty.");
        for (i = 0; i < builtin_formats_len; i++) {
                if (!strcmp(commit_formats[i].name, name))
                        return 0;
  
        commit_format->name = xstrdup(name);
        commit_format->format = CMIT_FMT_USERFORMAT;
 -      git_config_string(&fmt, var, value);
 -      if (!prefixcmp(fmt, "format:") || !prefixcmp(fmt, "tformat:")) {
 +      if (git_config_string(&fmt, var, value))
 +              return -1;
 +
 +      if (starts_with(fmt, "format:") || starts_with(fmt, "tformat:")) {
                commit_format->is_tformat = fmt[0] == 't';
                fmt = strchr(fmt, ':') + 1;
        } else if (strchr(fmt, '%'))
@@@ -121,7 -115,7 +121,7 @@@ static struct cmt_fmt_map *find_commit_
        for (i = 0; i < commit_formats_len; i++) {
                size_t match_len;
  
 -              if (prefixcmp(commit_formats[i].name, sought))
 +              if (!starts_with(commit_formats[i].name, sought))
                        continue;
  
                match_len = strlen(commit_formats[i].name);
@@@ -153,16 -147,16 +153,16 @@@ void get_commit_format(const char *arg
        struct cmt_fmt_map *commit_format;
  
        rev->use_terminator = 0;
 -      if (!arg || !*arg) {
 +      if (!arg) {
                rev->commit_format = CMIT_FMT_DEFAULT;
                return;
        }
 -      if (!prefixcmp(arg, "format:") || !prefixcmp(arg, "tformat:")) {
 +      if (starts_with(arg, "format:") || starts_with(arg, "tformat:")) {
                save_user_format(rev, strchr(arg, ':') + 1, arg[0] == 't');
                return;
        }
  
 -      if (strchr(arg, '%')) {
 +      if (!*arg || strchr(arg, '%')) {
                save_user_format(rev, arg, 1);
                return;
        }
@@@ -399,22 -393,16 +399,22 @@@ static void add_rfc2047(struct strbuf *
        strbuf_addstr(sb, "?=");
  }
  
 -static const char *show_ident_date(const struct ident_split *ident,
 -                                 enum date_mode mode)
 +const char *show_ident_date(const struct ident_split *ident,
 +                          enum date_mode mode)
  {
        unsigned long date = 0;
 -      int tz = 0;
 +      long tz = 0;
  
        if (ident->date_begin && ident->date_end)
                date = strtoul(ident->date_begin, NULL, 10);
 -      if (ident->tz_begin && ident->tz_end)
 -              tz = strtol(ident->tz_begin, NULL, 10);
 +      if (date_overflows(date))
 +              date = 0;
 +      else {
 +              if (ident->tz_begin && ident->tz_end)
 +                      tz = strtol(ident->tz_begin, NULL, 10);
 +              if (tz >= INT_MAX || tz <= INT_MIN)
 +                      tz = 0;
 +      }
        return show_date(date, tz, mode);
  }
  
@@@ -554,11 -542,32 +554,11 @@@ static void add_merge_info(const struc
        strbuf_addch(sb, '\n');
  }
  
 -static char *get_header(const struct commit *commit, const char *msg,
 -                      const char *key)
 +static char *get_header(const char *msg, const char *key)
  {
 -      int key_len = strlen(key);
 -      const char *line = msg;
 -
 -      while (line) {
 -              const char *eol = strchr(line, '\n'), *next;
 -
 -              if (line == eol)
 -                      return NULL;
 -              if (!eol) {
 -                      warning("malformed commit (header is missing newline): %s",
 -                              sha1_to_hex(commit->object.sha1));
 -                      eol = line + strlen(line);
 -                      next = NULL;
 -              } else
 -                      next = eol + 1;
 -              if (eol - line > key_len &&
 -                  !strncmp(line, key, key_len) &&
 -                  line[key_len] == ' ') {
 -                      return xmemdupz(line + key_len + 1, eol - line - key_len - 1);
 -              }
 -              line = next;
 -      }
 -      return NULL;
 +      size_t len;
 +      const char *v = find_commit_header(msg, key, &len);
 +      return v ? xmemdupz(v, len) : NULL;
  }
  
  static char *replace_encoding_header(char *buf, const char *encoding)
        return strbuf_detach(&tmp, NULL);
  }
  
 -char *logmsg_reencode(const struct commit *commit,
 -                    char **commit_encoding,
 -                    const char *output_encoding)
 +const char *logmsg_reencode(const struct commit *commit,
 +                          char **commit_encoding,
 +                          const char *output_encoding)
  {
        static const char *utf8 = "UTF-8";
        const char *use_encoding;
        char *encoding;
 -      char *msg = commit->buffer;
 +      const char *msg = get_commit_buffer(commit, NULL);
        char *out;
  
 -      if (!msg) {
 -              enum object_type type;
 -              unsigned long size;
 -
 -              msg = read_sha1_file(commit->object.sha1, &type, &size);
 -              if (!msg)
 -                      die("Cannot read commit object %s",
 -                          sha1_to_hex(commit->object.sha1));
 -              if (type != OBJ_COMMIT)
 -                      die("Expected commit for '%s', got %s",
 -                          sha1_to_hex(commit->object.sha1), typename(type));
 -      }
 -
        if (!output_encoding || !*output_encoding) {
                if (commit_encoding)
 -                      *commit_encoding =
 -                              get_header(commit, msg, "encoding");
 +                      *commit_encoding = get_header(msg, "encoding");
                return msg;
        }
 -      encoding = get_header(commit, msg, "encoding");
 +      encoding = get_header(msg, "encoding");
        if (commit_encoding)
                *commit_encoding = encoding;
        use_encoding = encoding ? encoding : utf8;
                 * Otherwise, we still want to munge the encoding header in the
                 * result, which will be done by modifying the buffer. If we
                 * are using a fresh copy, we can reuse it. But if we are using
 -               * the cached copy from commit->buffer, we need to duplicate it
 -               * to avoid munging commit->buffer.
 +               * the cached copy from get_commit_buffer, we need to duplicate it
 +               * to avoid munging the cached copy.
                 */
 -              out = msg;
 -              if (out == commit->buffer)
 -                      out = xstrdup(out);
 +              if (msg == get_cached_commit_buffer(commit, NULL))
 +                      out = xstrdup(msg);
 +              else
 +                      out = (char *)msg;
        }
        else {
                /*
                 * copy, we can free it.
                 */
                out = reencode_string(msg, output_encoding, use_encoding);
 -              if (out && msg != commit->buffer)
 -                      free(msg);
 +              if (out)
 +                      unuse_commit_buffer(commit, msg);
        }
  
        /*
        return out ? out : msg;
  }
  
 -void logmsg_free(char *msg, const struct commit *commit)
 -{
 -      if (msg != commit->buffer)
 -              free(msg);
 -}
 -
  static int mailmap_name(const char **email, size_t *email_len,
                        const char **name, size_t *name_len)
  {
@@@ -717,12 -745,9 +717,12 @@@ static size_t format_person_part(struc
        case 'r':       /* date, relative */
                strbuf_addstr(sb, show_ident_date(&s, DATE_RELATIVE));
                return placeholder_len;
 -      case 'i':       /* date, ISO 8601 */
 +      case 'i':       /* date, ISO 8601-like */
                strbuf_addstr(sb, show_ident_date(&s, DATE_ISO8601));
                return placeholder_len;
 +      case 'I':       /* date, ISO 8601 strict */
 +              strbuf_addstr(sb, show_ident_date(&s, DATE_ISO8601_STRICT));
 +              return placeholder_len;
        }
  
  skip:
@@@ -766,7 -791,7 +766,7 @@@ struct format_commit_context 
        struct signature_check signature_check;
        enum flush_type flush_type;
        enum trunc_type truncate;
 -      char *message;
 +      const char *message;
        char *commit_encoding;
        size_t width, indent1, indent2;
        int auto_color;
@@@ -815,10 -840,10 +815,10 @@@ static void parse_commit_header(struct 
  
                if (i == eol) {
                        break;
 -              } else if (!prefixcmp(msg + i, "author ")) {
 +              } else if (starts_with(msg + i, "author ")) {
                        context->author.off = i + 7;
                        context->author.len = eol - i - 7;
 -              } else if (!prefixcmp(msg + i, "committer ")) {
 +              } else if (starts_with(msg + i, "committer ")) {
                        context->committer.off = i + 10;
                        context->committer.len = eol - i - 10;
                }
@@@ -958,7 -983,7 +958,7 @@@ static size_t parse_color(struct strbu
  
                if (!end)
                        return 0;
 -              if (!prefixcmp(begin, "auto,")) {
 +              if (starts_with(begin, "auto,")) {
                        if (!want_color(c->pretty_ctx->color))
                                return end - placeholder + 1;
                        begin += 5;
                strbuf_addstr(sb, color);
                return end - placeholder + 1;
        }
 -      if (!prefixcmp(placeholder + 1, "red")) {
 +      if (starts_with(placeholder + 1, "red")) {
                strbuf_addstr(sb, GIT_COLOR_RED);
                return 4;
 -      } else if (!prefixcmp(placeholder + 1, "green")) {
 +      } else if (starts_with(placeholder + 1, "green")) {
                strbuf_addstr(sb, GIT_COLOR_GREEN);
                return 6;
 -      } else if (!prefixcmp(placeholder + 1, "blue")) {
 +      } else if (starts_with(placeholder + 1, "blue")) {
                strbuf_addstr(sb, GIT_COLOR_BLUE);
                return 5;
 -      } else if (!prefixcmp(placeholder + 1, "reset")) {
 +      } else if (starts_with(placeholder + 1, "reset")) {
                strbuf_addstr(sb, GIT_COLOR_RESET);
                return 6;
        } else
@@@ -1035,11 -1060,11 +1035,11 @@@ static size_t parse_padding_placeholder
                        end = strchr(start, ')');
                        if (!end || end == start)
                                return 0;
 -                      if (!prefixcmp(start, "trunc)"))
 +                      if (starts_with(start, "trunc)"))
                                c->truncate = trunc_right;
 -                      else if (!prefixcmp(start, "ltrunc)"))
 +                      else if (starts_with(start, "ltrunc)"))
                                c->truncate = trunc_left;
 -                      else if (!prefixcmp(start, "mtrunc)"))
 +                      else if (starts_with(start, "mtrunc)"))
                                c->truncate = trunc_middle;
                        else
                                return 0;
@@@ -1064,7 -1089,7 +1064,7 @@@ static size_t format_commit_one(struct 
        /* these are independent of the commit */
        switch (placeholder[0]) {
        case 'C':
 -              if (!prefixcmp(placeholder + 1, "(auto)")) {
 +              if (starts_with(placeholder + 1, "(auto)")) {
                        c->auto_color = 1;
                        return 7; /* consumed 7 bytes, "C(auto)" */
                } else {
                        if (c->signature_check.key)
                                strbuf_addstr(sb, c->signature_check.key);
                        break;
 +              default:
 +                      return 0;
                }
                return 2;
        }
@@@ -1365,7 -1388,7 +1365,7 @@@ static size_t format_and_pad_commit(str
                case trunc_none:
                        break;
                }
 -              strbuf_addstr(sb, local_sb.buf);
 +              strbuf_addbuf(sb, &local_sb);
        } else {
                int sb_len = sb->len, offset = 0;
                if (c->flush_type == flush_left)
                 * convert it back to chars
                 */
                padding = padding - len + local_sb.len;
-               strbuf_grow(sb, padding);
-               strbuf_setlen(sb, sb_len + padding);
-               memset(sb->buf + sb_len, ' ', sb->len - sb_len);
+               strbuf_addchars(sb, ' ', padding);
                memcpy(sb->buf + sb_len + offset, local_sb.buf,
                       local_sb.len);
        }
@@@ -1478,18 -1499,13 +1476,18 @@@ void format_commit_message(const struc
        context.commit = commit;
        context.pretty_ctx = pretty_ctx;
        context.wrap_start = sb->len;
 +      /*
 +       * convert a commit message to UTF-8 first
 +       * as far as 'format_commit_item' assumes it in UTF-8
 +       */
        context.message = logmsg_reencode(commit,
                                          &context.commit_encoding,
 -                                        output_enc);
 +                                        utf8);
  
        strbuf_expand(sb, format, format_commit_item, &context);
        rewrap_message_tail(sb, &context, 0, 0, 0);
  
 +      /* then convert a commit message to an actual output encoding */
        if (output_enc) {
                if (same_encoding(utf8, output_enc))
                        output_enc = NULL;
        }
  
        free(context.commit_encoding);
 -      logmsg_free(context.message, commit);
 -      free(context.signature_check.gpg_output);
 -      free(context.signature_check.signer);
 +      unuse_commit_buffer(commit, context.message);
  }
  
  static void pp_header(struct pretty_print_context *pp,
                        continue;
                }
  
 -              if (!prefixcmp(line, "parent ")) {
 +              if (starts_with(line, "parent ")) {
                        if (linelen != 48)
                                die("bad parent line in commit");
                        continue;
                }
  
                if (!parents_shown) {
 -                      struct commit_list *parent;
 -                      int num;
 -                      for (parent = commit->parents, num = 0;
 -                           parent;
 -                           parent = parent->next, num++)
 -                              ;
 +                      unsigned num = commit_list_count(commit->parents);
                        /* with enough slop */
                        strbuf_grow(sb, num * 50 + 20);
                        add_merge_info(pp, sb, commit);
                 * FULL shows both authors but not dates.
                 * FULLER shows both authors and dates.
                 */
 -              if (!prefixcmp(line, "author ")) {
 +              if (starts_with(line, "author ")) {
                        strbuf_grow(sb, linelen + 80);
                        pp_user_info(pp, "Author", sb, line + 7, encoding);
                }
 -              if (!prefixcmp(line, "committer ") &&
 +              if (starts_with(line, "committer ") &&
                    (pp->fmt == CMIT_FMT_FULL || pp->fmt == CMIT_FMT_FULLER)) {
                        strbuf_grow(sb, linelen + 80);
                        pp_user_info(pp, "Commit", sb, line + 10, encoding);
@@@ -1654,10 -1677,8 +1652,8 @@@ void pp_remainder(struct pretty_print_c
                first = 0;
  
                strbuf_grow(sb, linelen + indent + 20);
-               if (indent) {
-                       memset(sb->buf + sb->len, ' ', indent);
-                       strbuf_setlen(sb, sb->len + indent);
-               }
+               if (indent)
+                       strbuf_addchars(sb, ' ', indent);
                strbuf_add(sb, line, linelen);
                strbuf_addch(sb, '\n');
        }
@@@ -1670,7 -1691,7 +1666,7 @@@ void pretty_print_commit(struct pretty_
        unsigned long beginning_of_body;
        int indent = 4;
        const char *msg;
 -      char *reencoded;
 +      const char *reencoded;
        const char *encoding;
        int need_8bit_cte = pp->need_8bit_cte;
  
        if (pp->fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body)
                strbuf_addch(sb, '\n');
  
 -      logmsg_free(reencoded, commit);
 +      unuse_commit_buffer(commit, reencoded);
  }
  
  void pp_commit_easy(enum cmit_fmt fmt, const struct commit *commit,
diff --combined strbuf.c
index 4d3144308feaf3fdc541b582ed589962c6c459c1,5e44cf9204a5845cc001a6b0fbd63f436fde7673..0346e74a47d14ef13757cadfe2a0de778b5c4cc4
+++ b/strbuf.c
@@@ -1,14 -1,22 +1,14 @@@
  #include "cache.h"
  #include "refs.h"
 +#include "utf8.h"
  
 -int prefixcmp(const char *str, const char *prefix)
 +int starts_with(const char *str, const char *prefix)
  {
        for (; ; str++, prefix++)
                if (!*prefix)
 -                      return 0;
 +                      return 1;
                else if (*str != *prefix)
 -                      return (unsigned char)*prefix - (unsigned char)*str;
 -}
 -
 -int suffixcmp(const char *str, const char *suffix)
 -{
 -      int len = strlen(str), suflen = strlen(suffix);
 -      if (len < suflen)
 -              return -1;
 -      else
 -              return strcmp(str + len - suflen, suffix);
 +                      return 0;
  }
  
  /*
@@@ -70,8 -78,15 +70,8 @@@ void strbuf_grow(struct strbuf *sb, siz
  
  void strbuf_trim(struct strbuf *sb)
  {
 -      char *b = sb->buf;
 -      while (sb->len > 0 && isspace((unsigned char)sb->buf[sb->len - 1]))
 -              sb->len--;
 -      while (sb->len > 0 && isspace(*b)) {
 -              b++;
 -              sb->len--;
 -      }
 -      memmove(sb->buf, b, sb->len);
 -      sb->buf[sb->len] = '\0';
 +      strbuf_rtrim(sb);
 +      strbuf_ltrim(sb);
  }
  void strbuf_rtrim(struct strbuf *sb)
  {
@@@ -91,29 -106,6 +91,29 @@@ void strbuf_ltrim(struct strbuf *sb
        sb->buf[sb->len] = '\0';
  }
  
 +int strbuf_reencode(struct strbuf *sb, const char *from, const char *to)
 +{
 +      char *out;
 +      int len;
 +
 +      if (same_encoding(from, to))
 +              return 0;
 +
 +      out = reencode_string_len(sb->buf, sb->len, to, from, &len);
 +      if (!out)
 +              return -1;
 +
 +      strbuf_attach(sb, out, len, len);
 +      return 0;
 +}
 +
 +void strbuf_tolower(struct strbuf *sb)
 +{
 +      char *p = sb->buf, *end = sb->buf + sb->len;
 +      for (; p < end; p++)
 +              *p = tolower(*p);
 +}
 +
  struct strbuf **strbuf_split_buf(const char *str, size_t slen,
                                 int terminator, int max)
  {
@@@ -204,6 -196,13 +204,13 @@@ void strbuf_adddup(struct strbuf *sb, s
        strbuf_setlen(sb, sb->len + len);
  }
  
+ void strbuf_addchars(struct strbuf *sb, int c, size_t n)
+ {
+       strbuf_grow(sb, n);
+       memset(sb->buf + sb->len, c, n);
+       strbuf_setlen(sb, sb->len + n);
+ }
  void strbuf_addf(struct strbuf *sb, const char *fmt, ...)
  {
        va_list ap;
@@@ -406,27 -405,6 +413,27 @@@ int strbuf_readlink(struct strbuf *sb, 
        return -1;
  }
  
 +int strbuf_getcwd(struct strbuf *sb)
 +{
 +      size_t oldalloc = sb->alloc;
 +      size_t guessed_len = 128;
 +
 +      for (;; guessed_len *= 2) {
 +              strbuf_grow(sb, guessed_len);
 +              if (getcwd(sb->buf, sb->alloc)) {
 +                      strbuf_setlen(sb, strlen(sb->buf));
 +                      return 0;
 +              }
 +              if (errno != ERANGE)
 +                      break;
 +      }
 +      if (oldalloc == 0)
 +              strbuf_release(sb);
 +      else
 +              strbuf_reset(sb);
 +      return -1;
 +}
 +
  int strbuf_getwholeline(struct strbuf *sb, FILE *fp, int term)
  {
        int ch;
@@@ -576,31 -554,6 +583,31 @@@ void strbuf_humanise_bytes(struct strbu
        }
  }
  
 +void strbuf_add_absolute_path(struct strbuf *sb, const char *path)
 +{
 +      if (!*path)
 +              die("The empty string is not a valid path");
 +      if (!is_absolute_path(path)) {
 +              struct stat cwd_stat, pwd_stat;
 +              size_t orig_len = sb->len;
 +              char *cwd = xgetcwd();
 +              char *pwd = getenv("PWD");
 +              if (pwd && strcmp(pwd, cwd) &&
 +                  !stat(cwd, &cwd_stat) &&
 +                  (cwd_stat.st_dev || cwd_stat.st_ino) &&
 +                  !stat(pwd, &pwd_stat) &&
 +                  pwd_stat.st_dev == cwd_stat.st_dev &&
 +                  pwd_stat.st_ino == cwd_stat.st_ino)
 +                      strbuf_addstr(sb, pwd);
 +              else
 +                      strbuf_addstr(sb, cwd);
 +              if (sb->len > orig_len && !is_dir_sep(sb->buf[sb->len - 1]))
 +                      strbuf_addch(sb, '/');
 +              free(cwd);
 +      }
 +      strbuf_addstr(sb, path);
 +}
 +
  int printf_ln(const char *fmt, ...)
  {
        int ret;
@@@ -624,35 -577,3 +631,35 @@@ int fprintf_ln(FILE *fp, const char *fm
                return -1;
        return ret + 1;
  }
 +
 +char *xstrdup_tolower(const char *string)
 +{
 +      char *result;
 +      size_t len, i;
 +
 +      len = strlen(string);
 +      result = xmalloc(len + 1);
 +      for (i = 0; i < len; i++)
 +              result[i] = tolower(string[i]);
 +      result[i] = '\0';
 +      return result;
 +}
 +
 +char *xstrvfmt(const char *fmt, va_list ap)
 +{
 +      struct strbuf buf = STRBUF_INIT;
 +      strbuf_vaddf(&buf, fmt, ap);
 +      return strbuf_detach(&buf, NULL);
 +}
 +
 +char *xstrfmt(const char *fmt, ...)
 +{
 +      va_list ap;
 +      char *ret;
 +
 +      va_start(ap, fmt);
 +      ret = xstrvfmt(fmt, ap);
 +      va_end(ap);
 +
 +      return ret;
 +}
diff --combined strbuf.h
index 7bdc1da50732bacad3ba33526ba10ee8f7e200a4,2c34753a6877bfdbb851d50436f10c2626f57716..652b6c432b325aaed29d7d374994a2a95d11a7c8
+++ b/strbuf.h
@@@ -17,23 -17,20 +17,23 @@@ extern void strbuf_init(struct strbuf *
  extern void strbuf_release(struct strbuf *);
  extern char *strbuf_detach(struct strbuf *, size_t *);
  extern void strbuf_attach(struct strbuf *, void *, size_t, size_t);
 -static inline void strbuf_swap(struct strbuf *a, struct strbuf *b) {
 +static inline void strbuf_swap(struct strbuf *a, struct strbuf *b)
 +{
        struct strbuf tmp = *a;
        *a = *b;
        *b = tmp;
  }
  
  /*----- strbuf size related -----*/
 -static inline size_t strbuf_avail(const struct strbuf *sb) {
 +static inline size_t strbuf_avail(const struct strbuf *sb)
 +{
        return sb->alloc ? sb->alloc - sb->len - 1 : 0;
  }
  
  extern void strbuf_grow(struct strbuf *, size_t);
  
 -static inline void strbuf_setlen(struct strbuf *sb, size_t len) {
 +static inline void strbuf_setlen(struct strbuf *sb, size_t len)
 +{
        if (len > (sb->alloc ? sb->alloc - 1 : 0))
                die("BUG: strbuf_setlen() beyond buffer");
        sb->len = len;
  extern void strbuf_trim(struct strbuf *);
  extern void strbuf_rtrim(struct strbuf *);
  extern void strbuf_ltrim(struct strbuf *);
 +extern int strbuf_reencode(struct strbuf *sb, const char *from, const char *to);
 +extern void strbuf_tolower(struct strbuf *sb);
  extern int strbuf_cmp(const struct strbuf *, const struct strbuf *);
  
 +static inline int strbuf_strip_suffix(struct strbuf *sb, const char *suffix)
 +{
 +      if (strip_suffix_mem(sb->buf, &sb->len, suffix)) {
 +              strbuf_setlen(sb, sb->len);
 +              return 1;
 +      } else
 +              return 0;
 +}
 +
  /*
   * Split str (of length slen) at the specified terminator character.
   * Return a null-terminated array of pointers to strbuf objects
@@@ -111,8 -97,7 +111,8 @@@ static inline struct strbuf **strbuf_sp
  extern void strbuf_list_free(struct strbuf **);
  
  /*----- add data in your buffer -----*/
 -static inline void strbuf_addch(struct strbuf *sb, int c) {
 +static inline void strbuf_addch(struct strbuf *sb, int c)
 +{
        strbuf_grow(sb, 1);
        sb->buf[sb->len++] = c;
        sb->buf[sb->len] = '\0';
@@@ -128,16 -113,15 +128,17 @@@ extern void strbuf_splice(struct strbu
  extern void strbuf_add_commented_lines(struct strbuf *out, const char *buf, size_t size);
  
  extern void strbuf_add(struct strbuf *, const void *, size_t);
 -static inline void strbuf_addstr(struct strbuf *sb, const char *s) {
 +static inline void strbuf_addstr(struct strbuf *sb, const char *s)
 +{
        strbuf_add(sb, s, strlen(s));
  }
 -static inline void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2) {
 +static inline void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2)
 +{
        strbuf_grow(sb, sb2->len);
        strbuf_add(sb, sb2->buf, sb2->len);
  }
  extern void strbuf_adddup(struct strbuf *sb, size_t pos, size_t len);
+ extern void strbuf_addchars(struct strbuf *sb, int c, size_t n);
  
  typedef size_t (*expand_fn_t) (struct strbuf *sb, const char *placeholder, void *context);
  extern void strbuf_expand(struct strbuf *sb, const char *format, expand_fn_t fn, void *context);
@@@ -174,7 -158,6 +175,7 @@@ extern size_t strbuf_fread(struct strbu
  extern ssize_t strbuf_read(struct strbuf *, int fd, size_t hint);
  extern int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint);
  extern int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint);
 +extern int strbuf_getcwd(struct strbuf *sb);
  
  extern int strbuf_getwholeline(struct strbuf *, FILE *, int);
  extern int strbuf_getline(struct strbuf *, FILE *, int);
@@@ -190,22 -173,9 +191,22 @@@ extern void strbuf_addstr_urlencode(str
                                    int reserved);
  extern void strbuf_humanise_bytes(struct strbuf *buf, off_t bytes);
  
 +extern void strbuf_add_absolute_path(struct strbuf *sb, const char *path);
 +
  __attribute__((format (printf,1,2)))
  extern int printf_ln(const char *fmt, ...);
  __attribute__((format (printf,2,3)))
  extern int fprintf_ln(FILE *fp, const char *fmt, ...);
  
 +char *xstrdup_tolower(const char *);
 +
 +/*
 + * Create a newly allocated string using printf format. You can do this easily
 + * with a strbuf, but this provides a shortcut to save a few lines.
 + */
 +__attribute__((format (printf, 1, 0)))
 +char *xstrvfmt(const char *fmt, va_list ap);
 +__attribute__((format (printf, 1, 2)))
 +char *xstrfmt(const char *fmt, ...);
 +
  #endif /* STRBUF_H */
diff --combined utf8.c
index 401a6a509e8b4221a539cec6ad67721fc620d64e,c55f3ba0bc96c0993d4bae7f45fb00539c44d1c1..454177794929933a6ccc4d91e7d5c784f0f07bff
--- 1/utf8.c
--- 2/utf8.c
+++ b/utf8.c
@@@ -5,8 -5,8 +5,8 @@@
  /* This code is originally from http://www.cl.cam.ac.uk/~mgk25/ucs/ */
  
  struct interval {
 -  int first;
 -  int last;
 +      ucs_char_t first;
 +      ucs_char_t last;
  };
  
  size_t display_mode_esc_sequence_len(const char *s)
@@@ -80,8 -80,53 +80,8 @@@ static int git_wcwidth(ucs_char_t ch
  {
        /*
         * Sorted list of non-overlapping intervals of non-spacing characters,
 -       * generated by
 -       *   "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c".
         */
 -      static const struct interval combining[] = {
 -              { 0x0300, 0x0357 }, { 0x035D, 0x036F }, { 0x0483, 0x0486 },
 -              { 0x0488, 0x0489 }, { 0x0591, 0x05A1 }, { 0x05A3, 0x05B9 },
 -              { 0x05BB, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 },
 -              { 0x05C4, 0x05C4 }, { 0x0600, 0x0603 }, { 0x0610, 0x0615 },
 -              { 0x064B, 0x0658 }, { 0x0670, 0x0670 }, { 0x06D6, 0x06E4 },
 -              { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED }, { 0x070F, 0x070F },
 -              { 0x0711, 0x0711 }, { 0x0730, 0x074A }, { 0x07A6, 0x07B0 },
 -              { 0x0901, 0x0902 }, { 0x093C, 0x093C }, { 0x0941, 0x0948 },
 -              { 0x094D, 0x094D }, { 0x0951, 0x0954 }, { 0x0962, 0x0963 },
 -              { 0x0981, 0x0981 }, { 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 },
 -              { 0x09CD, 0x09CD }, { 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 },
 -              { 0x0A3C, 0x0A3C }, { 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 },
 -              { 0x0A4B, 0x0A4D }, { 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 },
 -              { 0x0ABC, 0x0ABC }, { 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 },
 -              { 0x0ACD, 0x0ACD }, { 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 },
 -              { 0x0B3C, 0x0B3C }, { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 },
 -              { 0x0B4D, 0x0B4D }, { 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 },
 -              { 0x0BC0, 0x0BC0 }, { 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 },
 -              { 0x0C46, 0x0C48 }, { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 },
 -              { 0x0CBC, 0x0CBC }, { 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 },
 -              { 0x0CCC, 0x0CCD }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D },
 -              { 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 },
 -              { 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E },
 -              { 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC },
 -              { 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 },
 -              { 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E },
 -              { 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 },
 -              { 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 },
 -              { 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 },
 -              { 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x1712, 0x1714 },
 -              { 0x1732, 0x1734 }, { 0x1752, 0x1753 }, { 0x1772, 0x1773 },
 -              { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD }, { 0x17C6, 0x17C6 },
 -              { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD }, { 0x180B, 0x180D },
 -              { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 }, { 0x1927, 0x1928 },
 -              { 0x1932, 0x1932 }, { 0x1939, 0x193B }, { 0x200B, 0x200F },
 -              { 0x202A, 0x202E }, { 0x2060, 0x2063 }, { 0x206A, 0x206F },
 -              { 0x20D0, 0x20EA }, { 0x302A, 0x302F }, { 0x3099, 0x309A },
 -              { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F }, { 0xFE20, 0xFE23 },
 -              { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB }, { 0x1D167, 0x1D169 },
 -              { 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B },
 -              { 0x1D1AA, 0x1D1AD }, { 0xE0001, 0xE0001 },
 -              { 0xE0020, 0xE007F }, { 0xE0100, 0xE01EF }
 -      };
 +#include "unicode_width.h"
  
        /* test for 8-bit control characters */
        if (ch == 0)
                return -1;
  
        /* binary search in table of non-spacing characters */
 -      if (bisearch(ch, combining, sizeof(combining)
 +      if (bisearch(ch, zero_width, sizeof(zero_width)
                                / sizeof(struct interval) - 1))
                return 0;
  
 -      /*
 -       * If we arrive here, ch is neither a combining nor a C0/C1
 -       * control character.
 -       */
 +      /* binary search in table of double width characters */
 +      if (bisearch(ch, double_width, sizeof(double_width)
 +                              / sizeof(struct interval) - 1))
 +              return 2;
  
 -      return 1 +
 -              (ch >= 0x1100 &&
 -                    /* Hangul Jamo init. consonants */
 -               (ch <= 0x115f ||
 -                ch == 0x2329 || ch == 0x232a ||
 -                  /* CJK ... Yi */
 -                (ch >= 0x2e80 && ch <= 0xa4cf &&
 -                 ch != 0x303f) ||
 -                /* Hangul Syllables */
 -                (ch >= 0xac00 && ch <= 0xd7a3) ||
 -                /* CJK Compatibility Ideographs */
 -                (ch >= 0xf900 && ch <= 0xfaff) ||
 -                /* CJK Compatibility Forms */
 -                (ch >= 0xfe30 && ch <= 0xfe6f) ||
 -                /* Fullwidth Forms */
 -                (ch >= 0xff00 && ch <= 0xff60) ||
 -                (ch >= 0xffe0 && ch <= 0xffe6) ||
 -                (ch >= 0x20000 && ch <= 0x2fffd) ||
 -                (ch >= 0x30000 && ch <= 0x3fffd)));
 +      return 1;
  }
  
  /*
@@@ -239,13 -302,6 +239,6 @@@ int is_utf8(const char *text
        return 1;
  }
  
- static void strbuf_addchars(struct strbuf *sb, int c, size_t n)
- {
-       strbuf_grow(sb, n);
-       memset(sb->buf + sb->len, c, n);
-       strbuf_setlen(sb, sb->len + n);
- }
  static void strbuf_add_indented_text(struct strbuf *buf, const char *text,
                                     int indent, int indent2)
  {
@@@ -382,9 -438,6 +375,9 @@@ void strbuf_utf8_replace(struct strbuf 
                        dst += n;
                }
  
 +              if (src >= end)
 +                      break;
 +
                old = src;
                n = utf8_width((const char**)&src, NULL);
                if (!src)       /* broken utf-8, do nothing */
@@@ -469,7 -522,7 +462,7 @@@ char *reencode_string_iconv(const char 
        while (1) {
                size_t cnt = iconv(conv, &cp, &insz, &outpos, &outsz);
  
 -              if (cnt == -1) {
 +              if (cnt == (size_t) -1) {
                        size_t sofar;
                        if (errno != E2BIG) {
                                free(out);