| #include "cache.h" | 
 | #include "refs.h" | 
 |  | 
 | 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 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 | 
 |  * initialized strbuf. | 
 |  */ | 
 | char strbuf_slopbuf[1]; | 
 |  | 
 | void strbuf_init(struct strbuf *sb, size_t hint) | 
 | { | 
 | 	sb->alloc = sb->len = 0; | 
 | 	sb->buf = strbuf_slopbuf; | 
 | 	if (hint) | 
 | 		strbuf_grow(sb, hint); | 
 | } | 
 |  | 
 | void strbuf_release(struct strbuf *sb) | 
 | { | 
 | 	if (sb->alloc) { | 
 | 		free(sb->buf); | 
 | 		strbuf_init(sb, 0); | 
 | 	} | 
 | } | 
 |  | 
 | char *strbuf_detach(struct strbuf *sb, size_t *sz) | 
 | { | 
 | 	char *res; | 
 | 	strbuf_grow(sb, 0); | 
 | 	res = sb->buf; | 
 | 	if (sz) | 
 | 		*sz = sb->len; | 
 | 	strbuf_init(sb, 0); | 
 | 	return res; | 
 | } | 
 |  | 
 | void strbuf_attach(struct strbuf *sb, void *buf, size_t len, size_t alloc) | 
 | { | 
 | 	strbuf_release(sb); | 
 | 	sb->buf   = buf; | 
 | 	sb->len   = len; | 
 | 	sb->alloc = alloc; | 
 | 	strbuf_grow(sb, 0); | 
 | 	sb->buf[sb->len] = '\0'; | 
 | } | 
 |  | 
 | void strbuf_grow(struct strbuf *sb, size_t extra) | 
 | { | 
 | 	int new_buf = !sb->alloc; | 
 | 	if (unsigned_add_overflows(extra, 1) || | 
 | 	    unsigned_add_overflows(sb->len, extra + 1)) | 
 | 		die("you want to use way too much memory"); | 
 | 	if (new_buf) | 
 | 		sb->buf = NULL; | 
 | 	ALLOC_GROW(sb->buf, sb->len + extra + 1, sb->alloc); | 
 | 	if (new_buf) | 
 | 		sb->buf[0] = '\0'; | 
 | } | 
 |  | 
 | 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'; | 
 | } | 
 | void strbuf_rtrim(struct strbuf *sb) | 
 | { | 
 | 	while (sb->len > 0 && isspace((unsigned char)sb->buf[sb->len - 1])) | 
 | 		sb->len--; | 
 | 	sb->buf[sb->len] = '\0'; | 
 | } | 
 |  | 
 | void strbuf_ltrim(struct strbuf *sb) | 
 | { | 
 | 	char *b = sb->buf; | 
 | 	while (sb->len > 0 && isspace(*b)) { | 
 | 		b++; | 
 | 		sb->len--; | 
 | 	} | 
 | 	memmove(sb->buf, b, sb->len); | 
 | 	sb->buf[sb->len] = '\0'; | 
 | } | 
 |  | 
 | struct strbuf **strbuf_split_buf(const char *str, size_t slen, | 
 | 				 int terminator, int max) | 
 | { | 
 | 	struct strbuf **ret = NULL; | 
 | 	size_t nr = 0, alloc = 0; | 
 | 	struct strbuf *t; | 
 |  | 
 | 	while (slen) { | 
 | 		int len = slen; | 
 | 		if (max <= 0 || nr + 1 < max) { | 
 | 			const char *end = memchr(str, terminator, slen); | 
 | 			if (end) | 
 | 				len = end - str + 1; | 
 | 		} | 
 | 		t = xmalloc(sizeof(struct strbuf)); | 
 | 		strbuf_init(t, len); | 
 | 		strbuf_add(t, str, len); | 
 | 		ALLOC_GROW(ret, nr + 2, alloc); | 
 | 		ret[nr++] = t; | 
 | 		str += len; | 
 | 		slen -= len; | 
 | 	} | 
 | 	ALLOC_GROW(ret, nr + 1, alloc); /* In case string was empty */ | 
 | 	ret[nr] = NULL; | 
 | 	return ret; | 
 | } | 
 |  | 
 | void strbuf_list_free(struct strbuf **sbs) | 
 | { | 
 | 	struct strbuf **s = sbs; | 
 |  | 
 | 	while (*s) { | 
 | 		strbuf_release(*s); | 
 | 		free(*s++); | 
 | 	} | 
 | 	free(sbs); | 
 | } | 
 |  | 
 | int strbuf_cmp(const struct strbuf *a, const struct strbuf *b) | 
 | { | 
 | 	int len = a->len < b->len ? a->len: b->len; | 
 | 	int cmp = memcmp(a->buf, b->buf, len); | 
 | 	if (cmp) | 
 | 		return cmp; | 
 | 	return a->len < b->len ? -1: a->len != b->len; | 
 | } | 
 |  | 
 | void strbuf_splice(struct strbuf *sb, size_t pos, size_t len, | 
 | 				   const void *data, size_t dlen) | 
 | { | 
 | 	if (unsigned_add_overflows(pos, len)) | 
 | 		die("you want to use way too much memory"); | 
 | 	if (pos > sb->len) | 
 | 		die("`pos' is too far after the end of the buffer"); | 
 | 	if (pos + len > sb->len) | 
 | 		die("`pos + len' is too far after the end of the buffer"); | 
 |  | 
 | 	if (dlen >= len) | 
 | 		strbuf_grow(sb, dlen - len); | 
 | 	memmove(sb->buf + pos + dlen, | 
 | 			sb->buf + pos + len, | 
 | 			sb->len - pos - len); | 
 | 	memcpy(sb->buf + pos, data, dlen); | 
 | 	strbuf_setlen(sb, sb->len + dlen - len); | 
 | } | 
 |  | 
 | void strbuf_insert(struct strbuf *sb, size_t pos, const void *data, size_t len) | 
 | { | 
 | 	strbuf_splice(sb, pos, 0, data, len); | 
 | } | 
 |  | 
 | void strbuf_remove(struct strbuf *sb, size_t pos, size_t len) | 
 | { | 
 | 	strbuf_splice(sb, pos, len, NULL, 0); | 
 | } | 
 |  | 
 | void strbuf_add(struct strbuf *sb, const void *data, size_t len) | 
 | { | 
 | 	strbuf_grow(sb, len); | 
 | 	memcpy(sb->buf + sb->len, data, len); | 
 | 	strbuf_setlen(sb, sb->len + len); | 
 | } | 
 |  | 
 | void strbuf_adddup(struct strbuf *sb, size_t pos, size_t len) | 
 | { | 
 | 	strbuf_grow(sb, len); | 
 | 	memcpy(sb->buf + sb->len, sb->buf + pos, len); | 
 | 	strbuf_setlen(sb, sb->len + len); | 
 | } | 
 |  | 
 | void strbuf_addf(struct strbuf *sb, const char *fmt, ...) | 
 | { | 
 | 	va_list ap; | 
 | 	va_start(ap, fmt); | 
 | 	strbuf_vaddf(sb, fmt, ap); | 
 | 	va_end(ap); | 
 | } | 
 |  | 
 | static void add_lines(struct strbuf *out, | 
 | 			const char *prefix1, | 
 | 			const char *prefix2, | 
 | 			const char *buf, size_t size) | 
 | { | 
 | 	while (size) { | 
 | 		const char *prefix; | 
 | 		const char *next = memchr(buf, '\n', size); | 
 | 		next = next ? (next + 1) : (buf + size); | 
 |  | 
 | 		prefix = (prefix2 && buf[0] == '\n') ? prefix2 : prefix1; | 
 | 		strbuf_addstr(out, prefix); | 
 | 		strbuf_add(out, buf, next - buf); | 
 | 		size -= next - buf; | 
 | 		buf = next; | 
 | 	} | 
 | 	strbuf_complete_line(out); | 
 | } | 
 |  | 
 | void strbuf_add_commented_lines(struct strbuf *out, const char *buf, size_t size) | 
 | { | 
 | 	static char prefix1[3]; | 
 | 	static char prefix2[2]; | 
 |  | 
 | 	if (prefix1[0] != comment_line_char) { | 
 | 		sprintf(prefix1, "%c ", comment_line_char); | 
 | 		sprintf(prefix2, "%c", comment_line_char); | 
 | 	} | 
 | 	add_lines(out, prefix1, prefix2, buf, size); | 
 | } | 
 |  | 
 | void strbuf_commented_addf(struct strbuf *sb, const char *fmt, ...) | 
 | { | 
 | 	va_list params; | 
 | 	struct strbuf buf = STRBUF_INIT; | 
 | 	int incomplete_line = sb->len && sb->buf[sb->len - 1] != '\n'; | 
 |  | 
 | 	va_start(params, fmt); | 
 | 	strbuf_vaddf(&buf, fmt, params); | 
 | 	va_end(params); | 
 |  | 
 | 	strbuf_add_commented_lines(sb, buf.buf, buf.len); | 
 | 	if (incomplete_line) | 
 | 		sb->buf[--sb->len] = '\0'; | 
 |  | 
 | 	strbuf_release(&buf); | 
 | } | 
 |  | 
 | void strbuf_vaddf(struct strbuf *sb, const char *fmt, va_list ap) | 
 | { | 
 | 	int len; | 
 | 	va_list cp; | 
 |  | 
 | 	if (!strbuf_avail(sb)) | 
 | 		strbuf_grow(sb, 64); | 
 | 	va_copy(cp, ap); | 
 | 	len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, cp); | 
 | 	va_end(cp); | 
 | 	if (len < 0) | 
 | 		die("BUG: your vsnprintf is broken (returned %d)", len); | 
 | 	if (len > strbuf_avail(sb)) { | 
 | 		strbuf_grow(sb, len); | 
 | 		len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap); | 
 | 		if (len > strbuf_avail(sb)) | 
 | 			die("BUG: your vsnprintf is broken (insatiable)"); | 
 | 	} | 
 | 	strbuf_setlen(sb, sb->len + len); | 
 | } | 
 |  | 
 | void strbuf_expand(struct strbuf *sb, const char *format, expand_fn_t fn, | 
 | 		   void *context) | 
 | { | 
 | 	for (;;) { | 
 | 		const char *percent; | 
 | 		size_t consumed; | 
 |  | 
 | 		percent = strchrnul(format, '%'); | 
 | 		strbuf_add(sb, format, percent - format); | 
 | 		if (!*percent) | 
 | 			break; | 
 | 		format = percent + 1; | 
 |  | 
 | 		if (*format == '%') { | 
 | 			strbuf_addch(sb, '%'); | 
 | 			format++; | 
 | 			continue; | 
 | 		} | 
 |  | 
 | 		consumed = fn(sb, format, context); | 
 | 		if (consumed) | 
 | 			format += consumed; | 
 | 		else | 
 | 			strbuf_addch(sb, '%'); | 
 | 	} | 
 | } | 
 |  | 
 | size_t strbuf_expand_dict_cb(struct strbuf *sb, const char *placeholder, | 
 | 		void *context) | 
 | { | 
 | 	struct strbuf_expand_dict_entry *e = context; | 
 | 	size_t len; | 
 |  | 
 | 	for (; e->placeholder && (len = strlen(e->placeholder)); e++) { | 
 | 		if (!strncmp(placeholder, e->placeholder, len)) { | 
 | 			if (e->value) | 
 | 				strbuf_addstr(sb, e->value); | 
 | 			return len; | 
 | 		} | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | void strbuf_addbuf_percentquote(struct strbuf *dst, const struct strbuf *src) | 
 | { | 
 | 	int i, len = src->len; | 
 |  | 
 | 	for (i = 0; i < len; i++) { | 
 | 		if (src->buf[i] == '%') | 
 | 			strbuf_addch(dst, '%'); | 
 | 		strbuf_addch(dst, src->buf[i]); | 
 | 	} | 
 | } | 
 |  | 
 | size_t strbuf_fread(struct strbuf *sb, size_t size, FILE *f) | 
 | { | 
 | 	size_t res; | 
 | 	size_t oldalloc = sb->alloc; | 
 |  | 
 | 	strbuf_grow(sb, size); | 
 | 	res = fread(sb->buf + sb->len, 1, size, f); | 
 | 	if (res > 0) | 
 | 		strbuf_setlen(sb, sb->len + res); | 
 | 	else if (oldalloc == 0) | 
 | 		strbuf_release(sb); | 
 | 	return res; | 
 | } | 
 |  | 
 | ssize_t strbuf_read(struct strbuf *sb, int fd, size_t hint) | 
 | { | 
 | 	size_t oldlen = sb->len; | 
 | 	size_t oldalloc = sb->alloc; | 
 |  | 
 | 	strbuf_grow(sb, hint ? hint : 8192); | 
 | 	for (;;) { | 
 | 		ssize_t cnt; | 
 |  | 
 | 		cnt = xread(fd, sb->buf + sb->len, sb->alloc - sb->len - 1); | 
 | 		if (cnt < 0) { | 
 | 			if (oldalloc == 0) | 
 | 				strbuf_release(sb); | 
 | 			else | 
 | 				strbuf_setlen(sb, oldlen); | 
 | 			return -1; | 
 | 		} | 
 | 		if (!cnt) | 
 | 			break; | 
 | 		sb->len += cnt; | 
 | 		strbuf_grow(sb, 8192); | 
 | 	} | 
 |  | 
 | 	sb->buf[sb->len] = '\0'; | 
 | 	return sb->len - oldlen; | 
 | } | 
 |  | 
 | #define STRBUF_MAXLINK (2*PATH_MAX) | 
 |  | 
 | int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint) | 
 | { | 
 | 	size_t oldalloc = sb->alloc; | 
 |  | 
 | 	if (hint < 32) | 
 | 		hint = 32; | 
 |  | 
 | 	while (hint < STRBUF_MAXLINK) { | 
 | 		int len; | 
 |  | 
 | 		strbuf_grow(sb, hint); | 
 | 		len = readlink(path, sb->buf, hint); | 
 | 		if (len < 0) { | 
 | 			if (errno != ERANGE) | 
 | 				break; | 
 | 		} else if (len < hint) { | 
 | 			strbuf_setlen(sb, len); | 
 | 			return 0; | 
 | 		} | 
 |  | 
 | 		/* .. the buffer was too small - try again */ | 
 | 		hint *= 2; | 
 | 	} | 
 | 	if (oldalloc == 0) | 
 | 		strbuf_release(sb); | 
 | 	return -1; | 
 | } | 
 |  | 
 | int strbuf_getwholeline(struct strbuf *sb, FILE *fp, int term) | 
 | { | 
 | 	int ch; | 
 |  | 
 | 	if (feof(fp)) | 
 | 		return EOF; | 
 |  | 
 | 	strbuf_reset(sb); | 
 | 	while ((ch = fgetc(fp)) != EOF) { | 
 | 		strbuf_grow(sb, 1); | 
 | 		sb->buf[sb->len++] = ch; | 
 | 		if (ch == term) | 
 | 			break; | 
 | 	} | 
 | 	if (ch == EOF && sb->len == 0) | 
 | 		return EOF; | 
 |  | 
 | 	sb->buf[sb->len] = '\0'; | 
 | 	return 0; | 
 | } | 
 |  | 
 | int strbuf_getline(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); | 
 | 	return 0; | 
 | } | 
 |  | 
 | int strbuf_getwholeline_fd(struct strbuf *sb, int fd, int term) | 
 | { | 
 | 	strbuf_reset(sb); | 
 |  | 
 | 	while (1) { | 
 | 		char ch; | 
 | 		ssize_t len = xread(fd, &ch, 1); | 
 | 		if (len <= 0) | 
 | 			return EOF; | 
 | 		strbuf_addch(sb, ch); | 
 | 		if (ch == term) | 
 | 			break; | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint) | 
 | { | 
 | 	int fd, len; | 
 |  | 
 | 	fd = open(path, O_RDONLY); | 
 | 	if (fd < 0) | 
 | 		return -1; | 
 | 	len = strbuf_read(sb, fd, hint); | 
 | 	close(fd); | 
 | 	if (len < 0) | 
 | 		return -1; | 
 |  | 
 | 	return len; | 
 | } | 
 |  | 
 | void strbuf_add_lines(struct strbuf *out, const char *prefix, | 
 | 		      const char *buf, size_t size) | 
 | { | 
 | 	add_lines(out, prefix, NULL, buf, size); | 
 | } | 
 |  | 
 | void strbuf_addstr_xml_quoted(struct strbuf *buf, const char *s) | 
 | { | 
 | 	while (*s) { | 
 | 		size_t len = strcspn(s, "\"<>&"); | 
 | 		strbuf_add(buf, s, len); | 
 | 		s += len; | 
 | 		switch (*s) { | 
 | 		case '"': | 
 | 			strbuf_addstr(buf, """); | 
 | 			break; | 
 | 		case '<': | 
 | 			strbuf_addstr(buf, "<"); | 
 | 			break; | 
 | 		case '>': | 
 | 			strbuf_addstr(buf, ">"); | 
 | 			break; | 
 | 		case '&': | 
 | 			strbuf_addstr(buf, "&"); | 
 | 			break; | 
 | 		case 0: | 
 | 			return; | 
 | 		} | 
 | 		s++; | 
 | 	} | 
 | } | 
 |  | 
 | static int is_rfc3986_reserved(char ch) | 
 | { | 
 | 	switch (ch) { | 
 | 		case '!': case '*': case '\'': case '(': case ')': case ';': | 
 | 		case ':': case '@': case '&': case '=': case '+': case '$': | 
 | 		case ',': case '/': case '?': case '#': case '[': case ']': | 
 | 			return 1; | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int is_rfc3986_unreserved(char ch) | 
 | { | 
 | 	return isalnum(ch) || | 
 | 		ch == '-' || ch == '_' || ch == '.' || ch == '~'; | 
 | } | 
 |  | 
 | static void strbuf_add_urlencode(struct strbuf *sb, const char *s, size_t len, | 
 | 				 int reserved) | 
 | { | 
 | 	strbuf_grow(sb, len); | 
 | 	while (len--) { | 
 | 		char ch = *s++; | 
 | 		if (is_rfc3986_unreserved(ch) || | 
 | 		    (!reserved && is_rfc3986_reserved(ch))) | 
 | 			strbuf_addch(sb, ch); | 
 | 		else | 
 | 			strbuf_addf(sb, "%%%02x", ch); | 
 | 	} | 
 | } | 
 |  | 
 | void strbuf_addstr_urlencode(struct strbuf *sb, const char *s, | 
 | 			     int reserved) | 
 | { | 
 | 	strbuf_add_urlencode(sb, s, strlen(s), reserved); | 
 | } | 
 |  | 
 | void strbuf_humanise_bytes(struct strbuf *buf, off_t bytes) | 
 | { | 
 | 	if (bytes > 1 << 30) { | 
 | 		strbuf_addf(buf, "%u.%2.2u GiB", | 
 | 			    (int)(bytes >> 30), | 
 | 			    (int)(bytes & ((1 << 30) - 1)) / 10737419); | 
 | 	} else if (bytes > 1 << 20) { | 
 | 		int x = bytes + 5243;  /* for rounding */ | 
 | 		strbuf_addf(buf, "%u.%2.2u MiB", | 
 | 			    x >> 20, ((x & ((1 << 20) - 1)) * 100) >> 20); | 
 | 	} else if (bytes > 1 << 10) { | 
 | 		int x = bytes + 5;  /* for rounding */ | 
 | 		strbuf_addf(buf, "%u.%2.2u KiB", | 
 | 			    x >> 10, ((x & ((1 << 10) - 1)) * 100) >> 10); | 
 | 	} else { | 
 | 		strbuf_addf(buf, "%u bytes", (int)bytes); | 
 | 	} | 
 | } | 
 |  | 
 | int printf_ln(const char *fmt, ...) | 
 | { | 
 | 	int ret; | 
 | 	va_list ap; | 
 | 	va_start(ap, fmt); | 
 | 	ret = vprintf(fmt, ap); | 
 | 	va_end(ap); | 
 | 	if (ret < 0 || putchar('\n') == EOF) | 
 | 		return -1; | 
 | 	return ret + 1; | 
 | } | 
 |  | 
 | int fprintf_ln(FILE *fp, const char *fmt, ...) | 
 | { | 
 | 	int ret; | 
 | 	va_list ap; | 
 | 	va_start(ap, fmt); | 
 | 	ret = vfprintf(fp, fmt, ap); | 
 | 	va_end(ap); | 
 | 	if (ret < 0 || putc('\n', fp) == EOF) | 
 | 		return -1; | 
 | 	return ret + 1; | 
 | } |