diff options
Diffstat (limited to 'strbuf.c')
-rw-r--r-- | strbuf.c | 406 |
1 files changed, 354 insertions, 52 deletions
@@ -1,5 +1,6 @@ #include "cache.h" #include "refs.h" +#include "utf8.h" int starts_with(const char *str, const char *prefix) { @@ -10,33 +11,6 @@ int starts_with(const char *str, const char *prefix) return 0; } -int prefixcmp(const char *str, const char *prefix) -{ - for (; ; str++, prefix++) - if (!*prefix) - return 0; - else if (*str != *prefix) - return (unsigned char)*prefix - (unsigned char)*str; -} - -int ends_with(const char *str, const char *suffix) -{ - int len = strlen(str), suflen = strlen(suffix); - if (len < suflen) - return 0; - else - return !strcmp(str + len - suflen, suffix); -} - -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); -} - /* * Used as the default ->buf value, so that people can always assume * buf is non NULL and ->buf is NUL terminated even for a freshly @@ -96,15 +70,8 @@ void strbuf_grow(struct strbuf *sb, size_t extra) 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) { @@ -124,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) { @@ -197,7 +187,7 @@ void strbuf_insert(struct strbuf *sb, size_t pos, const void *data, size_t len) void strbuf_remove(struct strbuf *sb, size_t pos, size_t len) { - strbuf_splice(sb, pos, len, NULL, 0); + strbuf_splice(sb, pos, len, "", 0); } void strbuf_add(struct strbuf *sb, const void *data, size_t len) @@ -207,6 +197,13 @@ void strbuf_add(struct strbuf *sb, const void *data, size_t len) strbuf_setlen(sb, sb->len + len); } +void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2) +{ + strbuf_grow(sb, sb2->len); + memcpy(sb->buf + sb->len, sb2->buf, sb2->len); + strbuf_setlen(sb, sb->len + sb2->len); +} + void strbuf_adddup(struct strbuf *sb, size_t pos, size_t len) { strbuf_grow(sb, len); @@ -214,6 +211,13 @@ void strbuf_adddup(struct strbuf *sb, size_t pos, size_t len) 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; @@ -232,7 +236,8 @@ static void add_lines(struct strbuf *out, const char *next = memchr(buf, '\n', size); next = next ? (next + 1) : (buf + size); - prefix = (prefix2 && buf[0] == '\n') ? prefix2 : prefix1; + prefix = ((prefix2 && (buf[0] == '\n' || buf[0] == '\t')) + ? prefix2 : prefix1); strbuf_addstr(out, prefix); strbuf_add(out, buf, next - buf); size -= next - buf; @@ -247,8 +252,8 @@ void strbuf_add_commented_lines(struct strbuf *out, const char *buf, size_t size static char prefix2[2]; if (prefix1[0] != comment_line_char) { - sprintf(prefix1, "%c ", comment_line_char); - sprintf(prefix2, "%c", comment_line_char); + xsnprintf(prefix1, sizeof(prefix1), "%c ", comment_line_char); + xsnprintf(prefix2, sizeof(prefix2), "%c", comment_line_char); } add_lines(out, prefix1, prefix2, buf, size); } @@ -366,19 +371,19 @@ ssize_t strbuf_read(struct strbuf *sb, int fd, size_t hint) strbuf_grow(sb, hint ? hint : 8192); for (;;) { - ssize_t cnt; + ssize_t want = sb->alloc - sb->len - 1; + ssize_t got = read_in_full(fd, sb->buf + sb->len, want); - cnt = xread(fd, sb->buf + sb->len, sb->alloc - sb->len - 1); - if (cnt < 0) { + if (got < 0) { if (oldalloc == 0) strbuf_release(sb); else strbuf_setlen(sb, oldlen); return -1; } - if (!cnt) + sb->len += got; + if (got < want) break; - sb->len += cnt; strbuf_grow(sb, 8192); } @@ -386,6 +391,23 @@ ssize_t strbuf_read(struct strbuf *sb, int fd, size_t hint) return sb->len - oldlen; } +ssize_t strbuf_read_once(struct strbuf *sb, int fd, size_t hint) +{ + ssize_t cnt; + + strbuf_grow(sb, hint ? hint : 8192); + cnt = xread(fd, sb->buf + sb->len, sb->alloc - sb->len - 1); + if (cnt > 0) + strbuf_setlen(sb, sb->len + cnt); + return cnt; +} + +ssize_t strbuf_write(struct strbuf *sb, FILE *f) +{ + return sb->len ? fwrite(sb->buf, 1, sb->len, f) : 0; +} + + #define STRBUF_MAXLINK (2*PATH_MAX) int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint) @@ -416,6 +438,74 @@ int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint) 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; +} + +#ifdef HAVE_GETDELIM +int strbuf_getwholeline(struct strbuf *sb, FILE *fp, int term) +{ + ssize_t r; + + if (feof(fp)) + return EOF; + + strbuf_reset(sb); + + /* Translate slopbuf to NULL, as we cannot call realloc on it */ + if (!sb->alloc) + sb->buf = NULL; + r = getdelim(&sb->buf, &sb->alloc, term, fp); + + if (r > 0) { + sb->len = r; + return 0; + } + assert(r == -1); + + /* + * Normally we would have called xrealloc, which will try to free + * memory and recover. But we have no way to tell getdelim() to do so. + * Worse, we cannot try to recover ENOMEM ourselves, because we have + * no idea how many bytes were read by getdelim. + * + * Dying here is reasonable. It mirrors what xrealloc would do on + * catastrophic memory failure. We skip the opportunity to free pack + * memory and retry, but that's unlikely to help for a malloc small + * enough to hold a single line of input, anyway. + */ + if (errno == ENOMEM) + die("Out of memory, getdelim failed"); + + /* + * Restore strbuf invariants; if getdelim left us with a NULL pointer, + * we can just re-init, but otherwise we should make sure that our + * length is empty, and that the result is NUL-terminated. + */ + if (!sb->buf) + strbuf_init(sb, 0); + else + strbuf_reset(sb); + return EOF; +} +#else int strbuf_getwholeline(struct strbuf *sb, FILE *fp, int term) { int ch; @@ -424,28 +514,54 @@ int strbuf_getwholeline(struct strbuf *sb, FILE *fp, int term) return EOF; strbuf_reset(sb); - while ((ch = fgetc(fp)) != EOF) { - strbuf_grow(sb, 1); + flockfile(fp); + while ((ch = getc_unlocked(fp)) != EOF) { + if (!strbuf_avail(sb)) + strbuf_grow(sb, 1); sb->buf[sb->len++] = ch; if (ch == term) break; } + funlockfile(fp); if (ch == EOF && sb->len == 0) return EOF; sb->buf[sb->len] = '\0'; return 0; } +#endif -int strbuf_getline(struct strbuf *sb, FILE *fp, int term) +static int strbuf_getdelim(struct strbuf *sb, FILE *fp, int term) { if (strbuf_getwholeline(sb, fp, term)) return EOF; - if (sb->buf[sb->len-1] == term) - strbuf_setlen(sb, sb->len-1); + if (sb->buf[sb->len - 1] == term) + strbuf_setlen(sb, sb->len - 1); + return 0; +} + +int strbuf_getline(struct strbuf *sb, FILE *fp) +{ + if (strbuf_getwholeline(sb, fp, '\n')) + return EOF; + if (sb->buf[sb->len - 1] == '\n') { + strbuf_setlen(sb, sb->len - 1); + if (sb->len && sb->buf[sb->len - 1] == '\r') + strbuf_setlen(sb, sb->len - 1); + } return 0; } +int strbuf_getline_lf(struct strbuf *sb, FILE *fp) +{ + return strbuf_getdelim(sb, fp, '\n'); +} + +int strbuf_getline_nul(struct strbuf *sb, FILE *fp) +{ + return strbuf_getdelim(sb, fp, '\0'); +} + int strbuf_getwholeline_fd(struct strbuf *sb, int fd, int term) { strbuf_reset(sb); @@ -462,9 +578,10 @@ int strbuf_getwholeline_fd(struct strbuf *sb, int fd, int term) return 0; } -int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint) +ssize_t strbuf_read_file(struct strbuf *sb, const char *path, size_t hint) { - int fd, len; + int fd; + ssize_t len; fd = open(path, O_RDONLY); if (fd < 0) @@ -565,6 +682,31 @@ void strbuf_humanise_bytes(struct strbuf *buf, off_t bytes) } } +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; @@ -588,3 +730,163 @@ int fprintf_ln(FILE *fp, const char *fmt, ...) return -1; return ret + 1; } + +char *xstrdup_tolower(const char *string) +{ + char *result; + size_t len, i; + + len = strlen(string); + result = xmallocz(len); + 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; +} + +void strbuf_addftime(struct strbuf *sb, const char *fmt, const struct tm *tm) +{ + size_t hint = 128; + size_t len; + + if (!*fmt) + return; + + strbuf_grow(sb, hint); + len = strftime(sb->buf + sb->len, sb->alloc - sb->len, fmt, tm); + + if (!len) { + /* + * strftime reports "0" if it could not fit the result in the buffer. + * Unfortunately, it also reports "0" if the requested time string + * takes 0 bytes. So our strategy is to munge the format so that the + * output contains at least one character, and then drop the extra + * character before returning. + */ + struct strbuf munged_fmt = STRBUF_INIT; + strbuf_addf(&munged_fmt, "%s ", fmt); + while (!len) { + hint *= 2; + strbuf_grow(sb, hint); + len = strftime(sb->buf + sb->len, sb->alloc - sb->len, + munged_fmt.buf, tm); + } + strbuf_release(&munged_fmt); + len--; /* drop munged space */ + } + strbuf_setlen(sb, sb->len + len); +} + +void strbuf_add_unique_abbrev(struct strbuf *sb, const unsigned char *sha1, + int abbrev_len) +{ + int r; + strbuf_grow(sb, GIT_SHA1_HEXSZ + 1); + r = find_unique_abbrev_r(sb->buf + sb->len, sha1, abbrev_len); + strbuf_setlen(sb, sb->len + r); +} + +/* + * Returns the length of a line, without trailing spaces. + * + * If the line ends with newline, it will be removed too. + */ +static size_t cleanup(char *line, size_t len) +{ + while (len) { + unsigned char c = line[len - 1]; + if (!isspace(c)) + break; + len--; + } + + return len; +} + +/* + * Remove empty lines from the beginning and end + * and also trailing spaces from every line. + * + * Turn multiple consecutive empty lines between paragraphs + * into just one empty line. + * + * If the input has only empty lines and spaces, + * no output will be produced. + * + * If last line does not have a newline at the end, one is added. + * + * Enable skip_comments to skip every line starting with comment + * character. + */ +void strbuf_stripspace(struct strbuf *sb, int skip_comments) +{ + int empties = 0; + size_t i, j, len, newlen; + char *eol; + + /* We may have to add a newline. */ + strbuf_grow(sb, 1); + + for (i = j = 0; i < sb->len; i += len, j += newlen) { + eol = memchr(sb->buf + i, '\n', sb->len - i); + len = eol ? eol - (sb->buf + i) + 1 : sb->len - i; + + if (skip_comments && len && sb->buf[i] == comment_line_char) { + newlen = 0; + continue; + } + newlen = cleanup(sb->buf + i, len); + + /* Not just an empty line? */ + if (newlen) { + if (empties > 0 && j > 0) + sb->buf[j++] = '\n'; + empties = 0; + memmove(sb->buf + j, sb->buf + i, newlen); + sb->buf[newlen + j++] = '\n'; + } else { + empties++; + } + } + + strbuf_setlen(sb, j); +} + +int strbuf_normalize_path(struct strbuf *src) +{ + struct strbuf dst = STRBUF_INIT; + + strbuf_grow(&dst, src->len); + if (normalize_path_copy(dst.buf, src->buf) < 0) { + strbuf_release(&dst); + return -1; + } + + /* + * normalize_path does not tell us the new length, so we have to + * compute it by looking for the new NUL it placed + */ + strbuf_setlen(&dst, strlen(dst.buf)); + strbuf_swap(src, &dst); + strbuf_release(&dst); + return 0; +} |