ldns: Update vendor branch from 1.6.16 to 1.7.0
[dragonfly.git] / contrib / ldns / rdata.c
1 /*
2  * rdata.c
3  *
4  * rdata implementation
5  *
6  * a Net::DNS like library for C
7  *
8  * (c) NLnet Labs, 2004-2006
9  *
10  * See the file LICENSE for the license
11  */
12
13 #include <ldns/config.h>
14
15 #include <ldns/ldns.h>
16
17 /*
18  * Access functions 
19  * do this as functions to get type checking
20  */
21
22 /* read */
23 size_t
24 ldns_rdf_size(const ldns_rdf *rd)
25 {
26         assert(rd != NULL);
27         return rd->_size;
28 }
29
30 ldns_rdf_type
31 ldns_rdf_get_type(const ldns_rdf *rd)
32 {
33         assert(rd != NULL);
34         return rd->_type;
35 }
36
37 uint8_t *
38 ldns_rdf_data(const ldns_rdf *rd)
39 {
40         assert(rd != NULL);
41         return rd->_data;
42 }
43
44 /* write */
45 void
46 ldns_rdf_set_size(ldns_rdf *rd, size_t size)
47 {
48         assert(rd != NULL);
49         rd->_size = size;
50 }
51
52 void
53 ldns_rdf_set_type(ldns_rdf *rd, ldns_rdf_type type)
54 {
55         assert(rd != NULL);
56         rd->_type = type;
57 }
58
59 void
60 ldns_rdf_set_data(ldns_rdf *rd, void *data)
61 {
62         /* only copy the pointer */
63         assert(rd != NULL);
64         rd->_data = data;
65 }
66
67 /* for types that allow it, return
68  * the native/host order type */
69 uint8_t
70 ldns_rdf2native_int8(const ldns_rdf *rd)
71 {
72         uint8_t data;
73
74         /* only allow 8 bit rdfs */
75         if (ldns_rdf_size(rd) != LDNS_RDF_SIZE_BYTE) {
76                 return 0;
77         }
78         
79         memcpy(&data, ldns_rdf_data(rd), sizeof(data));
80         return data;
81 }
82
83 uint16_t
84 ldns_rdf2native_int16(const ldns_rdf *rd)
85 {
86         uint16_t data;
87
88         /* only allow 16 bit rdfs */
89         if (ldns_rdf_size(rd) != LDNS_RDF_SIZE_WORD) {
90                 return 0;
91         }
92         
93         memcpy(&data, ldns_rdf_data(rd), sizeof(data));
94         return ntohs(data);
95 }
96
97 uint32_t
98 ldns_rdf2native_int32(const ldns_rdf *rd)
99 {
100         uint32_t data;
101
102         /* only allow 32 bit rdfs */
103         if (ldns_rdf_size(rd) != LDNS_RDF_SIZE_DOUBLEWORD) {
104                 return 0;
105         }
106         
107         memcpy(&data, ldns_rdf_data(rd), sizeof(data));
108         return ntohl(data);
109 }
110
111 time_t
112 ldns_rdf2native_time_t(const ldns_rdf *rd)
113 {
114         uint32_t data;
115
116         /* only allow 32 bit rdfs */
117         if (ldns_rdf_size(rd) != LDNS_RDF_SIZE_DOUBLEWORD ||
118                         ldns_rdf_get_type(rd) != LDNS_RDF_TYPE_TIME) {
119                 return 0;
120         }
121         memcpy(&data, ldns_rdf_data(rd), sizeof(data));
122         return (time_t)ntohl(data);
123 }
124
125 ldns_rdf *
126 ldns_native2rdf_int8(ldns_rdf_type type, uint8_t value)
127 {
128         return ldns_rdf_new_frm_data(type, LDNS_RDF_SIZE_BYTE, &value);
129 }
130
131 ldns_rdf *
132 ldns_native2rdf_int16(ldns_rdf_type type, uint16_t value)
133 {
134         uint16_t *rdf_data = LDNS_XMALLOC(uint16_t, 1);
135         ldns_rdf* rdf;
136         if (!rdf_data) {
137                 return NULL;
138         }
139         ldns_write_uint16(rdf_data, value);
140         rdf = ldns_rdf_new(type, LDNS_RDF_SIZE_WORD, rdf_data);
141         if(!rdf)
142                 LDNS_FREE(rdf_data);
143         return rdf;
144 }
145
146 ldns_rdf *
147 ldns_native2rdf_int32(ldns_rdf_type type, uint32_t value)
148 {
149         uint32_t *rdf_data = LDNS_XMALLOC(uint32_t, 1);
150         ldns_rdf* rdf;
151         if (!rdf_data) {
152                 return NULL;
153         }
154         ldns_write_uint32(rdf_data, value);
155         rdf = ldns_rdf_new(type, LDNS_RDF_SIZE_DOUBLEWORD, rdf_data);
156         if(!rdf)
157                 LDNS_FREE(rdf_data);
158         return rdf;
159 }
160
161 ldns_rdf *
162 ldns_native2rdf_int16_data(size_t size, uint8_t *data)
163 {
164         uint8_t *rdf_data = LDNS_XMALLOC(uint8_t, size + 2);
165         ldns_rdf* rdf;
166         if (!rdf_data) {
167                 return NULL;
168         }
169         ldns_write_uint16(rdf_data, size);
170         memcpy(rdf_data + 2, data, size);
171         rdf = ldns_rdf_new(LDNS_RDF_TYPE_INT16_DATA, size + 2, rdf_data);
172         if(!rdf)
173                 LDNS_FREE(rdf_data);
174         return rdf;
175 }
176
177 /* note: data must be allocated memory */
178 ldns_rdf *
179 ldns_rdf_new(ldns_rdf_type type, size_t size, void *data)
180 {
181         ldns_rdf *rd;
182         rd = LDNS_MALLOC(ldns_rdf);
183         if (!rd) {
184                 return NULL;
185         }
186         ldns_rdf_set_size(rd, size);
187         ldns_rdf_set_type(rd, type);
188         ldns_rdf_set_data(rd, data);
189         return rd;
190 }
191
192 ldns_rdf *
193 ldns_rdf_new_frm_data(ldns_rdf_type type, size_t size, const void *data)
194 {
195         ldns_rdf *rdf;
196
197         /* if the size is too big, fail */
198         if (size > LDNS_MAX_RDFLEN) {
199                 return NULL;
200         }
201
202         /* allocate space */
203         rdf = LDNS_MALLOC(ldns_rdf);
204         if (!rdf) {
205                 return NULL;
206         }
207         rdf->_data = LDNS_XMALLOC(uint8_t, size);
208         if (!rdf->_data) {
209                 LDNS_FREE(rdf);
210                 return NULL;
211         }
212         
213         /* set the values */
214         ldns_rdf_set_type(rdf, type);
215         ldns_rdf_set_size(rdf, size);
216         memcpy(rdf->_data, data, size);
217
218         return rdf;
219 }
220
221 ldns_rdf *
222 ldns_rdf_clone(const ldns_rdf *rd)
223 {
224         assert(rd != NULL);
225         return (ldns_rdf_new_frm_data( ldns_rdf_get_type(rd),
226                 ldns_rdf_size(rd), ldns_rdf_data(rd)));
227 }
228
229 void
230 ldns_rdf_deep_free(ldns_rdf *rd)
231 {
232         if (rd) {
233                 if (rd->_data) {
234                         LDNS_FREE(rd->_data);
235                 }
236                 LDNS_FREE(rd);
237         }
238 }
239
240 void 
241 ldns_rdf_free(ldns_rdf *rd)
242 {
243         if (rd) {
244                 LDNS_FREE(rd);
245         }
246 }
247
248 ldns_rdf *
249 ldns_rdf_new_frm_str(ldns_rdf_type type, const char *str)
250 {
251         ldns_rdf *rdf = NULL;
252         ldns_status status;
253
254         switch (type) {
255         case LDNS_RDF_TYPE_DNAME:
256                 status = ldns_str2rdf_dname(&rdf, str);
257                 break;
258         case LDNS_RDF_TYPE_INT8:
259                 status = ldns_str2rdf_int8(&rdf, str);
260                 break;
261         case LDNS_RDF_TYPE_INT16:
262                 status = ldns_str2rdf_int16(&rdf, str);
263                 break;
264         case LDNS_RDF_TYPE_INT32:
265                 status = ldns_str2rdf_int32(&rdf, str);
266                 break;
267         case LDNS_RDF_TYPE_A:
268                 status = ldns_str2rdf_a(&rdf, str);
269                 break;
270         case LDNS_RDF_TYPE_AAAA:
271                 status = ldns_str2rdf_aaaa(&rdf, str);
272                 break;
273         case LDNS_RDF_TYPE_STR:
274                 status = ldns_str2rdf_str(&rdf, str);
275                 break;
276         case LDNS_RDF_TYPE_APL:
277                 status = ldns_str2rdf_apl(&rdf, str);
278                 break;
279         case LDNS_RDF_TYPE_B64:
280                 status = ldns_str2rdf_b64(&rdf, str);
281                 break;
282         case LDNS_RDF_TYPE_B32_EXT:
283                 status = ldns_str2rdf_b32_ext(&rdf, str);
284                 break;
285         case LDNS_RDF_TYPE_HEX:
286                 status = ldns_str2rdf_hex(&rdf, str);
287                 break;
288         case LDNS_RDF_TYPE_NSEC:
289                 status = ldns_str2rdf_nsec(&rdf, str);
290                 break;
291         case LDNS_RDF_TYPE_TYPE:
292                 status = ldns_str2rdf_type(&rdf, str);
293                 break;
294         case LDNS_RDF_TYPE_CLASS:
295                 status = ldns_str2rdf_class(&rdf, str);
296                 break;
297         case LDNS_RDF_TYPE_CERT_ALG:
298                 status = ldns_str2rdf_cert_alg(&rdf, str);
299                 break;
300         case LDNS_RDF_TYPE_ALG:
301                 status = ldns_str2rdf_alg(&rdf, str);
302                 break;
303         case LDNS_RDF_TYPE_UNKNOWN:
304                 status = ldns_str2rdf_unknown(&rdf, str);
305                 break;
306         case LDNS_RDF_TYPE_TIME:
307                 status = ldns_str2rdf_time(&rdf, str);
308                 break;
309         case LDNS_RDF_TYPE_PERIOD:
310                 status = ldns_str2rdf_period(&rdf, str);
311                 break;
312         case LDNS_RDF_TYPE_HIP:
313                 status = ldns_str2rdf_hip(&rdf, str);
314                 break;
315         case LDNS_RDF_TYPE_SERVICE:
316                 status = ldns_str2rdf_service(&rdf, str);
317                 break;
318         case LDNS_RDF_TYPE_LOC:
319                 status = ldns_str2rdf_loc(&rdf, str);
320                 break;
321         case LDNS_RDF_TYPE_WKS:
322                 status = ldns_str2rdf_wks(&rdf, str);
323                 break;
324         case LDNS_RDF_TYPE_NSAP:
325                 status = ldns_str2rdf_nsap(&rdf, str);
326                 break;
327         case LDNS_RDF_TYPE_ATMA:
328                 status = ldns_str2rdf_atma(&rdf, str);
329                 break;
330         case LDNS_RDF_TYPE_IPSECKEY:
331                 status = ldns_str2rdf_ipseckey(&rdf, str);
332                 break;
333         case LDNS_RDF_TYPE_NSEC3_SALT:
334                 status = ldns_str2rdf_nsec3_salt(&rdf, str);
335                 break;
336         case LDNS_RDF_TYPE_NSEC3_NEXT_OWNER:
337                 status = ldns_str2rdf_b32_ext(&rdf, str);
338                 break;
339         case LDNS_RDF_TYPE_ILNP64:
340                 status = ldns_str2rdf_ilnp64(&rdf, str);
341                 break;
342         case LDNS_RDF_TYPE_EUI48:
343                 status = ldns_str2rdf_eui48(&rdf, str);
344                 break;
345         case LDNS_RDF_TYPE_EUI64:
346                 status = ldns_str2rdf_eui64(&rdf, str);
347                 break;
348         case LDNS_RDF_TYPE_TAG:
349                 status = ldns_str2rdf_tag(&rdf, str);
350                 break;
351         case LDNS_RDF_TYPE_LONG_STR:
352                 status = ldns_str2rdf_long_str(&rdf, str);
353                 break;
354         case LDNS_RDF_TYPE_CERTIFICATE_USAGE:
355                 status = ldns_str2rdf_certificate_usage(&rdf, str);
356                 break;
357         case LDNS_RDF_TYPE_SELECTOR:
358                 status = ldns_str2rdf_selector(&rdf, str);
359                 break;
360         case LDNS_RDF_TYPE_MATCHING_TYPE:
361                 status = ldns_str2rdf_matching_type(&rdf, str);
362                 break;
363         case LDNS_RDF_TYPE_NONE:
364         default:
365                 /* default default ??? */
366                 status = LDNS_STATUS_ERR;
367                 break;
368         }
369         if (LDNS_STATUS_OK == status) {
370                 ldns_rdf_set_type(rdf, type);
371                 return rdf;
372         }
373         if (rdf) {
374                 LDNS_FREE(rdf);
375         }
376         return NULL;
377 }
378
379 ldns_status
380 ldns_rdf_new_frm_fp(ldns_rdf **rdf, ldns_rdf_type type, FILE *fp)
381 {
382         return ldns_rdf_new_frm_fp_l(rdf, type, fp, NULL);
383 }
384
385 ldns_status
386 ldns_rdf_new_frm_fp_l(ldns_rdf **rdf, ldns_rdf_type type, FILE *fp, int *line_nr)
387 {
388         char *line;
389         ldns_rdf *r;
390         ssize_t t;
391
392         line = LDNS_XMALLOC(char, LDNS_MAX_LINELEN + 1);
393         if (!line) {
394                 return LDNS_STATUS_MEM_ERR;
395         }
396
397         /* read an entire line in from the file */
398         if ((t = ldns_fget_token_l(fp, line, LDNS_PARSE_SKIP_SPACE, 0, line_nr)) == -1 || t == 0) {
399                 LDNS_FREE(line);
400                 return LDNS_STATUS_SYNTAX_RDATA_ERR;
401         }
402         r =  ldns_rdf_new_frm_str(type, (const char*) line);
403         LDNS_FREE(line);
404         if (rdf) {
405                 *rdf = r;
406                 return LDNS_STATUS_OK;
407         } else {
408                 return LDNS_STATUS_NULL;
409         }
410 }
411
412 ldns_rdf *
413 ldns_rdf_address_reverse(const ldns_rdf *rd)
414 {
415         uint8_t buf_4[LDNS_IP4ADDRLEN];
416         uint8_t buf_6[LDNS_IP6ADDRLEN * 2];
417         ldns_rdf *rev;
418         ldns_rdf *in_addr;
419         ldns_rdf *ret_dname;
420         uint8_t octet;
421         uint8_t nnibble;
422         uint8_t nibble;
423         uint8_t i, j;
424
425         char *char_dname;
426         int nbit;
427
428         if (ldns_rdf_get_type(rd) != LDNS_RDF_TYPE_A &&
429                         ldns_rdf_get_type(rd) != LDNS_RDF_TYPE_AAAA) {
430                 return NULL;
431         }
432
433         in_addr = NULL;
434         ret_dname = NULL;
435
436         switch(ldns_rdf_get_type(rd)) {
437                 case LDNS_RDF_TYPE_A:
438                         /* the length of the buffer is 4 */
439                         buf_4[3] = ldns_rdf_data(rd)[0];
440                         buf_4[2] = ldns_rdf_data(rd)[1];
441                         buf_4[1] = ldns_rdf_data(rd)[2];
442                         buf_4[0] = ldns_rdf_data(rd)[3];
443                         in_addr = ldns_dname_new_frm_str("in-addr.arpa.");
444                         if (!in_addr) {
445                                 return NULL;
446                         }
447                         /* make a new rdf and convert that back  */
448                         rev = ldns_rdf_new_frm_data( LDNS_RDF_TYPE_A,
449                                 LDNS_IP4ADDRLEN, (void*)&buf_4);
450                         if (!rev) {
451                                 LDNS_FREE(in_addr);
452                                 return NULL;
453                         }
454
455                         /* convert rev to a string */
456                         char_dname = ldns_rdf2str(rev);
457                         if (!char_dname) {
458                                 LDNS_FREE(in_addr);
459                                 ldns_rdf_deep_free(rev);
460                                 return NULL;
461                         }
462                         /* transform back to rdf with type dname */
463                         ret_dname = ldns_dname_new_frm_str(char_dname);
464                         if (!ret_dname) {
465                                 LDNS_FREE(in_addr);
466                                 ldns_rdf_deep_free(rev);
467                                 LDNS_FREE(char_dname);
468                                 return NULL;
469                         }
470                         /* not needed anymore */
471                         ldns_rdf_deep_free(rev);
472                         LDNS_FREE(char_dname);
473                         break;
474                 case LDNS_RDF_TYPE_AAAA:
475                         /* some foo magic to reverse the nibbles ... */
476
477                         for (nbit = 127; nbit >= 0; nbit = nbit - 4) {
478                                 /* calculate octet (8 bit) */
479                                 octet = ( ((unsigned int) nbit) & 0x78) >> 3;
480                                 /* calculate nibble */
481                                 nnibble = ( ((unsigned int) nbit) & 0x04) >> 2;
482                                 /* extract nibble */
483                                 nibble = (ldns_rdf_data(rd)[octet] & ( 0xf << (4 * (1 -
484                                                  nnibble)) ) ) >> ( 4 * (1 - 
485                                                 nnibble));
486
487                                 buf_6[(LDNS_IP6ADDRLEN * 2 - 1) -
488                                         (octet * 2 + nnibble)] = 
489                                                 (uint8_t)ldns_int_to_hexdigit((int)nibble);
490                         }
491
492                         char_dname = LDNS_XMALLOC(char, (LDNS_IP6ADDRLEN * 4));
493                         if (!char_dname) {
494                                 return NULL;
495                         }
496                         char_dname[LDNS_IP6ADDRLEN * 4 - 1] = '\0'; /* closure */
497
498                         /* walk the string and add . 's */
499                         for (i = 0, j = 0; i < LDNS_IP6ADDRLEN * 2; i++, j = j + 2) {
500                                 char_dname[j] = (char)buf_6[i];
501                                 if (i != LDNS_IP6ADDRLEN * 2 - 1) {
502                                         char_dname[j + 1] = '.';
503                                 }
504                         }
505                         in_addr = ldns_dname_new_frm_str("ip6.arpa.");
506                         if (!in_addr) {
507                                 LDNS_FREE(char_dname);
508                                 return NULL;
509                         }
510
511                         /* convert rev to a string */
512                         ret_dname = ldns_dname_new_frm_str(char_dname);
513                         LDNS_FREE(char_dname);
514                         if (!ret_dname) {
515                                 ldns_rdf_deep_free(in_addr);
516                                 return NULL;
517                         }
518                         break;
519                 default:
520                         break;
521         }
522         /* add the suffix */
523         rev = ldns_dname_cat_clone(ret_dname, in_addr);
524
525         ldns_rdf_deep_free(ret_dname);
526         ldns_rdf_deep_free(in_addr);
527         return rev;
528 }
529
530 ldns_status
531 ldns_rdf_hip_get_alg_hit_pk(ldns_rdf *rdf, uint8_t* alg,
532                             uint8_t *hit_size, uint8_t** hit,
533                             uint16_t *pk_size, uint8_t** pk)
534 {
535         uint8_t *data;
536         size_t rdf_size;
537
538         if (! rdf || ! alg || ! hit || ! hit_size || ! pk || ! pk_size) {
539                 return LDNS_STATUS_INVALID_POINTER;
540         } else if (ldns_rdf_get_type(rdf) != LDNS_RDF_TYPE_HIP) {
541                 return LDNS_STATUS_INVALID_RDF_TYPE;
542         } else if ((rdf_size = ldns_rdf_size(rdf)) < 6) {
543                 return LDNS_STATUS_WIRE_RDATA_ERR;
544         }
545         data = ldns_rdf_data(rdf);
546         *hit_size = data[0];
547         *alg      = data[1];
548         *pk_size  = ldns_read_uint16(data + 2);
549         *hit      = data + 4;
550         *pk       = data + 4 + *hit_size;
551         if (*hit_size == 0 || *pk_size == 0 ||
552                         rdf_size < (size_t) *hit_size + *pk_size + 4) {
553                 return LDNS_STATUS_WIRE_RDATA_ERR;
554         }
555         return LDNS_STATUS_OK;
556 }
557
558 ldns_status
559 ldns_rdf_hip_new_frm_alg_hit_pk(ldns_rdf** rdf, uint8_t alg,
560                                 uint8_t hit_size, uint8_t *hit,
561                                 uint16_t pk_size, uint8_t *pk)
562 {
563         uint8_t *data;
564
565         if (! rdf) {
566                 return LDNS_STATUS_INVALID_POINTER;
567         }
568         if (4 + hit_size + pk_size > LDNS_MAX_RDFLEN) {
569                 return LDNS_STATUS_RDATA_OVERFLOW;
570         }
571         data = LDNS_XMALLOC(uint8_t, 4 + hit_size + pk_size);
572         if (data == NULL) {
573                 return LDNS_STATUS_MEM_ERR;
574         }
575         data[0] = hit_size;
576         data[1] = alg;
577         ldns_write_uint16(data + 2, pk_size);
578         memcpy(data + 4, hit, hit_size);
579         memcpy(data + 4 + hit_size, pk, pk_size);
580         *rdf = ldns_rdf_new(LDNS_RDF_TYPE_HIP, 4 + hit_size + pk_size, data);
581         if (! *rdf) {
582                 LDNS_FREE(data);
583                 return LDNS_STATUS_MEM_ERR;
584         }
585         return LDNS_STATUS_OK;
586 }
587
588 ldns_status
589 ldns_octet(char *word, size_t *length)
590 {
591     char *s; 
592     char *p;
593     *length = 0;
594
595     for (s = p = word; *s != '\0'; s++,p++) {
596         switch (*s) {
597             case '.':
598                 if (s[1] == '.') {
599                     return LDNS_STATUS_EMPTY_LABEL;
600                 }
601                 *p = *s;
602                 (*length)++;
603                 break;
604             case '\\':
605                 if ('0' <= s[1] && s[1] <= '9' &&
606                     '0' <= s[2] && s[2] <= '9' &&
607                     '0' <= s[3] && s[3] <= '9') {
608                     /* \DDD seen */
609                     int val = ((s[1] - '0') * 100 +
610                            (s[2] - '0') * 10 + (s[3] - '0'));
611
612                     if (0 <= val && val <= 255) {
613                         /* this also handles \0 */
614                         s += 3;
615                         *p = val;
616                         (*length)++;
617                     } else {
618                         return LDNS_STATUS_DDD_OVERFLOW;
619                     }
620                 } else {
621                     /* an espaced character, like \<space> ? 
622                     * remove the '\' keep the rest */
623                     *p = *++s;
624                     (*length)++;
625                 }
626                 break;
627             case '\"':
628                 /* non quoted " Is either first or the last character in
629                  * the string */
630
631                 *p = *++s; /* skip it */
632                 (*length)++;
633                 /* I'm not sure if this is needed in libdns... MG */
634                 if ( *s == '\0' ) {
635                     /* ok, it was the last one */
636                     *p  = '\0'; 
637                     return LDNS_STATUS_OK;
638                 }
639                 break;
640             default:
641                 *p = *s;
642                 (*length)++;
643                 break;
644         }
645     }
646     *p = '\0';
647     return LDNS_STATUS_OK;
648 }
649
650 int
651 ldns_rdf_compare(const ldns_rdf *rd1, const ldns_rdf *rd2)
652 {
653         uint16_t i1, i2, i;
654         uint8_t *d1, *d2;
655
656         /* only when both are not NULL we can say anything about them */
657         if (!rd1 && !rd2) {
658                 return 0;
659         }
660         if (!rd1 || !rd2) {
661                 return -1;
662         }
663         i1 = ldns_rdf_size(rd1);
664         i2 = ldns_rdf_size(rd2);
665
666         if (i1 < i2) {
667                 return -1;
668         } else if (i1 > i2) {
669                 return +1;
670         } else {
671                 d1 = (uint8_t*)ldns_rdf_data(rd1);
672                 d2 = (uint8_t*)ldns_rdf_data(rd2);
673                 for(i = 0; i < i1; i++) {
674                         if (d1[i] < d2[i]) {
675                                 return -1;
676                         } else if (d1[i] > d2[i]) {
677                                 return +1;
678                         }
679                 }
680         }
681         return 0;
682 }
683
684 uint32_t
685 ldns_str2period(const char *nptr, const char **endptr)
686 {
687         int sign = 0;
688         uint32_t i = 0;
689         uint32_t seconds = 0;
690
691         for(*endptr = nptr; **endptr; (*endptr)++) {
692                 switch (**endptr) {
693                         case ' ':
694                         case '\t':
695                                 break;
696                         case '-':
697                                 if(sign == 0) {
698                                         sign = -1;
699                                 } else {
700                                         return seconds;
701                                 }
702                                 break;
703                         case '+':
704                                 if(sign == 0) {
705                                         sign = 1;
706                                 } else {
707                                         return seconds;
708                                 }
709                                 break;
710                         case 's':
711                         case 'S':
712                                 seconds += i;
713                                 i = 0;
714                                 break;
715                         case 'm':
716                         case 'M':
717                                 seconds += i * 60;
718                                 i = 0;
719                                 break;
720                         case 'h':
721                         case 'H':
722                                 seconds += i * 60 * 60;
723                                 i = 0;
724                                 break;
725                         case 'd':
726                         case 'D':
727                                 seconds += i * 60 * 60 * 24;
728                                 i = 0;
729                                 break;
730                         case 'w':
731                         case 'W':
732                                 seconds += i * 60 * 60 * 24 * 7;
733                                 i = 0;
734                                 break;
735                         case '0':
736                         case '1':
737                         case '2':
738                         case '3':
739                         case '4':
740                         case '5':
741                         case '6':
742                         case '7':
743                         case '8':
744                         case '9':
745                                 i *= 10;
746                                 i += (**endptr - '0');
747                                 break;
748                         default:
749                                 seconds += i;
750                                 /* disregard signedness */
751                                 return seconds;
752                 }
753         }
754         seconds += i;
755         /* disregard signedness */
756         return seconds;
757 }