1 /* $NetBSD: split_qnameval.c,v 1.2 2020/03/18 19:05:22 christos Exp $ */ 2 3 /*++ 4 /* NAME 5 /* split_qnameval 3 6 /* SUMMARY 7 /* name-value splitter 8 /* SYNOPSIS 9 /* #include <stringops.h> 10 /* 11 /* const char *split_qnameval(buf, name, value) 12 /* char *buf; 13 /* char **name; 14 /* char **value; 15 /* DESCRIPTION 16 /* split_qnameval() expects text of the form "key = value" 17 /* or "key =", where the key may be quoted with backslash or 18 /* double quotes. The buffer argument is broken up into the key 19 /* and value substrings. 20 /* 21 /* Arguments: 22 /* .IP buf 23 /* Result from readlline() or equivalent. The buffer is modified. 24 /* .IP key 25 /* Upon successful completion, this is set to the key 26 /* substring. 27 /* .IP value 28 /* Upon successful completion, this is set to the value 29 /* substring. 30 /* SEE ALSO 31 /* split_nameval(3) name-value splitter 32 /* BUGS 33 /* DIAGNOSTICS 34 /* The result is a null pointer in case of success, a string 35 /* describing the error otherwise: missing '=' after attribute 36 /* name; missing attribute name. 37 /* LICENSE 38 /* .ad 39 /* .fi 40 /* The Secure Mailer license must be distributed with this software. 41 /* AUTHOR(S) 42 /* Wietse Venema 43 /* Google, Inc. 44 /* 111 8th Avenue 45 /* New York, NY 10011, USA 46 /*--*/ 47 48 /* System libraries. */ 49 50 #include <sys_defs.h> 51 #include <ctype.h> 52 #include <string.h> 53 54 /* Utility library. */ 55 56 #include <msg.h> 57 #include <stringops.h> 58 59 /* split_qnameval - split "key = value", support quoted key */ 60 61 const char *split_qnameval(char *buf, char **pkey, char **pvalue) 62 { 63 int in_quotes = 0; 64 char *key; 65 char *key_end; 66 char *value; 67 68 for (key = buf; *key && ISSPACE(*key); key++) 69 /* void */ ; 70 if (*key == 0) 71 return ("no key found; expected format: key = value"); 72 73 for (key_end = key; *key_end; key_end++) { 74 if (*key_end == '\\') { 75 if (*++key_end == 0) 76 break; 77 } else if (ISSPACE(*key_end) || *key_end == '=') { 78 if (!in_quotes) 79 break; 80 } else if (*key_end == '"') { 81 in_quotes = !in_quotes; 82 } 83 } 84 if (in_quotes) { 85 return ("unbalanced '\"\'"); 86 } 87 value = key_end; 88 while (ISSPACE(*value)) 89 value++; 90 if (*value != '=') 91 return ("missing '=' after attribute name"); 92 *key_end = 0; 93 do { 94 value++; 95 } while (ISSPACE(*value)); 96 trimblanks(value, 0)[0] = 0; 97 *pkey = key; 98 *pvalue = value; 99 return (0); 100 } 101 102 #ifdef TEST 103 104 #include <stdlib.h> 105 #include <unistd.h> 106 #include <string.h> 107 108 #include <mymalloc.h> 109 110 static int compare(int test_number, const char *what, 111 const char *expect, const char *real) 112 { 113 if ((expect == 0 && real == 0) 114 || (expect != 0 && real != 0 && strcmp(expect, real) == 0)) { 115 return (0); 116 } else { 117 msg_warn("test %d: %s mis-match: expect='%s', real='%s'", 118 test_number, what, expect ? expect : "(null)", 119 real ? real : "(null)"); 120 return (1); 121 } 122 } 123 124 int main(int argc, char **argv) 125 { 126 struct test_info { 127 const char *input; 128 const char *expect_result; 129 const char *expect_key; 130 const char *expect_value; 131 }; 132 static const struct test_info test_info[] = { 133 /* Unquoted keys. */ 134 {"xx = yy", 0, "xx", "yy"}, 135 {"xx=yy", 0, "xx", "yy"}, 136 {"xx =", 0, "xx", ""}, 137 {"xx=", 0, "xx", ""}, 138 {"xx", "missing '=' after attribute name", 0, 0}, 139 /* Quoted keys. */ 140 {"\"xx \" = yy", 0, "\"xx \"", "yy"}, 141 {"\"xx \"= yy", 0, "\"xx \"", "yy"}, 142 {"\"xx \" =", 0, "\"xx \"", ""}, 143 {"\"xx \"=", 0, "\"xx \"", ""}, 144 {"\"xx \"", "missing '=' after attribute name", 0, 0}, 145 {"\"xx ", "unbalanced '\"'", 0, 0}, 146 /* Backslash escapes. */ 147 {"\"\\\"xx \" = yy", 0, "\"\\\"xx \"", "yy"}, 148 {0,}, 149 }; 150 151 int errs = 0; 152 const struct test_info *tp; 153 154 for (tp = test_info; tp->input != 0; tp++) { 155 const char *result; 156 char *key = 0; 157 char *value = 0; 158 char *buf = mystrdup(tp->input); 159 int test_number = (int) (tp - test_info); 160 161 result = split_qnameval(buf, &key, &value); 162 errs += compare(test_number, "result", tp->expect_result, result); 163 errs += compare(test_number, "key", tp->expect_key, key); 164 errs += compare(test_number, "value", tp->expect_value, value); 165 myfree(buf); 166 } 167 exit(errs); 168 } 169 170 #endif 171