summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--opentracker.c4
-rw-r--r--ot_config.c22
-rw-r--r--ot_http.c23
-rw-r--r--ot_ip.c2
-rw-r--r--ot_ip.h2
-rw-r--r--ot_scan.c29
-rw-r--r--ot_scan.h12
-rw-r--r--ot_sync.c2
-rw-r--r--ot_urlencoded.c13
-rw-r--r--ot_urlencoded.h6
-rw-r--r--proxy.c4
-rw-r--r--tests/t_ip.c54
-rw-r--r--tests/t_scan.c124
-rw-r--r--tests/t_urlencoded.c29
14 files changed, 179 insertions, 147 deletions
diff --git a/opentracker.c b/opentracker.c
index f0f6404..7de6c6a 100644
--- a/opentracker.c
+++ b/opentracker.c
@@ -379,10 +379,10 @@ void load_state(const char *const state_filename) {
379 continue; 379 continue;
380 i = 2 * sizeof(ot_hash); 380 i = 2 * sizeof(ot_hash);
381 381
382 if (inbuf[i++] != ':' || !(consumed = scan_udec(inbuf + i, &base, UINT64_MAX))) 382 if (inbuf[i++] != ':' || !(consumed = scan_uint(inbuf + i, &base, UINT64_MAX)))
383 continue; 383 continue;
384 i += consumed; 384 i += consumed;
385 if (inbuf[i++] != ':' || !(consumed = scan_udec(inbuf + i, &downcount, UINT64_MAX))) 385 if (inbuf[i++] != ':' || !(consumed = scan_uint(inbuf + i, &downcount, UINT64_MAX)))
386 continue; 386 continue;
387 add_torrent_from_saved_state(infohash, base, downcount); 387 add_torrent_from_saved_state(infohash, base, downcount);
388 } 388 }
diff --git a/ot_config.c b/ot_config.c
index a5fc06e..32625bd 100644
--- a/ot_config.c
+++ b/ot_config.c
@@ -107,7 +107,7 @@ static int listen_scan_options(char *p, int *shared, int *workers) {
107 *shared = 1; 107 *shared = 1;
108 break; 108 break;
109 case LISTEN_OPTION_WORKERS: { 109 case LISTEN_OPTION_WORKERS: {
110 size_t off = scan_udec(p, &value, UINT_MAX); 110 size_t off = scan_uint(p, &value, UINT_MAX);
111 111
112 if (!off) 112 if (!off)
113 return 0; 113 return 0;
@@ -140,7 +140,7 @@ static int scan_ip6_port(const char *src, ot_ip6 ip, uint16_t *port) {
140 ++s; 140 ++s;
141 if (*s == '[') 141 if (*s == '[')
142 ++s, ++bracket; /* for v6 style notation */ 142 ++s, ++bracket; /* for v6 style notation */
143 if (!(off = ip_scan(s, ip))) 143 if (!(off = ip_parse(s, ip)))
144 return 0; 144 return 0;
145 s += off; 145 s += off;
146 if (bracket && *s == ']') 146 if (bracket && *s == ']')
@@ -157,7 +157,7 @@ static int scan_ip6_port(const char *src, ot_ip6 ip, uint16_t *port) {
157 if (*(s++) != ':') 157 if (*(s++) != ':')
158 return 0; 158 return 0;
159 } 159 }
160 if (!(off = scan_udec(s, &value, 65535))) 160 if (!(off = scan_uint(s, &value, 65535)))
161 return 0; 161 return 0;
162 *port = (uint16_t)value; 162 *port = (uint16_t)value;
163 return off + s - src; 163 return off + s - src;
@@ -169,14 +169,14 @@ static int scan_ip6_net(const char *src, ot_net *net) {
169 int off; 169 int off;
170 while (isspace(*s)) 170 while (isspace(*s))
171 ++s; 171 ++s;
172 if (!(off = ip_scan(s, net->address))) 172 if (!(off = ip_parse(s, net->address)))
173 return 0; 173 return 0;
174 s += off; 174 s += off;
175 if (*s != '/') 175 if (*s != '/')
176 net->bits = 128; 176 net->bits = 128;
177 else { 177 else {
178 s++; 178 s++;
179 if (!(off = scan_udec(s, &value, 128))) 179 if (!(off = scan_uint(s, &value, 128)))
180 return 0; 180 return 0;
181 net->bits = (int)value; 181 net->bits = (int)value;
182 if (ip_is_v4mapped(net->address)) 182 if (ip_is_v4mapped(net->address))
@@ -324,12 +324,12 @@ static int parse_configfile(char *config_filename) {
324 break; 324 break;
325 } 325 }
326 case CONFIG_LISTEN_UDP_WORKERS: 326 case CONFIG_LISTEN_UDP_WORKERS:
327 if (!scan_udec(p, &value, UINT_MAX)) 327 if (!scan_uint(p, &value, UINT_MAX))
328 goto parse_error; 328 goto parse_error;
329 g_listen_workers = (int)value; 329 g_listen_workers = (int)value;
330 break; 330 break;
331 case CONFIG_LISTEN_SHARED: 331 case CONFIG_LISTEN_SHARED:
332 if (!scan_udec(p, &value, 1)) 332 if (!scan_uint(p, &value, 1))
333 goto parse_error; 333 goto parse_error;
334 g_listen_shared = (int)value; 334 g_listen_shared = (int)value;
335 break; 335 break;
@@ -479,7 +479,7 @@ void config_parse(int argc, char **argv) {
479 scanon = 0; 479 scanon = 0;
480 break; 480 break;
481 case 'i': 481 case 'i':
482 if (!ip_scan(optarg, serverip)) { 482 if (!ip_parse(optarg, serverip)) {
483 usage(argv[0]); 483 usage(argv[0]);
484 exit(1); 484 exit(1);
485 } 485 }
@@ -494,7 +494,7 @@ void config_parse(int argc, char **argv) {
494 break; 494 break;
495#endif 495#endif
496 case 'p': 496 case 'p':
497 if (!scan_udec(optarg, &value, 65535)) { 497 if (!scan_uint(optarg, &value, 65535)) {
498 usage(argv[0]); 498 usage(argv[0]);
499 exit(1); 499 exit(1);
500 } 500 }
@@ -502,7 +502,7 @@ void config_parse(int argc, char **argv) {
502 bound++; 502 bound++;
503 break; 503 break;
504 case 'P': 504 case 'P':
505 if (!scan_udec(optarg, &value, 65535)) { 505 if (!scan_uint(optarg, &value, 65535)) {
506 usage(argv[0]); 506 usage(argv[0]);
507 exit(1); 507 exit(1);
508 } 508 }
@@ -511,7 +511,7 @@ void config_parse(int argc, char **argv) {
511 break; 511 break;
512#ifdef WANT_SYNC_LIVE 512#ifdef WANT_SYNC_LIVE
513 case 's': 513 case 's':
514 if (!scan_udec(optarg, &value, 65535)) { 514 if (!scan_uint(optarg, &value, 65535)) {
515 usage(argv[0]); 515 usage(argv[0]);
516 exit(1); 516 exit(1);
517 } 517 }
diff --git a/ot_http.c b/ot_http.c
index 6c3332c..3c135cb 100644
--- a/ot_http.c
+++ b/ot_http.c
@@ -482,7 +482,8 @@ static ot_keywords keywords_announce[] = {{"port", 1}, {"left", 2}, {"event",
482static ot_keywords keywords_announce_event[] = {{"completed", 1}, {"stopped", 2}, {NULL, -3}}; 482static ot_keywords keywords_announce_event[] = {{"completed", 1}, {"stopped", 2}, {NULL, -3}};
483static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct *ws, char *read_ptr) { 483static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct *ws, char *read_ptr) {
484 size_t numwant; 484 size_t numwant;
485 int tmp, scanon; 485 int64_t tmp;
486 int scanon;
486 unsigned short port = 0; 487 unsigned short port = 0;
487 char *write_ptr; 488 char *write_ptr;
488 ssize_t len; 489 ssize_t len;
@@ -501,7 +502,7 @@ static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct
501 if (accesslist_is_blessed(cookie->ip, OT_PERMISSION_MAY_PROXY)) { 502 if (accesslist_is_blessed(cookie->ip, OT_PERMISSION_MAY_PROXY)) {
502 ot_ip6 proxied_ip; 503 ot_ip6 proxied_ip;
503 char *fwd = http_header(ws->request, ws->header_size, "x-forwarded-for"); 504 char *fwd = http_header(ws->request, ws->header_size, "x-forwarded-for");
504 if (fwd && ip_scan(fwd, proxied_ip)) { 505 if (fwd && ip_parse(fwd, proxied_ip)) {
505 OT_SETIP(ws->peer, proxied_ip); 506 OT_SETIP(ws->peer, proxied_ip);
506 } else 507 } else
507 OT_SETIP(ws->peer, cookie->ip); 508 OT_SETIP(ws->peer, cookie->ip);
@@ -529,15 +530,15 @@ static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct
529 break; 530 break;
530 case 1: /* matched "port" */ 531 case 1: /* matched "port" */
531 len = urlencoded_parse(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE); 532 len = urlencoded_parse(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE);
532 if ((len <= 0) || scan_fixed_int(write_ptr, len, &tmp) || (tmp > 0xffff)) 533 if ((len <= 0) || scan_int(write_ptr, len, &tmp) || (tmp > 0xffff))
533 HTTPERROR_400_PARAM; 534 HTTPERROR_400_PARAM;
534 port = htons(tmp); 535 port = htons((uint16_t)tmp);
535 OT_SETPORT(&ws->peer, &port); 536 OT_SETPORT(&ws->peer, &port);
536 break; 537 break;
537 case 2: /* matched "left" */ 538 case 2: /* matched "left" */
538 if ((len = urlencoded_parse(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE)) <= 0) 539 if ((len = urlencoded_parse(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE)) <= 0)
539 HTTPERROR_400_PARAM; 540 HTTPERROR_400_PARAM;
540 if (scan_fixed_int(write_ptr, len, &tmp)) 541 if (scan_int(write_ptr, len, &tmp))
541 tmp = 0; 542 tmp = 0;
542 if (!tmp) 543 if (!tmp)
543 OT_PEERFLAG(&ws->peer) |= PEER_FLAG_SEEDING; 544 OT_PEERFLAG(&ws->peer) |= PEER_FLAG_SEEDING;
@@ -558,17 +559,17 @@ static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct
558 break; 559 break;
559 case 4: /* matched "numwant" */ 560 case 4: /* matched "numwant" */
560 len = urlencoded_parse(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE); 561 len = urlencoded_parse(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE);
561 if ((len <= 0) || scan_fixed_int(write_ptr, len, &tmp)) 562 if ((len <= 0) || scan_int(write_ptr, len, &tmp))
562 HTTPERROR_400_PARAM; 563 HTTPERROR_400_PARAM;
563 if (tmp < 0) 564 if (tmp < 0)
564 tmp = 50; 565 tmp = 50;
565 if (tmp > 200) 566 if (tmp > 200)
566 tmp = 200; 567 tmp = 200;
567 numwant = tmp; 568 numwant = (size_t)tmp;
568 break; 569 break;
569 case 5: /* matched "compact" */ 570 case 5: /* matched "compact" */
570 len = urlencoded_parse(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE); 571 len = urlencoded_parse(&read_ptr, write_ptr = read_ptr, SCAN_SEARCHPATH_VALUE);
571 if ((len <= 0) || scan_fixed_int(write_ptr, len, &tmp)) 572 if ((len <= 0) || scan_int(write_ptr, len, &tmp))
572 HTTPERROR_400_PARAM; 573 HTTPERROR_400_PARAM;
573 if (!tmp) 574 if (!tmp)
574 HTTPERROR_400_COMPACT; 575 HTTPERROR_400_COMPACT;
@@ -587,7 +588,7 @@ static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct
587 char *tmp_buf1 = ws->reply, *tmp_buf2 = ws->reply + 16; 588 char *tmp_buf1 = ws->reply, *tmp_buf2 = ws->reply + 16;
588 len = urlencoded_parse(&read_ptr, tmp_buf2, SCAN_SEARCHPATH_VALUE); 589 len = urlencoded_parse(&read_ptr, tmp_buf2, SCAN_SEARCHPATH_VALUE);
589 tmp_buf2[len] = 0; 590 tmp_buf2[len] = 0;
590 if ((len <= 0) || !ip_scan(tmp_buf2, tmp_buf1)) 591 if ((len <= 0) || !ip_parse(tmp_buf2, tmp_buf1))
591 HTTPERROR_400_PARAM; 592 HTTPERROR_400_PARAM;
592 OT_SETIP(&ws->peer, tmp_buf1); 593 OT_SETIP(&ws->peer, tmp_buf1);
593 } break; 594 } break;
@@ -609,13 +610,13 @@ static ssize_t http_handle_announce(const int64_t sock, struct ot_workstruct
609 loglist_reset(); 610 loglist_reset();
610 return ws->reply_size = sprintf(ws->reply, "Successfully removed.\n"); 611 return ws->reply_size = sprintf(ws->reply, "Successfully removed.\n");
611 } 612 }
612 parsed = ip_scan(tmp_buf, net.address); 613 parsed = ip_parse(tmp_buf, net.address);
613 if (!parsed) 614 if (!parsed)
614 HTTPERROR_400_PARAM; 615 HTTPERROR_400_PARAM;
615 if (tmp_buf[parsed++] != '/') 616 if (tmp_buf[parsed++] != '/')
616 bits = 128; 617 bits = 128;
617 else { 618 else {
618 parsed = scan_udec(tmp_buf + parsed, &value, 128); 619 parsed = scan_uint(tmp_buf + parsed, &value, 128);
619 if (!parsed) 620 if (!parsed)
620 HTTPERROR_400_PARAM; 621 HTTPERROR_400_PARAM;
621 bits = (signed short)value; 622 bits = (signed short)value;
diff --git a/ot_ip.c b/ot_ip.c
index 8cde7d2..e694883 100644
--- a/ot_ip.c
+++ b/ot_ip.c
@@ -120,7 +120,7 @@ size_t ip_format(char *dest, const ot_ip6 ip) {
120 return (size_t)(d - dest); 120 return (size_t)(d - dest);
121} 121}
122 122
123size_t ip_scan(const char *src, ot_ip6 ip) { 123size_t ip_parse(const char *src, ot_ip6 ip) {
124 uint8_t prefix[16], suffix[16]; 124 uint8_t prefix[16], suffix[16];
125 size_t prefixlen = 0, suffixlen = 0; 125 size_t prefixlen = 0, suffixlen = 0;
126 const char *s = src; 126 const char *s = src;
diff --git a/ot_ip.h b/ot_ip.h
index ac8964d..344bd45 100644
--- a/ot_ip.h
+++ b/ot_ip.h
@@ -23,7 +23,7 @@ static inline int ip_is_v4mapped(const void *ip) { return !memcmp(ip, ip_v4mappe
23 23
24 The count lets the caller carry on where the address ended, which is how 24 The count lets the caller carry on where the address ended, which is how
25 "address:port" and "address/bits" are read. */ 25 "address:port" and "address/bits" are read. */
26size_t ip_scan(const char *src, ot_ip6 ip); 26size_t ip_parse(const char *src, ot_ip6 ip);
27 27
28/* Write an address and return how many characters it took. Nothing is 28/* Write an address and return how many characters it took. Nothing is
29 terminated, so a caller may keep appending. A v4 mapped address is written 29 terminated, so a caller may keep appending. A v4 mapped address is written
diff --git a/ot_scan.c b/ot_scan.c
index a96f82b..c4b951a 100644
--- a/ot_scan.c
+++ b/ot_scan.c
@@ -11,7 +11,7 @@
11/* Opentracker */ 11/* Opentracker */
12#include "ot_scan.h" 12#include "ot_scan.h"
13 13
14size_t scan_udec(const char *src, uint64_t *dest, uint64_t max) { 14size_t scan_uint(const char *src, uint64_t *dest, uint64_t max) {
15 unsigned long long value; 15 unsigned long long value;
16 char *end; 16 char *end;
17 17
@@ -28,9 +28,9 @@ size_t scan_udec(const char *src, uint64_t *dest, uint64_t max) {
28 return (size_t)(end - src); 28 return (size_t)(end - src);
29} 29}
30 30
31size_t scan_udec_bencode(const char *src, uint64_t *dest, uint64_t max) { 31size_t scan_uint_bencode(const char *src, uint64_t *dest, uint64_t max) {
32 uint64_t value; 32 uint64_t value;
33 size_t consumed = scan_udec(src, &value, max); 33 size_t consumed = scan_uint(src, &value, max);
34 34
35 /* bencode writes no leading zeros, so anything longer than a single digit 35 /* bencode writes no leading zeros, so anything longer than a single digit
36 must not begin with one. */ 36 must not begin with one. */
@@ -42,3 +42,26 @@ size_t scan_udec_bencode(const char *src, uint64_t *dest, uint64_t max) {
42 42
43 return consumed; 43 return consumed;
44} 44}
45
46int scan_int(const char *src, size_t len, int64_t *dest) {
47 int64_t value = 0;
48 int negate = 0;
49
50 if (len && *src == '-')
51 --len, ++src, negate = 1;
52 if (!len)
53 return -1;
54
55 while (len--) {
56 if (*src < '0' || *src > '9')
57 return -1;
58 /* A twenty character field of nines does not fit, and silently wrapping
59 would magically heal a nonsense announce */
60 if (value > (INT64_MAX - (*src - '0')) / 10)
61 return -1;
62 value = 10 * value + (*src++ - '0');
63 }
64
65 *dest = negate ? -value : value;
66 return 0;
67}
diff --git a/ot_scan.h b/ot_scan.h
index 860a088..ca3bbbd 100644
--- a/ot_scan.h
+++ b/ot_scan.h
@@ -7,15 +7,21 @@
7#define OT_SCAN_H__ 7#define OT_SCAN_H__
8 8
9#include <stddef.h> 9#include <stddef.h>
10#include <stddef.h>
10#include <stdint.h> 11#include <stdint.h>
11 12
12/* Read an unsigned decimal number from src into dest and return how many 13/* Read an unsigned decimal number from src into dest and return how many
13 characters were consumed. Returns zero and leaves dest untouched when src 14 characters were consumed. Returns zero and leaves dest untouched when src
14 does not begin with a digit, or when the value would exceed max. */ 15 does not begin with a digit, or when the value would exceed max. */
15size_t scan_udec(const char *src, uint64_t *dest, uint64_t max); 16size_t scan_uint(const char *src, uint64_t *dest, uint64_t max);
16 17
17/* As scan_udec, but following the bencode integer grammar, which permits a 18/* As scan_uint, but following the bencode integer grammar, which permits a
18 leading zero on zero alone. */ 19 leading zero on zero alone. */
19size_t scan_udec_bencode(const char *src, uint64_t *dest, uint64_t max); 20size_t scan_uint_bencode(const char *src, uint64_t *dest, uint64_t max);
21
22/* Read a signed decimal number that has to take up exactly len characters,
23 which is how a value lifted out of a query string arrives. Returns 0, or
24 -1 leaving dest untouched. */
25int scan_int(const char *src, size_t len, int64_t *dest);
20 26
21#endif 27#endif
diff --git a/ot_sync.c b/ot_sync.c
index 5527c94..1a6bebc 100644
--- a/ot_sync.c
+++ b/ot_sync.c
@@ -51,7 +51,7 @@ int add_changeset_to_tracker(uint8_t *data, size_t len) {
51 data += sizeof(ot_hash); 51 data += sizeof(ot_hash);
52 52
53 /* Scan string length indicator, strictly: bencode writes no leading zeros */ 53 /* Scan string length indicator, strictly: bencode writes no leading zeros */
54 data += (len = scan_udec_bencode((char *)data, &peer_count, (uint64_t)(end - data))); 54 data += (len = scan_uint_bencode((char *)data, &peer_count, (uint64_t)(end - data)));
55 55
56 /* If no long was scanned, it is not divisible by 8, it is not 56 /* If no long was scanned, it is not divisible by 8, it is not
57 followed by a colon or claims to need to much memory, we fail */ 57 followed by a colon or claims to need to much memory, we fail */
diff --git a/ot_urlencoded.c b/ot_urlencoded.c
index 1ca6e81..17f12a4 100644
--- a/ot_urlencoded.c
+++ b/ot_urlencoded.c
@@ -146,19 +146,6 @@ ssize_t urlencoded_parse(char **string, char *deststring, SCAN_SEARCHPATH_FLAG f
146 return d - (unsigned char *)deststring; 146 return d - (unsigned char *)deststring;
147} 147}
148 148
149ssize_t scan_fixed_int(char *data, size_t len, int *tmp) {
150 int minus = 0;
151 *tmp = 0;
152 if (*data == '-')
153 --len, ++data, ++minus;
154 while ((len > 0) && (*data >= '0') && (*data <= '9')) {
155 --len;
156 *tmp = 10 * *tmp + *data++ - '0';
157 }
158 if (minus)
159 *tmp = -*tmp;
160 return len;
161}
162 149
163size_t urlencoded_format(char *dest, const uint8_t *src, size_t len) { 150size_t urlencoded_format(char *dest, const uint8_t *src, size_t len) {
164 static const char safe[] = "$/.=~-_"; 151 static const char safe[] = "$/.=~-_";
diff --git a/ot_urlencoded.h b/ot_urlencoded.h
index 8147370..b6f1208 100644
--- a/ot_urlencoded.h
+++ b/ot_urlencoded.h
@@ -47,12 +47,6 @@ int urlencoded_match_keyword(const ot_keywords *keywords, char **string, SCA
47*/ 47*/
48void urlencoded_skip_value(char **string); 48void urlencoded_skip_value(char **string);
49 49
50/* data pointer to len chars of string
51 len length of chars in data to parse
52 number number to receive result
53 returns number of bytes not parsed, mostly !=0 means fail
54 */
55ssize_t scan_fixed_int(char *data, size_t len, int *number);
56 50
57/* The other direction: percent encode len bytes from src into dest and return 51/* The other direction: percent encode len bytes from src into dest and return
58 how many characters were written. dest needs room for three times len. */ 52 how many characters were written. dest needs room for three times len. */
diff --git a/proxy.c b/proxy.c
index 0cac20c..a5c6bb7 100644
--- a/proxy.c
+++ b/proxy.c
@@ -507,7 +507,7 @@ static int scan_ip6_port(const char *src, ot_ip6 ip, uint16_t *port) {
507 ++s; 507 ++s;
508 if (*s == '[') 508 if (*s == '[')
509 ++s, ++bracket; /* for v6 style notation */ 509 ++s, ++bracket; /* for v6 style notation */
510 if (!(off = ip_scan(s, ip))) 510 if (!(off = ip_parse(s, ip)))
511 return 0; 511 return 0;
512 s += off; 512 s += off;
513 if (*s == 0 || isspace(*s)) 513 if (*s == 0 || isspace(*s))
@@ -522,7 +522,7 @@ static int scan_ip6_port(const char *src, ot_ip6 ip, uint16_t *port) {
522 if (*(s++) != ':') 522 if (*(s++) != ':')
523 return 0; 523 return 0;
524 } 524 }
525 if (!(off = scan_udec(s, &value, 65535))) 525 if (!(off = scan_uint(s, &value, 65535)))
526 return 0; 526 return 0;
527 *port = (uint16_t)value; 527 *port = (uint16_t)value;
528 return off + s - src; 528 return off + s - src;
diff --git a/tests/t_ip.c b/tests/t_ip.c
index 2fe3a72..8d71679 100644
--- a/tests/t_ip.c
+++ b/tests/t_ip.c
@@ -95,27 +95,27 @@ static void case_scan_forms(void) {
95 char ip[16]; 95 char ip[16];
96 96
97 test_case("2.2 form 1, every group spelled out"); 97 test_case("2.2 form 1, every group spelled out");
98 CHECK_INT(ip_scan("1111:2222:3333:4444:5555:6666:7777:8888", ip), 39); 98 CHECK_INT(ip_parse("1111:2222:3333:4444:5555:6666:7777:8888", ip), 39);
99 CHECK_MEM(ip, "\x11\x11\x22\x22\x33\x33\x44\x44\x55\x55\x66\x66\x77\x77\x88\x88", 16); 99 CHECK_MEM(ip, "\x11\x11\x22\x22\x33\x33\x44\x44\x55\x55\x66\x66\x77\x77\x88\x88", 16);
100 100
101 test_case("2.2 form 2, zero groups abbreviated"); 101 test_case("2.2 form 2, zero groups abbreviated");
102 CHECK_INT(ip_scan("2001:db8::1", ip), 11); 102 CHECK_INT(ip_parse("2001:db8::1", ip), 11);
103 CHECK_MEM(ip, "\x20\x01\x0d\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", 16); 103 CHECK_MEM(ip, "\x20\x01\x0d\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", 16);
104 CHECK_INT(ip_scan("::1", ip), 3); 104 CHECK_INT(ip_parse("::1", ip), 3);
105 CHECK_MEM(ip, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", 16); 105 CHECK_MEM(ip, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", 16);
106 CHECK_INT(ip_scan("::", ip), 2); 106 CHECK_INT(ip_parse("::", ip), 2);
107 CHECK_MEM(ip, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16); 107 CHECK_MEM(ip, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16);
108 CHECK_INT(ip_scan("1::", ip), 3); 108 CHECK_INT(ip_parse("1::", ip), 3);
109 CHECK_MEM(ip, "\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16); 109 CHECK_MEM(ip, "\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16);
110 110
111 test_case("2.2 form 3, a dotted quad in the last two groups"); 111 test_case("2.2 form 3, a dotted quad in the last two groups");
112 CHECK_INT(ip_scan("::ffff:1.2.3.4", ip), 14); 112 CHECK_INT(ip_parse("::ffff:1.2.3.4", ip), 14);
113 CHECK_MEM(ip, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x01\x02\x03\x04", 16); 113 CHECK_MEM(ip, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x01\x02\x03\x04", 16);
114 CHECK_INT(ip_scan("1:2:3:4:5:6:1.2.3.4", ip), 19); 114 CHECK_INT(ip_parse("1:2:3:4:5:6:1.2.3.4", ip), 19);
115 CHECK_MEM(ip, "\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x01\x02\x03\x04", 16); 115 CHECK_MEM(ip, "\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x01\x02\x03\x04", 16);
116 116
117 test_case("upper case input is accepted"); 117 test_case("upper case input is accepted");
118 CHECK_INT(ip_scan("2001:DB8::1", ip), 11); 118 CHECK_INT(ip_parse("2001:DB8::1", ip), 11);
119 CHECK_MEM(ip, "\x20\x01\x0d\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", 16); 119 CHECK_MEM(ip, "\x20\x01\x0d\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", 16);
120} 120}
121 121
@@ -125,7 +125,7 @@ static void case_scan_accepts_bare_ipv4(void) {
125 /* Every address opentracker holds is sixteen bytes, so an operator writing 125 /* Every address opentracker holds is sixteen bytes, so an operator writing
126 a plain IPv4 address in the config has to land as the v4 mapped form */ 126 a plain IPv4 address in the config has to land as the v4 mapped form */
127 test_case("a bare IPv4 address is stored v4 mapped"); 127 test_case("a bare IPv4 address is stored v4 mapped");
128 CHECK_INT(ip_scan("10.0.0.1", ip), 8); 128 CHECK_INT(ip_parse("10.0.0.1", ip), 8);
129 CHECK_MEM(ip, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x0a\x00\x00\x01", 16); 129 CHECK_MEM(ip, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x0a\x00\x00\x01", 16);
130 CHECK(ip_is_v4mapped(ip)); 130 CHECK(ip_is_v4mapped(ip));
131} 131}
@@ -136,39 +136,39 @@ static void case_scan_reports_length(void) {
136 /* The config parser continues from where the address ended, which is how 136 /* The config parser continues from where the address ended, which is how
137 "address:port" and "address/bits" are read */ 137 "address:port" and "address/bits" are read */
138 test_case("scanning stops at the character after the address"); 138 test_case("scanning stops at the character after the address");
139 CHECK_INT(ip_scan("10.0.0.1:6969", ip), 8); 139 CHECK_INT(ip_parse("10.0.0.1:6969", ip), 8);
140 CHECK_INT(ip_scan("2001:db8::1/64", ip), 11); 140 CHECK_INT(ip_parse("2001:db8::1/64", ip), 11);
141 CHECK_INT(ip_scan("::1]", ip), 3); 141 CHECK_INT(ip_parse("::1]", ip), 3);
142 CHECK_INT(ip_scan("1:2:3:4:5:6:7:8 trailing", ip), 15); 142 CHECK_INT(ip_parse("1:2:3:4:5:6:7:8 trailing", ip), 15);
143} 143}
144 144
145static void case_scan_rejects(void) { 145static void case_scan_rejects(void) {
146 char ip[16]; 146 char ip[16];
147 147
148 test_case("an empty or junk string is refused"); 148 test_case("an empty or junk string is refused");
149 CHECK_INT(ip_scan("", ip), 0); 149 CHECK_INT(ip_parse("", ip), 0);
150 CHECK_INT(ip_scan("x", ip), 0); 150 CHECK_INT(ip_parse("x", ip), 0);
151 CHECK_INT(ip_scan(":", ip), 0); 151 CHECK_INT(ip_parse(":", ip), 0);
152 CHECK_INT(ip_scan(":1:2", ip), 0); 152 CHECK_INT(ip_parse(":1:2", ip), 0);
153 153
154 test_case("too few groups without :: are refused"); 154 test_case("too few groups without :: are refused");
155 CHECK_INT(ip_scan("1:2:3:4:5:6:7", ip), 0); 155 CHECK_INT(ip_parse("1:2:3:4:5:6:7", ip), 0);
156 CHECK_INT(ip_scan("2001:db8:1", ip), 0); 156 CHECK_INT(ip_parse("2001:db8:1", ip), 0);
157 157
158 /* libowfat accepted a prefix of each of these and carried on. Taking only 158 /* libowfat accepted a prefix of each of these and carried on. Taking only
159 part of a malformed address is worse than refusing it, especially where 159 part of a malformed address is worse than refusing it, especially where
160 the text comes from a config file or an X-Forwarded-For header. */ 160 the text comes from a config file or an X-Forwarded-For header. */
161 test_case("a second :: is refused rather than truncated"); 161 test_case("a second :: is refused rather than truncated");
162 CHECK_INT(ip_scan("1::2::3", ip), 0); 162 CHECK_INT(ip_parse("1::2::3", ip), 0);
163 CHECK_INT(ip_scan("::1::", ip), 0); 163 CHECK_INT(ip_parse("::1::", ip), 0);
164 CHECK_INT(ip_scan("::::", ip), 0); 164 CHECK_INT(ip_parse("::::", ip), 0);
165 165
166 test_case("more than eight groups is refused"); 166 test_case("more than eight groups is refused");
167 CHECK_INT(ip_scan("1:2:3:4:5:6:7:8:9", ip), 0); 167 CHECK_INT(ip_parse("1:2:3:4:5:6:7:8:9", ip), 0);
168 168
169 test_case("a group of more than four digits is refused, not truncated"); 169 test_case("a group of more than four digits is refused, not truncated");
170 CHECK_INT(ip_scan("12345::1", ip), 0); 170 CHECK_INT(ip_parse("12345::1", ip), 0);
171 CHECK_INT(ip_scan("fffff::1", ip), 0); 171 CHECK_INT(ip_parse("fffff::1", ip), 0);
172} 172}
173 173
174static void case_round_trip(void) { 174static void case_round_trip(void) {
@@ -187,7 +187,7 @@ static void case_round_trip(void) {
187 187
188 test_case("every formatted address scans back to the same bytes"); 188 test_case("every formatted address scans back to the same bytes");
189 for (i = 0; samples[i]; ++i) { 189 for (i = 0; samples[i]; ++i) {
190 size_t n = ip_scan(samples[i], ip); 190 size_t n = ip_parse(samples[i], ip);
191 if (n != strlen(samples[i])) { 191 if (n != strlen(samples[i])) {
192 test_failf(__FILE__, __LINE__, "\"%s\" consumed %zu of %zu", samples[i], n, strlen(samples[i])); 192 test_failf(__FILE__, __LINE__, "\"%s\" consumed %zu of %zu", samples[i], n, strlen(samples[i]));
193 return; 193 return;
@@ -197,7 +197,7 @@ static void case_round_trip(void) {
197 test_failf(__FILE__, __LINE__, "\"%s\" formatted back as \"%s\"", samples[i], text); 197 test_failf(__FILE__, __LINE__, "\"%s\" formatted back as \"%s\"", samples[i], text);
198 return; 198 return;
199 } 199 }
200 if (!ip_scan(text, again) || memcmp(ip, again, 16)) { 200 if (!ip_parse(text, again) || memcmp(ip, again, 16)) {
201 test_failf(__FILE__, __LINE__, "\"%s\" did not survive a second pass", samples[i]); 201 test_failf(__FILE__, __LINE__, "\"%s\" did not survive a second pass", samples[i]);
202 return; 202 return;
203 } 203 }
diff --git a/tests/t_scan.c b/tests/t_scan.c
index 89f1590..aab4413 100644
--- a/tests/t_scan.c
+++ b/tests/t_scan.c
@@ -15,13 +15,13 @@ static void case_plain_numbers(void) {
15 uint64_t value; 15 uint64_t value;
16 16
17 test_case("a decimal number is read and its length reported"); 17 test_case("a decimal number is read and its length reported");
18 CHECK_INT(scan_udec("0", &value, 65535), 1); 18 CHECK_INT(scan_uint("0", &value, 65535), 1);
19 CHECK_INT(value, 0); 19 CHECK_INT(value, 0);
20 CHECK_INT(scan_udec("7", &value, 65535), 1); 20 CHECK_INT(scan_uint("7", &value, 65535), 1);
21 CHECK_INT(value, 7); 21 CHECK_INT(value, 7);
22 CHECK_INT(scan_udec("6969", &value, 65535), 4); 22 CHECK_INT(scan_uint("6969", &value, 65535), 4);
23 CHECK_INT(value, 6969); 23 CHECK_INT(value, 6969);
24 CHECK_INT(scan_udec("65535", &value, 65535), 5); 24 CHECK_INT(scan_uint("65535", &value, 65535), 5);
25 CHECK_INT(value, 65535); 25 CHECK_INT(value, 65535);
26} 26}
27 27
@@ -29,11 +29,11 @@ static void case_stops_at_non_digit(void) {
29 uint64_t value; 29 uint64_t value;
30 30
31 test_case("scanning stops at the first character that is not a digit"); 31 test_case("scanning stops at the first character that is not a digit");
32 CHECK_INT(scan_udec("6969:rest", &value, 65535), 4); 32 CHECK_INT(scan_uint("6969:rest", &value, 65535), 4);
33 CHECK_INT(value, 6969); 33 CHECK_INT(value, 6969);
34 CHECK_INT(scan_udec("128/8", &value, 65535), 3); 34 CHECK_INT(scan_uint("128/8", &value, 65535), 3);
35 CHECK_INT(value, 128); 35 CHECK_INT(value, 128);
36 CHECK_INT(scan_udec("42\n", &value, 65535), 2); 36 CHECK_INT(scan_uint("42\n", &value, 65535), 2);
37 CHECK_INT(value, 42); 37 CHECK_INT(value, 42);
38} 38}
39 39
@@ -41,9 +41,9 @@ static void case_rejects_whitespace(void) {
41 uint64_t value = 0xdead; 41 uint64_t value = 0xdead;
42 42
43 test_case("leading whitespace is not skipped"); 43 test_case("leading whitespace is not skipped");
44 CHECK_INT(scan_udec(" 1", &value, 65535), 0); 44 CHECK_INT(scan_uint(" 1", &value, 65535), 0);
45 CHECK_INT(scan_udec("\t1", &value, 65535), 0); 45 CHECK_INT(scan_uint("\t1", &value, 65535), 0);
46 CHECK_INT(scan_udec("\n1", &value, 65535), 0); 46 CHECK_INT(scan_uint("\n1", &value, 65535), 0);
47 CHECK_INT(value, 0xdead); 47 CHECK_INT(value, 0xdead);
48} 48}
49 49
@@ -51,9 +51,9 @@ static void case_rejects_sign(void) {
51 uint64_t value = 0xdead; 51 uint64_t value = 0xdead;
52 52
53 test_case("a sign is not accepted"); 53 test_case("a sign is not accepted");
54 CHECK_INT(scan_udec("+1", &value, 65535), 0); 54 CHECK_INT(scan_uint("+1", &value, 65535), 0);
55 CHECK_INT(scan_udec("-1", &value, 65535), 0); 55 CHECK_INT(scan_uint("-1", &value, 65535), 0);
56 CHECK_INT(scan_udec("-0", &value, 65535), 0); 56 CHECK_INT(scan_uint("-0", &value, 65535), 0);
57 CHECK_INT(value, 0xdead); 57 CHECK_INT(value, 0xdead);
58} 58}
59 59
@@ -61,9 +61,9 @@ static void case_rejects_empty_and_junk(void) {
61 uint64_t value = 0xdead; 61 uint64_t value = 0xdead;
62 62
63 test_case("a string with no leading digit yields nothing"); 63 test_case("a string with no leading digit yields nothing");
64 CHECK_INT(scan_udec("", &value, 65535), 0); 64 CHECK_INT(scan_uint("", &value, 65535), 0);
65 CHECK_INT(scan_udec("x", &value, 65535), 0); 65 CHECK_INT(scan_uint("x", &value, 65535), 0);
66 CHECK_INT(scan_udec(":80", &value, 65535), 0); 66 CHECK_INT(scan_uint(":80", &value, 65535), 0);
67 CHECK_INT(value, 0xdead); 67 CHECK_INT(value, 0xdead);
68} 68}
69 69
@@ -71,9 +71,9 @@ static void case_no_alternative_bases(void) {
71 uint64_t value; 71 uint64_t value;
72 72
73 test_case("hexadecimal and octal notations are read as plain decimal"); 73 test_case("hexadecimal and octal notations are read as plain decimal");
74 CHECK_INT(scan_udec("0x10", &value, 65535), 1); 74 CHECK_INT(scan_uint("0x10", &value, 65535), 1);
75 CHECK_INT(value, 0); 75 CHECK_INT(value, 0);
76 CHECK_INT(scan_udec("010", &value, 65535), 3); 76 CHECK_INT(scan_uint("010", &value, 65535), 3);
77 CHECK_INT(value, 10); 77 CHECK_INT(value, 10);
78} 78}
79 79
@@ -81,13 +81,13 @@ static void case_refuses_out_of_range(void) {
81 uint64_t value = 0xdead; 81 uint64_t value = 0xdead;
82 82
83 test_case("a value above max is refused, not truncated"); 83 test_case("a value above max is refused, not truncated");
84 CHECK_INT(scan_udec("65536", &value, 65535), 0); 84 CHECK_INT(scan_uint("65536", &value, 65535), 0);
85 CHECK_INT(scan_udec("99999", &value, 65535), 0); 85 CHECK_INT(scan_uint("99999", &value, 65535), 0);
86 CHECK_INT(scan_udec("129", &value, 128), 0); 86 CHECK_INT(scan_uint("129", &value, 128), 0);
87 CHECK_INT(value, 0xdead); 87 CHECK_INT(value, 0xdead);
88 88
89 test_case("the boundary itself is accepted"); 89 test_case("the boundary itself is accepted");
90 CHECK_INT(scan_udec("128", &value, 128), 3); 90 CHECK_INT(scan_uint("128", &value, 128), 3);
91 CHECK_INT(value, 128); 91 CHECK_INT(value, 128);
92} 92}
93 93
@@ -95,21 +95,21 @@ static void case_sixty_four_bit_range(void) {
95 uint64_t value; 95 uint64_t value;
96 96
97 test_case("the full unsigned 64 bit range is available"); 97 test_case("the full unsigned 64 bit range is available");
98 CHECK_INT(scan_udec("18446744073709551615", &value, UINT64_MAX), 20); 98 CHECK_INT(scan_uint("18446744073709551615", &value, UINT64_MAX), 20);
99 CHECK(value == UINT64_MAX); 99 CHECK(value == UINT64_MAX);
100 100
101 test_case("a number too large for 64 bits is refused"); 101 test_case("a number too large for 64 bits is refused");
102 CHECK_INT(scan_udec("18446744073709551616", &value, UINT64_MAX), 0); 102 CHECK_INT(scan_uint("18446744073709551616", &value, UINT64_MAX), 0);
103 CHECK_INT(scan_udec("99999999999999999999999999", &value, UINT64_MAX), 0); 103 CHECK_INT(scan_uint("99999999999999999999999999", &value, UINT64_MAX), 0);
104} 104}
105 105
106static void case_leading_zeros_allowed(void) { 106static void case_leading_zeros_allowed(void) {
107 uint64_t value; 107 uint64_t value;
108 108
109 test_case("leading zeros are accepted outside bencode"); 109 test_case("leading zeros are accepted outside bencode");
110 CHECK_INT(scan_udec("007", &value, 65535), 3); 110 CHECK_INT(scan_uint("007", &value, 65535), 3);
111 CHECK_INT(value, 7); 111 CHECK_INT(value, 7);
112 CHECK_INT(scan_udec("0000", &value, 65535), 4); 112 CHECK_INT(scan_uint("0000", &value, 65535), 4);
113 CHECK_INT(value, 0); 113 CHECK_INT(value, 0);
114} 114}
115 115
@@ -117,9 +117,9 @@ static void case_bencode_rejects_leading_zeros(void) {
117 uint64_t value = 0xdead; 117 uint64_t value = 0xdead;
118 118
119 test_case("bencode refuses a leading zero on anything but zero"); 119 test_case("bencode refuses a leading zero on anything but zero");
120 CHECK_INT(scan_udec_bencode("007", &value, 65535), 0); 120 CHECK_INT(scan_uint_bencode("007", &value, 65535), 0);
121 CHECK_INT(scan_udec_bencode("0000", &value, 65535), 0); 121 CHECK_INT(scan_uint_bencode("0000", &value, 65535), 0);
122 CHECK_INT(scan_udec_bencode("01", &value, 65535), 0); 122 CHECK_INT(scan_uint_bencode("01", &value, 65535), 0);
123 CHECK_INT(value, 0xdead); 123 CHECK_INT(value, 0xdead);
124} 124}
125 125
@@ -127,11 +127,11 @@ static void case_bencode_accepts_canonical(void) {
127 uint64_t value; 127 uint64_t value;
128 128
129 test_case("bencode accepts a canonically written number"); 129 test_case("bencode accepts a canonically written number");
130 CHECK_INT(scan_udec_bencode("0", &value, 65535), 1); 130 CHECK_INT(scan_uint_bencode("0", &value, 65535), 1);
131 CHECK_INT(value, 0); 131 CHECK_INT(value, 0);
132 CHECK_INT(scan_udec_bencode("0:", &value, 65535), 1); 132 CHECK_INT(scan_uint_bencode("0:", &value, 65535), 1);
133 CHECK_INT(value, 0); 133 CHECK_INT(value, 0);
134 CHECK_INT(scan_udec_bencode("160:", &value, 65535), 3); 134 CHECK_INT(scan_uint_bencode("160:", &value, 65535), 3);
135 CHECK_INT(value, 160); 135 CHECK_INT(value, 160);
136} 136}
137 137
@@ -139,14 +139,64 @@ static void case_bencode_inherits_the_rest(void) {
139 uint64_t value = 0xdead; 139 uint64_t value = 0xdead;
140 140
141 test_case("bencode is no laxer than the plain scanner"); 141 test_case("bencode is no laxer than the plain scanner");
142 CHECK_INT(scan_udec_bencode(" 1", &value, 65535), 0); 142 CHECK_INT(scan_uint_bencode(" 1", &value, 65535), 0);
143 CHECK_INT(scan_udec_bencode("-1", &value, 65535), 0); 143 CHECK_INT(scan_uint_bencode("-1", &value, 65535), 0);
144 CHECK_INT(scan_udec_bencode("+1", &value, 65535), 0); 144 CHECK_INT(scan_uint_bencode("+1", &value, 65535), 0);
145 CHECK_INT(scan_udec_bencode("65536", &value, 65535), 0); 145 CHECK_INT(scan_uint_bencode("65536", &value, 65535), 0);
146 CHECK_INT(value, 0xdead); 146 CHECK_INT(value, 0xdead);
147} 147}
148 148
149/* ------------------------------------------- fixed width signed integers */
150
151/* A value lifted out of a query string arrives as a pointer and a length
152 with nothing terminating it, and the whole of it has to be the number. */
153
154static void case_int_whole_field(void) {
155 int64_t value = 0;
156
157 test_case("the whole field is read, sign and all");
158 CHECK_INT(scan_int("123", 3, &value), 0);
159 CHECK_INT(value, 123);
160 CHECK_INT(scan_int("-45", 3, &value), 0);
161 CHECK_INT(value, -45);
162
163 test_case("and only as far as the length given");
164 CHECK_INT(scan_int("12345", 3, &value), 0);
165 CHECK_INT(value, 123);
166}
167
168static void case_int_rejects_the_rest(void) {
169 int64_t value = 4711;
170
171 test_case("anything that is not all digits is refused");
172 CHECK_INT(scan_int("12a", 3, &value), -1);
173 CHECK_INT(scan_int("", 0, &value), -1);
174 CHECK_INT(scan_int("-", 1, &value), -1);
175 CHECK_INT(scan_int(" 12", 3, &value), -1);
176 CHECK_INT(scan_int("+12", 3, &value), -1);
177
178 test_case("and a refused field leaves the number alone");
179 CHECK_INT(value, 4711);
180}
181
182static void case_int_overflow(void) {
183 int64_t value = 0;
184
185 test_case("a field too long to fit is refused rather than wrapped");
186 CHECK_INT(scan_int("9223372036854775807", 19, &value), 0);
187 CHECK_INT(value, INT64_MAX);
188 CHECK_INT(scan_int("9223372036854775808", 19, &value), -1);
189 CHECK_INT(scan_int("99999999999999999999", 20, &value), -1);
190
191 test_case("including the negative side");
192 CHECK_INT(scan_int("-9223372036854775807", 20, &value), 0);
193 CHECK_INT(value, -INT64_MAX);
194}
195
149void test_scan(void) { 196void test_scan(void) {
197 case_int_whole_field();
198 case_int_rejects_the_rest();
199 case_int_overflow();
150 case_plain_numbers(); 200 case_plain_numbers();
151 case_stops_at_non_digit(); 201 case_stops_at_non_digit();
152 case_rejects_whitespace(); 202 case_rejects_whitespace();
diff --git a/tests/t_urlencoded.c b/tests/t_urlencoded.c
index 1239e6b..6e0ee31 100644
--- a/tests/t_urlencoded.c
+++ b/tests/t_urlencoded.c
@@ -228,33 +228,6 @@ static void case_find_keywords(void) {
228 CHECK_INT(urlencoded_match_keyword(keywords, &p, SCAN_SEARCHPATH_PARAM), -3); 228 CHECK_INT(urlencoded_match_keyword(keywords, &p, SCAN_SEARCHPATH_PARAM), -3);
229} 229}
230 230
231static void case_fixed_int(void) {
232 char positive[] = "123";
233 char negative[] = "-45";
234 char trailing[] = "12a";
235 int value;
236
237 test_case("scan_fixed_int returns the count of unparsed bytes");
238 CHECK_INT(scan_fixed_int(positive, 3, &value), 0);
239 CHECK_INT(value, 123);
240
241 CHECK_INT(scan_fixed_int(negative, 3, &value), 0);
242 CHECK_INT(value, -45);
243
244 CHECK_INT(scan_fixed_int(trailing, 3, &value), 1);
245 CHECK_INT(value, 12);
246}
247
248static void case_fixed_int_bounded(void) {
249 char digits[] = "12345";
250 int value;
251
252 test_case("scan_fixed_int honours the length it is given");
253 CHECK_INT(scan_fixed_int(digits, 3, &value), 0);
254 CHECK_INT(value, 123);
255}
256
257
258/* The encoding direction. Which bytes survive unescaped is fixed by what 231/* The encoding direction. Which bytes survive unescaped is fixed by what
259 existing parsers of the urlencoded full scrape expect, so these cases pin 232 existing parsers of the urlencoded full scrape expect, so these cases pin
260 the set rather than describe a preference. */ 233 the set rather than describe a preference. */
@@ -361,8 +334,6 @@ void test_urlencoded(void) {
361 case_raw_high_byte(); 334 case_raw_high_byte();
362 case_skipvalue(); 335 case_skipvalue();
363 case_find_keywords(); 336 case_find_keywords();
364 case_fixed_int();
365 case_fixed_int_bounded();
366 case_fmt_passes_safe_bytes(); 337 case_fmt_passes_safe_bytes();
367 case_fmt_escapes_the_rest(); 338 case_fmt_escapes_the_rest();
368 case_fmt_hex_is_lower_case(); 339 case_fmt_hex_is_lower_case();