chiark / gitweb /
more coverage + doxygen
authorRichard Kettlewell <rjk@greenend.org.uk>
Sat, 6 Dec 2008 14:24:24 +0000 (14:24 +0000)
committerRichard Kettlewell <rjk@greenend.org.uk>
Sat, 6 Dec 2008 14:24:24 +0000 (14:24 +0000)
lib/kvp.c
lib/kvp.h
libtests/t-kvp.c
libtests/t-regsub.c
libtests/t-url.c

index 748c47e..5a116de 100644 (file)
--- a/lib/kvp.c
+++ b/lib/kvp.c
 #include "hex.h"
 #include "sink.h"
 
+/** @brief Decode a URL-encoded string to a ink
+ * @param sink Where to store result
+ * @param ptr Start of string
+ * @param n Length of string
+ * @return 0 on success, non-0 if string could not be decoded or sink write failed
+ */
 int urldecode(struct sink *sink, const char *ptr, size_t n) {
   int c, d1, d2;
   
@@ -69,6 +75,15 @@ static char *decode(const char *ptr, size_t n) {
   return d.vec;
 }
 
+/** @brief Decode a URL-decoded key-value pair list
+ * @param ptr Start of input string
+ * @param n Length of input string
+ * @return @ref kvp of values from input
+ *
+ * The KVP is in the same order as the original input.
+ *
+ * If the original input contains duplicates names, so will the KVP.
+ */
 struct kvp *kvp_urldecode(const char *ptr, size_t n) {
   struct kvp *kvp, **kk = &kvp, *k;
   const char *q, *r, *top = ptr + n, *next;
@@ -92,6 +107,12 @@ struct kvp *kvp_urldecode(const char *ptr, size_t n) {
   return kvp;
 }
 
+/** @brief URL-encode a string to a sink
+ * @param sink Where to send output
+ * @param s String to encode
+ * @param n Length of string to encode
+ * @return 0 on success or non-0 if sink write failed
+ */
 int urlencode(struct sink *sink, const char *s, size_t n) {
   unsigned char c;
 
@@ -153,6 +174,11 @@ char *urldecodestring(const char *s, size_t ns) {
   return d.vec;
 }
 
+/** @brief URL-encode a KVP
+ * @param kvp Linked list to encode
+ * @param np Where to store length (or NULL)
+ * @return Newly created string
+ */
 char *kvp_urlencode(const struct kvp *kvp, size_t *np) {
   struct dynstr d;
   struct sink *sink;
@@ -173,6 +199,20 @@ char *kvp_urlencode(const struct kvp *kvp, size_t *np) {
   return d.vec;
 }
 
+/** @brief Set or remove a value in a @ref kvp
+ * @param kvpp Address of KVP head to modify
+ * @param name Key to search for
+ * @param value New value or NULL to delete
+ * @return 1 if any change was made otherwise 0
+ *
+ * If @p value is not NULL then the first matching key is replaced; if
+ * there was no matching key a new one is added at the end.
+ *
+ * If @p value is NULL then the first matching key is removed.
+ *
+ * If anything actually changes the return value is 1.  If no actual
+ * change is made then 0 is returned instead.
+ */
 int kvp_set(struct kvp **kvpp, const char *name, const char *value) {
   struct kvp *k, **kk;
 
@@ -200,12 +240,29 @@ int kvp_set(struct kvp **kvpp, const char *name, const char *value) {
   }
 }
 
+/** @brief Look up a value in a @ref kvp
+ * @param kvp Head of KVP linked list
+ * @param name Key to search for
+ * @return Value or NULL
+ *
+ * The returned value is owned by the KVP so must not be modified or
+ * freed.
+ */
 const char *kvp_get(const struct kvp *kvp, const char *name) {
   for(;kvp && strcmp(kvp->name, name); kvp = kvp->next)
     ;
   return kvp ? kvp->value : 0;
 }
 
+/** @brief Construct a KVP from arguments
+ * @param name First name
+ * @return Newly created KVP
+ *
+ * Arguments must come in name/value pairs and must be followed by a (char *)0.
+ *
+ * The order of the new KVP is not formally defined though the test
+ * programs rely on it nonetheless so update them if you change it.
+ */
 struct kvp *kvp_make(const char *name, ...) {
   const char *value;
   struct kvp *kvp = 0, *k;
@@ -216,7 +273,7 @@ struct kvp *kvp_make(const char *name, ...) {
     value = va_arg(ap, const char *);
     k = xmalloc(sizeof *k);
     k->name = name;
-    k->value = value ? xstrdup(value) : value;
+    k->value = value ? xstrdup(value) : "";
     k->next = kvp;
     kvp = k;
     name = va_arg(ap, const char *);
index 745304b..0a7e478 100644 (file)
--- a/lib/kvp.h
+++ b/lib/kvp.h
 struct dynstr;
 struct sink;
 
+/** @brief Linked list of key-value pairs */
 struct kvp {
-  struct kvp *next;                    /* next entry */
-  const char *name;                    /* name */
-  const char *value;                   /* value */
+  /** @brief Next entry */
+  struct kvp *next;
+
+  /** @brief Name
+   *
+   * Might not be unique.  Must not be null.
+   */
+  const char *name;
+
+  /** @brief Value
+   *
+   * Must not be null.
+   */
+  const char *value;
 };
 
 struct kvp *kvp_urldecode(const char *ptr, size_t n);
index d9835c1..f2e359a 100644 (file)
@@ -58,6 +58,16 @@ static void test_kvp(void) {
   check_integer(urldecode(sink_error(), "bar=foo", 7), -1);
   check_integer(urlencode(sink_error(), "wibble", 7), -1);
   check_integer(urlencode(sink_error(), " ", 1), -1);
+  k = kvp_make("wibble", "spong",
+               "blit", "blat",
+               (char *)0);
+  check_string(kvp_urlencode(k, &n),
+               "blit=blat&wibble=spong");
+  k = kvp_make("wibble", (char *)0,
+               "blit", "blat",
+               (char *)0);
+  check_string(kvp_urlencode(k, &n),
+               "blit=blat&wibble=");
 }
 
 TEST(kvp);
index 75db82b..a1e2220 100644 (file)
@@ -50,6 +50,8 @@ static void test_regsub(void) {
                "bspong");
   check_string(regsub(re, "baaaaa", "foo-$&-bar", 0),
                "bfoo-aaaaa-bar");
+  check_string(regsub(re, "baaaaa", "foo-$&-bar$x", 0),
+               "bfoo-aaaaa-bar$x");
 
   re = pcre_compile("(a+)(b+)", PCRE_UTF8|PCRE_CASELESS, &errstr, &erroffset, 0);
   assert(re != 0);
index 63811c2..cf680ea 100644 (file)
@@ -46,16 +46,21 @@ static void test_url(void) {
   insist(parse_url("http://www.example.com/example%2zpath", &p) == -1);
 
   setenv("SERVER_NAME", "www.anjou.terraraq.org.uk", 1);
-  setenv("SERVER_PORT", "80", 1);
   setenv("SCRIPT_NAME", "/~richard/env.cgi", 1);
   check_string(infer_url(1),
                "http://www.anjou.terraraq.org.uk/~richard/env.cgi");
+  setenv("SERVER_PORT", "80", 1);
+  check_string(infer_url(1),
+               "http://www.anjou.terraraq.org.uk/~richard/env.cgi");
   setenv("HTTPS", "on", 1);
   check_string(infer_url(1),
                "https://www.anjou.terraraq.org.uk/~richard/env.cgi");
   setenv("QUERY_STRING", "foo", 1);
   check_string(infer_url(1),
                "https://www.anjou.terraraq.org.uk/~richard/env.cgi");
+  setenv("SCRIPT_NAME", "", 1);
+  check_string(infer_url(1),
+               "https://www.anjou.terraraq.org.uk/");
   setenv("REQUEST_URI", "/~richard/env%2ecgi", 1);
   check_string(infer_url(1),
                "https://www.anjou.terraraq.org.uk/~richard/env%2ecgi");