void strbuf_utf8_replace(struct strbuf *sb_src, int pos, int width, const char *subst) { struct strbuf sb_dst = STRBUF_INIT; char *src = sb_src->buf; char *end = src + sb_src->len; char *dst; int w = 0, subst_len = 0; if (subst) subst_len = strlen(subst); strbuf_grow(&sb_dst, sb_src->len + subst_len); dst = sb_dst.buf; while (src < end) { char *old; size_t n; while ((n = display_mode_esc_sequence_len(src))) { memcpy(dst, src, n); src += n; dst += n; } if (src >= end) break; old = src; n = utf8_width((const char**)&src, NULL); if (!src) /* broken utf-8, do nothing */ return; if (n && w >= pos && w < pos + width) { if (subst) { memcpy(dst, subst, subst_len); dst += subst_len; subst = NULL; } w += n; continue; } memcpy(dst, old, src - old); dst += src - old; w += n; } strbuf_setlen(&sb_dst, dst - sb_dst.buf); strbuf_swap(sb_src, &sb_dst); strbuf_release(&sb_dst); }
/* * Returns the total number of columns required by a null-terminated * string, assuming that the string is utf8. Returns strlen() instead * if the string does not look like a valid utf8 string. */ int utf8_strnwidth(const char *string, int len, int skip_ansi) { int width = 0; const char *orig = string; if (len == -1) len = strlen(string); while (string && string < orig + len) { int skip; while (skip_ansi && (skip = display_mode_esc_sequence_len(string)) != 0) string += skip; width += utf8_width(&string, NULL); } return string ? width : len; }
static size_t format_and_pad_commit(struct strbuf *sb, /* in UTF-8 */ const char *placeholder, struct format_commit_context *c) { struct strbuf local_sb = STRBUF_INIT; int total_consumed = 0, len, padding = c->padding; if (padding < 0) { const char *start = strrchr(sb->buf, '\n'); int occupied; if (!start) start = sb->buf; occupied = utf8_strnwidth(start, -1, 1); padding = (-padding) - occupied; } while (1) { int modifier = *placeholder == 'C'; int consumed = format_commit_one(&local_sb, placeholder, c); total_consumed += consumed; if (!modifier) break; placeholder += consumed; if (*placeholder != '%') break; placeholder++; total_consumed++; } len = utf8_strnwidth(local_sb.buf, -1, 1); if (c->flush_type == flush_left_and_steal) { const char *ch = sb->buf + sb->len - 1; while (len > padding && ch > sb->buf) { const char *p; if (*ch == ' ') { ch--; padding++; continue; } /* check for trailing ansi sequences */ if (*ch != 'm') break; p = ch - 1; while (ch - p < 10 && *p != '\033') p--; if (*p != '\033' || ch + 1 - p != display_mode_esc_sequence_len(p)) break; /* * got a good ansi sequence, put it back to * local_sb as we're cutting sb */ strbuf_insert(&local_sb, 0, p, ch + 1 - p); ch = p - 1; } strbuf_setlen(sb, ch + 1 - sb->buf); c->flush_type = flush_left; } if (len > padding) { switch (c->truncate) { case trunc_left: strbuf_utf8_replace(&local_sb, 0, len - (padding - 2), ".."); break; case trunc_middle: strbuf_utf8_replace(&local_sb, padding / 2 - 1, len - (padding - 2), ".."); break; case trunc_right: strbuf_utf8_replace(&local_sb, padding - 2, len - (padding - 2), ".."); break; case trunc_none: break; } strbuf_addbuf(sb, &local_sb); } else { int sb_len = sb->len, offset = 0; if (c->flush_type == flush_left) offset = padding - len; else if (c->flush_type == flush_both) offset = (padding - len) / 2; /* * we calculate padding in columns, now * 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); memcpy(sb->buf + sb_len + offset, local_sb.buf, local_sb.len); } strbuf_release(&local_sb); c->flush_type = no_flush; return total_consumed; }
/* * Wrap the text, if necessary. The variable indent is the indent for the * first line, indent2 is the indent for all other lines. * If indent is negative, assume that already -indent columns have been * consumed (and no extra indent is necessary for the first line). */ int strbuf_add_wrapped_text(struct strbuf *buf, const char *text, int indent1, int indent2, int width) { int indent, w, assume_utf8 = 1; const char *bol, *space, *start = text; size_t orig_len = buf->len; if (width <= 0) { strbuf_add_indented_text(buf, text, indent1, indent2); return 1; } retry: bol = text; w = indent = indent1; space = NULL; if (indent < 0) { w = -indent; space = text; } for (;;) { char c; size_t skip; while ((skip = display_mode_esc_sequence_len(text))) text += skip; c = *text; if (!c || isspace(c)) { if (w < width || !space) { const char *start = bol; if (!c && text == start) return w; if (space) start = space; else strbuf_addchars(buf, ' ', indent); strbuf_add(buf, start, text - start); if (!c) return w; space = text; if (c == '\t') w |= 0x07; else if (c == '\n') { space++; if (*space == '\n') { strbuf_addch(buf, '\n'); goto new_line; } else if (!isalnum(*space)) goto new_line; else strbuf_addch(buf, ' '); } w++; text++; } else { new_line: strbuf_addch(buf, '\n'); text = bol = space + isspace(*space); space = NULL; w = indent = indent2; } continue; } if (assume_utf8) { w += utf8_width(&text, NULL); if (!text) { assume_utf8 = 0; text = start; strbuf_setlen(buf, orig_len); goto retry; } } else { w++; text++; } } }
/* * Wrap the text, if necessary. The variable indent is the indent for the * first line, indent2 is the indent for all other lines. * If indent is negative, assume that already -indent columns have been * consumed (and no extra indent is necessary for the first line). */ int strbuf_add_wrapped_text(struct strbuf *buf, const char *text, int indent, int indent2, int width) { int w = indent, assume_utf8 = is_utf8(text); const char *bol = text, *space = NULL; if (width <= 0) { strbuf_add_indented_text(buf, text, indent, indent2); return 1; } if (indent < 0) { w = -indent; space = text; } for (;;) { char c; size_t skip; while ((skip = display_mode_esc_sequence_len(text))) text += skip; c = *text; if (!c || isspace(c)) { if (w < width || !space) { const char *start = bol; if (!c && text == start) return w; if (space) start = space; else print_spaces(buf, indent); strbuf_write(buf, start, text - start); if (!c) return w; space = text; if (c == '\t') w |= 0x07; else if (c == '\n') { space++; if (*space == '\n') { strbuf_write(buf, "\n", 1); goto new_line; } else if (!isalnum(*space)) goto new_line; else strbuf_write(buf, " ", 1); } w++; text++; } else { new_line: strbuf_write(buf, "\n", 1); text = bol = space + isspace(*space); space = NULL; w = indent = indent2; } continue; } if (assume_utf8) w += utf8_width(&text, NULL); else { w++; text++; } } }