summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrian Lopez <seniorlopez@gmail.com>2018-01-12 15:13:40 -0800
committerBrian Lopez <seniorlopez@gmail.com>2018-01-13 12:18:17 -0800
commitd01759f5d1430fe663bbcd333389cb2556c8d0a9 (patch)
treecabfac2b045ae191c538d73b2f6e7491dedf9f59
parent5734768b9edaecedaa7b13fed2ce59cb588df7e8 (diff)
downloadlibgit2-brianmario/trailer-info.tar.gz
Add message trailer iterator APIbrianmario/trailer-info
This also moves the existing callback-based API over to wrap the new iterator API.
-rw-r--r--include/git2/message.h13
-rw-r--r--src/trailer.c131
-rw-r--r--tests/message/trailer.c268
3 files changed, 374 insertions, 38 deletions
diff --git a/include/git2/message.h b/include/git2/message.h
index a74dcc2d9..d5229660d 100644
--- a/include/git2/message.h
+++ b/include/git2/message.h
@@ -58,6 +58,19 @@ typedef int(*git_message_trailer_cb)(const char *key, const char *value, void *p
*/
GIT_EXTERN(int) git_message_trailers(const char *message, git_message_trailer_cb cb, void *payload);
+typedef struct git_message_trailer_iterator git_message_trailer_iterator;
+
+GIT_EXTERN(int) git_message_trailer_iterator_new(
+ git_message_trailer_iterator **out,
+ const char *message);
+
+GIT_EXTERN(int) git_message_trailer_iterator_next(
+ const char **key_out,
+ const char **value_out,
+ git_message_trailer_iterator *iter);
+
+GIT_EXTERN(void) git_message_trailer_iterator_free(git_message_trailer_iterator *iter);
+
/** @} */
GIT_END_DECL
diff --git a/src/trailer.c b/src/trailer.c
index cfefc530c..fd8bd5c8b 100644
--- a/src/trailer.c
+++ b/src/trailer.c
@@ -248,7 +248,7 @@ static int find_trailer_end(const char *buf, size_t len)
return len - ignore_non_trailer(buf, len);
}
-static char *find_trailer(const char *message, size_t* len)
+static char *extract_trailer_block(const char *message, size_t* len)
{
size_t patch_start = find_patch_start(message);
size_t trailer_end = find_trailer_end(message, patch_start);
@@ -276,48 +276,84 @@ enum trailer_state {
S_IGNORE = 7,
};
-#define NEXT(st) { state = (st); ptr++; continue; }
+typedef struct {
+ char *trailer_block;
+ size_t block_len;
+ char *ptr;
+} trailer_iterator;
+
+int git_message_trailer_iterator_new(
+ git_message_trailer_iterator **out,
+ const char *message)
+{
+ trailer_iterator *iter;
+
+ iter = git__calloc(1, sizeof(trailer_iterator));
+ GITERR_CHECK_ALLOC(iter);
+
+ iter->trailer_block = extract_trailer_block(message, &iter->block_len);
+ iter->ptr = iter->trailer_block;
+
+ *out = (git_message_trailer_iterator *) iter;
+
+ return 0;
+}
+
+void git_message_trailer_iterator_free(git_message_trailer_iterator *_iter)
+{
+ trailer_iterator *iter = (trailer_iterator *) _iter;
+
+ if (iter == NULL)
+ return;
+
+ git__free(iter->trailer_block);
+ git__free(iter);
+}
+
+#define NEXT(st) { state = (st); iter->ptr++; continue; }
#define GOTO(st) { state = (st); continue; }
-int git_message_trailers(const char *message, git_message_trailer_cb cb, void *payload)
+int git_message_trailer_iterator_next(
+ const char **key_out,
+ const char **value_out,
+ git_message_trailer_iterator *_iter)
{
+ trailer_iterator *iter = (trailer_iterator *) _iter;
enum trailer_state state = S_START;
int rc = 0;
- char *ptr;
- char *key = NULL;
- char *value = NULL;
- size_t trailer_len;
- char *trailer = find_trailer(message, &trailer_len);
+ if (*iter->ptr == 0) {
+ return GIT_ITEROVER;
+ }
- for (ptr = trailer;;) {
+ while (iter->ptr != 0) {
switch (state) {
case S_START: {
- if (*ptr == 0) {
+ if (*iter->ptr == 0) {
goto ret;
}
- key = ptr;
+ *key_out = iter->ptr;
GOTO(S_KEY);
}
case S_KEY: {
- if (*ptr == 0) {
+ if (*iter->ptr == 0) {
goto ret;
}
- if (isalnum(*ptr) || *ptr == '-') {
+ if (isalnum(*iter->ptr) || *iter->ptr == '-') {
// legal key character
NEXT(S_KEY);
}
- if (*ptr == ' ' || *ptr == '\t') {
+ if (*iter->ptr == ' ' || *iter->ptr == '\t') {
// optional whitespace before separator
- *ptr = 0;
+ *iter->ptr = 0;
NEXT(S_KEY_WS);
}
- if (strchr(TRAILER_SEPARATORS, *ptr)) {
- *ptr = 0;
+ if (strchr(TRAILER_SEPARATORS, *iter->ptr)) {
+ *iter->ptr = 0;
NEXT(S_SEP_WS);
}
@@ -325,15 +361,15 @@ int git_message_trailers(const char *message, git_message_trailer_cb cb, void *p
GOTO(S_IGNORE);
}
case S_KEY_WS: {
- if (*ptr == 0) {
+ if (*iter->ptr == 0) {
goto ret;
}
- if (*ptr == ' ' || *ptr == '\t') {
+ if (*iter->ptr == ' ' || *iter->ptr == '\t') {
NEXT(S_KEY_WS);
}
- if (strchr(TRAILER_SEPARATORS, *ptr)) {
+ if (strchr(TRAILER_SEPARATORS, *iter->ptr)) {
NEXT(S_SEP_WS);
}
@@ -341,53 +377,46 @@ int git_message_trailers(const char *message, git_message_trailer_cb cb, void *p
GOTO(S_IGNORE);
}
case S_SEP_WS: {
- if (*ptr == 0) {
+ if (*iter->ptr == 0) {
goto ret;
}
- if (*ptr == ' ' || *ptr == '\t') {
+ if (*iter->ptr == ' ' || *iter->ptr == '\t') {
NEXT(S_SEP_WS);
}
- value = ptr;
+ *value_out = iter->ptr;
NEXT(S_VALUE);
}
case S_VALUE: {
- if (*ptr == 0) {
+ if (*iter->ptr == 0) {
GOTO(S_VALUE_END);
}
- if (*ptr == '\n') {
+ if (*iter->ptr == '\n') {
NEXT(S_VALUE_NL);
}
NEXT(S_VALUE);
}
case S_VALUE_NL: {
- if (*ptr == ' ') {
+ if (*iter->ptr == ' ') {
// continuation;
NEXT(S_VALUE);
}
- ptr[-1] = 0;
+ iter->ptr[-1] = 0;
GOTO(S_VALUE_END);
}
case S_VALUE_END: {
- if ((rc = cb(key, value, payload))) {
- goto ret;
- }
-
- key = NULL;
- value = NULL;
-
- GOTO(S_START);
+ goto ret;
}
case S_IGNORE: {
- if (*ptr == 0) {
+ if (*iter->ptr == 0) {
goto ret;
}
- if (*ptr == '\n') {
+ if (*iter->ptr == '\n') {
NEXT(S_START);
}
@@ -397,6 +426,32 @@ int git_message_trailers(const char *message, git_message_trailer_cb cb, void *p
}
ret:
- git__free(trailer);
return rc;
}
+
+int git_message_trailers(const char *message, git_message_trailer_cb cb, void *payload)
+{
+ git_message_trailer_iterator *iterator;
+ int rc;
+
+ if ((rc = git_message_trailer_iterator_new(&iterator, message))) {
+ return rc;
+ }
+
+ while (rc != GIT_ITEROVER) {
+ const char *key;
+ const char *value;
+
+ if ((rc = git_message_trailer_iterator_next(&key, &value, iterator))) {
+ goto ret;
+ }
+
+ if ((rc = cb(key, value, payload))) {
+ goto ret;
+ }
+ }
+
+ret:
+ git_message_trailer_iterator_free(iterator);
+ return 0;
+}
diff --git a/tests/message/trailer.c b/tests/message/trailer.c
index 83fc77808..85949404c 100644
--- a/tests/message/trailer.c
+++ b/tests/message/trailer.c
@@ -177,3 +177,271 @@ void test_message_trailer__invalid(void)
"Another: trailer\n"
, trailers);
}
+
+void test_message_trailer__iterator_simple(void)
+{
+ git_message_trailer_iterator *iterator;
+ const char *key;
+ const char *value;
+ int rc;
+
+ rc = git_message_trailer_iterator_new(
+ &iterator,
+ "Message\n"
+ "\n"
+ "Signed-off-by: foo@bar.com\n"
+ "Signed-off-by: someone@else.com\n");
+
+ cl_assert(rc == 0);
+
+ rc = git_message_trailer_iterator_next(&key, &value, iterator);
+
+ cl_assert(rc == 0);
+
+ cl_assert_equal_s("Signed-off-by", key);
+ cl_assert_equal_s("foo@bar.com", value);
+
+ rc = git_message_trailer_iterator_next(&key, &value, iterator);
+
+ cl_assert(rc == 0);
+
+ cl_assert_equal_s("Signed-off-by", key);
+ cl_assert_equal_s("someone@else.com", value);
+
+ rc = git_message_trailer_iterator_next(&key, &value, iterator);
+
+ cl_assert(rc == GIT_ITEROVER);
+
+ git_message_trailer_iterator_free(iterator);
+}
+
+void test_message_trailer__iterator_no_whitespace(void)
+{
+ git_message_trailer_iterator *iterator;
+ const char *key;
+ const char *value;
+ int rc;
+
+ rc = git_message_trailer_iterator_new(
+ &iterator,
+ "Message\n"
+ "\n"
+ "Key:value\n");
+
+ cl_assert(rc == 0);
+
+ rc = git_message_trailer_iterator_next(&key, &value, iterator);
+
+ cl_assert(rc == 0);
+
+ cl_assert_equal_s("Key", key);
+ cl_assert_equal_s("value", value);
+
+ rc = git_message_trailer_iterator_next(&key, &value, iterator);
+
+ cl_assert(rc == GIT_ITEROVER);
+
+ git_message_trailer_iterator_free(iterator);
+}
+
+void test_message_trailer__iterator_no_newline(void)
+{
+ git_message_trailer_iterator *iterator;
+ const char *key;
+ const char *value;
+ int rc;
+
+ rc = git_message_trailer_iterator_new(
+ &iterator,
+ "Message\n"
+ "\n"
+ "Key:value");
+
+ cl_assert(rc == 0);
+
+ rc = git_message_trailer_iterator_next(&key, &value, iterator);
+
+ cl_assert(rc == 0);
+
+ cl_assert_equal_s("Key", key);
+ cl_assert_equal_s("value", value);
+
+ rc = git_message_trailer_iterator_next(&key, &value, iterator);
+
+ cl_assert(rc == GIT_ITEROVER);
+
+ git_message_trailer_iterator_free(iterator);
+}
+
+void test_message_trailer__iterator_not_last_paragraph(void)
+{
+ git_message_trailer_iterator *iterator;
+ const char *key;
+ const char *value;
+ int rc;
+
+ rc = git_message_trailer_iterator_new(
+ &iterator,
+ "Message\n"
+ "\n"
+ "Key: value\n"
+ "\n"
+ "More stuff\n");
+
+ cl_assert(rc == 0);
+
+ rc = git_message_trailer_iterator_next(&key, &value, iterator);
+
+ cl_assert(rc == GIT_ITEROVER);
+
+ git_message_trailer_iterator_free(iterator);
+}
+
+void test_message_trailer__iterator_conflicts(void)
+{
+ git_message_trailer_iterator *iterator;
+ const char *key;
+ const char *value;
+ int rc;
+
+ rc = git_message_trailer_iterator_new(
+ &iterator,
+ "Message\n"
+ "\n"
+ "Key: value\n"
+ "\n"
+ "Conflicts:\n"
+ "\tfoo.c\n");
+
+ cl_assert(rc == 0);
+
+ rc = git_message_trailer_iterator_next(&key, &value, iterator);
+
+ cl_assert(rc == 0);
+
+ cl_assert_equal_s("Key", key);
+ cl_assert_equal_s("value", value);
+
+ rc = git_message_trailer_iterator_next(&key, &value, iterator);
+
+ cl_assert(rc == GIT_ITEROVER);
+
+ git_message_trailer_iterator_free(iterator);
+}
+
+void test_message_trailer__iterator_patch(void)
+{
+ git_message_trailer_iterator *iterator;
+ const char *key;
+ const char *value;
+ int rc;
+
+ rc = git_message_trailer_iterator_new(
+ &iterator,
+ "Message\n"
+ "\n"
+ "Key: value\n"
+ "\n"
+ "---\n"
+ "More: stuff\n");
+
+ cl_assert(rc == 0);
+
+ rc = git_message_trailer_iterator_next(&key, &value, iterator);
+
+ cl_assert(rc == 0);
+
+ cl_assert_equal_s("Key", key);
+ cl_assert_equal_s("value", value);
+
+ rc = git_message_trailer_iterator_next(&key, &value, iterator);
+
+ cl_assert(rc == GIT_ITEROVER);
+
+ git_message_trailer_iterator_free(iterator);
+}
+
+void test_message_trailer__iterator_continuation(void)
+{
+ git_message_trailer_iterator *iterator;
+ const char *key;
+ const char *value;
+ int rc;
+
+ rc = git_message_trailer_iterator_new(
+ &iterator,
+ "Message\n"
+ "\n"
+ "A: b\n"
+ " c\n"
+ "D: e\n"
+ " f: g h\n"
+ "I: j\n");
+
+ cl_assert(rc == 0);
+
+ rc = git_message_trailer_iterator_next(&key, &value, iterator);
+
+ cl_assert(rc == 0);
+
+ cl_assert_equal_s("A", key);
+ cl_assert_equal_s("b\n c", value);
+
+ rc = git_message_trailer_iterator_next(&key, &value, iterator);
+
+ cl_assert(rc == 0);
+
+ cl_assert_equal_s("D", key);
+ cl_assert_equal_s("e\n f: g h", value);
+
+ rc = git_message_trailer_iterator_next(&key, &value, iterator);
+
+ cl_assert(rc == 0);
+
+ cl_assert_equal_s("I", key);
+ cl_assert_equal_s("j", value);
+
+ rc = git_message_trailer_iterator_next(&key, &value, iterator);
+
+ cl_assert(rc == GIT_ITEROVER);
+
+ git_message_trailer_iterator_free(iterator);
+}
+
+void test_message_trailer__iterator_invalid(void)
+{
+ git_message_trailer_iterator *iterator;
+ const char *key;
+ const char *value;
+ int rc;
+
+ rc = git_message_trailer_iterator_new(
+ &iterator,
+ "Message\n"
+ "\n"
+ "Signed-off-by: some@one.com\n"
+ "Not a trailer\n"
+ "Another: trailer\n");
+
+ cl_assert(rc == 0);
+
+ rc = git_message_trailer_iterator_next(&key, &value, iterator);
+
+ cl_assert(rc == 0);
+
+ cl_assert_equal_s("Signed-off-by", key);
+ cl_assert_equal_s("some@one.com", value);
+
+ rc = git_message_trailer_iterator_next(&key, &value, iterator);
+
+ cl_assert(rc == 0);
+
+ cl_assert_equal_s("Another", key);
+ cl_assert_equal_s("trailer", value);
+
+ rc = git_message_trailer_iterator_next(&key, &value, iterator);
+
+ cl_assert(rc == GIT_ITEROVER);
+
+ git_message_trailer_iterator_free(iterator);
+}