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 61const 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 110static 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 124int 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