notmuch-mutt: replace shell pipeline with internal pipe processing
[notmuch] / lib / message-id.c
1 #include "notmuch-private.h"
2 #include "string-util.h"
3
4 /* Advance 'str' past any whitespace or RFC 822 comments. A comment is
5  * a (potentially nested) parenthesized sequence with '\' used to
6  * escape any character (including parentheses).
7  *
8  * If the sequence to be skipped continues to the end of the string,
9  * then 'str' will be left pointing at the final terminating '\0'
10  * character.
11  */
12 static void
13 skip_space_and_comments (const char **str)
14 {
15     const char *s;
16
17     s = *str;
18     while (*s && (isspace (*s) || *s == '(')) {
19         while (*s && isspace (*s))
20             s++;
21         if (*s == '(') {
22             int nesting = 1;
23             s++;
24             while (*s && nesting) {
25                 if (*s == '(') {
26                     nesting++;
27                 } else if (*s == ')') {
28                     nesting--;
29                 } else if (*s == '\\') {
30                     if (*(s + 1))
31                         s++;
32                 }
33                 s++;
34             }
35         }
36     }
37
38     *str = s;
39 }
40
41 char *
42 _notmuch_message_id_parse (void *ctx, const char *message_id, const char **next)
43 {
44     const char *s, *end;
45     char *result;
46
47     if (message_id == NULL || *message_id == '\0')
48         return NULL;
49
50     s = message_id;
51
52     skip_space_and_comments (&s);
53
54     /* Skip any unstructured text as well. */
55     while (*s && *s != '<')
56         s++;
57
58     if (*s == '<') {
59         s++;
60     } else {
61         if (next)
62             *next = s;
63         return NULL;
64     }
65
66     skip_space_and_comments (&s);
67
68     end = s;
69     while (*end && *end != '>')
70         end++;
71     if (next) {
72         if (*end)
73             *next = end + 1;
74         else
75             *next = end;
76     }
77
78     if (end > s && *end == '>')
79         end--;
80     if (end <= s)
81         return NULL;
82
83     result = talloc_strndup (ctx, s, end - s + 1);
84
85     /* Finally, collapse any whitespace that is within the message-id
86      * itself. */
87     {
88         char *r;
89         int len;
90
91         for (r = result, len = strlen (r); *r; r++, len--)
92             if (*r == ' ' || *r == '\t')
93                 memmove (r, r + 1, len);
94     }
95
96     return result;
97 }
98
99 char *
100 _notmuch_message_id_parse_strict (void *ctx, const char *message_id)
101 {
102     const char *s, *end;
103
104     if (message_id == NULL || *message_id == '\0')
105         return NULL;
106
107     s = skip_space (message_id);
108     if (*s == '<')
109         s++;
110     else
111         return NULL;
112
113     for (end = s; *end && *end != '>'; end++)
114         if (isspace (*end))
115             return NULL;
116
117     if (*end != '>')
118         return NULL;
119     else {
120         const char *last = skip_space (end + 1);
121         if (*last != '\0')
122             return NULL;
123     }
124
125     return talloc_strndup (ctx, s, end - s);
126 }