1 /* 2 * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * * Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * * Neither the name of Redis nor the names of its contributors may be used 14 * to endorse or promote products derived from this software without 15 * specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 * POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 #include "fmacros.h" 31 #include <stdlib.h> 32 #include <stdio.h> 33 #include <string.h> 34 #include <ctype.h> 35 #include <limits.h> 36 #include <math.h> 37 #include <unistd.h> 38 #include <sys/time.h> 39 #include <float.h> 40 #include <stdint.h> 41 #include <errno.h> 42 43 #include "util.h" 44 #include "sha1.h" 45 46 /* Glob-style pattern matching. */ 47 int stringmatchlen(const char *pattern, int patternLen, 48 const char *string, int stringLen, int nocase) 49 { 50 while(patternLen) { 51 switch(pattern[0]) { 52 case '*': 53 while (pattern[1] == '*') { 54 pattern++; 55 patternLen--; 56 } 57 if (patternLen == 1) 58 return 1; /* match */ 59 while(stringLen) { 60 if (stringmatchlen(pattern+1, patternLen-1, 61 string, stringLen, nocase)) 62 return 1; /* match */ 63 string++; 64 stringLen--; 65 } 66 return 0; /* no match */ 67 break; 68 case '?': 69 if (stringLen == 0) 70 return 0; /* no match */ 71 string++; 72 stringLen--; 73 break; 74 case '[': 75 { 76 int not, match; 77 78 pattern++; 79 patternLen--; 80 not = pattern[0] == '^'; 81 if (not) { 82 pattern++; 83 patternLen--; 84 } 85 match = 0; 86 while(1) { 87 if (pattern[0] == '\\') { 88 pattern++; 89 patternLen--; 90 if (pattern[0] == string[0]) 91 match = 1; 92 } else if (pattern[0] == ']') { 93 break; 94 } else if (patternLen == 0) { 95 pattern--; 96 patternLen++; 97 break; 98 } else if (pattern[1] == '-' && patternLen >= 3) { 99 int start = pattern[0]; 100 int end = pattern[2]; 101 int c = string[0]; 102 if (start > end) { 103 int t = start; 104 start = end; 105 end = t; 106 } 107 if (nocase) { 108 start = tolower(start); 109 end = tolower(end); 110 c = tolower(c); 111 } 112 pattern += 2; 113 patternLen -= 2; 114 if (c >= start && c <= end) 115 match = 1; 116 } else { 117 if (!nocase) { 118 if (pattern[0] == string[0]) 119 match = 1; 120 } else { 121 if (tolower((int)pattern[0]) == tolower((int)string[0])) 122 match = 1; 123 } 124 } 125 pattern++; 126 patternLen--; 127 } 128 if (not) 129 match = !match; 130 if (!match) 131 return 0; /* no match */ 132 string++; 133 stringLen--; 134 break; 135 } 136 case '\\': 137 if (patternLen >= 2) { 138 pattern++; 139 patternLen--; 140 } 141 /* fall through */ 142 default: 143 if (!nocase) { 144 if (pattern[0] != string[0]) 145 return 0; /* no match */ 146 } else { 147 if (tolower((int)pattern[0]) != tolower((int)string[0])) 148 return 0; /* no match */ 149 } 150 string++; 151 stringLen--; 152 break; 153 } 154 pattern++; 155 patternLen--; 156 if (stringLen == 0) { 157 while(*pattern == '*') { 158 pattern++; 159 patternLen--; 160 } 161 break; 162 } 163 } 164 if (patternLen == 0 && stringLen == 0) 165 return 1; 166 return 0; 167 } 168 169 int stringmatch(const char *pattern, const char *string, int nocase) { 170 return stringmatchlen(pattern,strlen(pattern),string,strlen(string),nocase); 171 } 172 173 /* Convert a string representing an amount of memory into the number of 174 * bytes, so for instance memtoll("1Gb") will return 1073741824 that is 175 * (1024*1024*1024). 176 * 177 * On parsing error, if *err is not NULL, it's set to 1, otherwise it's 178 * set to 0. On error the function return value is 0, regardless of the 179 * fact 'err' is NULL or not. */ 180 long long memtoll(const char *p, int *err) { 181 const char *u; 182 char buf[128]; 183 long mul; /* unit multiplier */ 184 long long val; 185 unsigned int digits; 186 187 if (err) *err = 0; 188 189 /* Search the first non digit character. */ 190 u = p; 191 if (*u == '-') u++; 192 while(*u && isdigit(*u)) u++; 193 if (*u == '\0' || !strcasecmp(u,"b")) { 194 mul = 1; 195 } else if (!strcasecmp(u,"k")) { 196 mul = 1000; 197 } else if (!strcasecmp(u,"kb")) { 198 mul = 1024; 199 } else if (!strcasecmp(u,"m")) { 200 mul = 1000*1000; 201 } else if (!strcasecmp(u,"mb")) { 202 mul = 1024*1024; 203 } else if (!strcasecmp(u,"g")) { 204 mul = 1000L*1000*1000; 205 } else if (!strcasecmp(u,"gb")) { 206 mul = 1024L*1024*1024; 207 } else { 208 if (err) *err = 1; 209 return 0; 210 } 211 212 /* Copy the digits into a buffer, we'll use strtoll() to convert 213 * the digit (without the unit) into a number. */ 214 digits = u-p; 215 if (digits >= sizeof(buf)) { 216 if (err) *err = 1; 217 return 0; 218 } 219 memcpy(buf,p,digits); 220 buf[digits] = '\0'; 221 222 char *endptr; 223 errno = 0; 224 val = strtoll(buf,&endptr,10); 225 if ((val == 0 && errno == EINVAL) || *endptr != '\0') { 226 if (err) *err = 1; 227 return 0; 228 } 229 return val*mul; 230 } 231 232 /* Return the number of digits of 'v' when converted to string in radix 10. 233 * See ll2string() for more information. */ 234 uint32_t digits10(uint64_t v) { 235 if (v < 10) return 1; 236 if (v < 100) return 2; 237 if (v < 1000) return 3; 238 if (v < 1000000000000UL) { 239 if (v < 100000000UL) { 240 if (v < 1000000) { 241 if (v < 10000) return 4; 242 return 5 + (v >= 100000); 243 } 244 return 7 + (v >= 10000000UL); 245 } 246 if (v < 10000000000UL) { 247 return 9 + (v >= 1000000000UL); 248 } 249 return 11 + (v >= 100000000000UL); 250 } 251 return 12 + digits10(v / 1000000000000UL); 252 } 253 254 /* Like digits10() but for signed values. */ 255 uint32_t sdigits10(int64_t v) { 256 if (v < 0) { 257 /* Abs value of LLONG_MIN requires special handling. */ 258 uint64_t uv = (v != LLONG_MIN) ? 259 (uint64_t)-v : ((uint64_t) LLONG_MAX)+1; 260 return digits10(uv)+1; /* +1 for the minus. */ 261 } else { 262 return digits10(v); 263 } 264 } 265 266 /* Convert a long long into a string. Returns the number of 267 * characters needed to represent the number. 268 * If the buffer is not big enough to store the string, 0 is returned. 269 * 270 * Based on the following article (that apparently does not provide a 271 * novel approach but only publicizes an already used technique): 272 * 273 * https://www.facebook.com/notes/facebook-engineering/three-optimization-tips-for-c/10151361643253920 274 * 275 * Modified in order to handle signed integers since the original code was 276 * designed for unsigned integers. */ 277 int ll2string(char* dst, size_t dstlen, long long svalue) { 278 static const char digits[201] = 279 "0001020304050607080910111213141516171819" 280 "2021222324252627282930313233343536373839" 281 "4041424344454647484950515253545556575859" 282 "6061626364656667686970717273747576777879" 283 "8081828384858687888990919293949596979899"; 284 int negative; 285 unsigned long long value; 286 287 /* The main loop works with 64bit unsigned integers for simplicity, so 288 * we convert the number here and remember if it is negative. */ 289 if (svalue < 0) { 290 if (svalue != LLONG_MIN) { 291 value = -svalue; 292 } else { 293 value = ((unsigned long long) LLONG_MAX)+1; 294 } 295 negative = 1; 296 } else { 297 value = svalue; 298 negative = 0; 299 } 300 301 /* Check length. */ 302 uint32_t const length = digits10(value)+negative; 303 if (length >= dstlen) return 0; 304 305 /* Null term. */ 306 uint32_t next = length; 307 dst[next] = '\0'; 308 next--; 309 while (value >= 100) { 310 int const i = (value % 100) * 2; 311 value /= 100; 312 dst[next] = digits[i + 1]; 313 dst[next - 1] = digits[i]; 314 next -= 2; 315 } 316 317 /* Handle last 1-2 digits. */ 318 if (value < 10) { 319 dst[next] = '0' + (uint32_t) value; 320 } else { 321 int i = (uint32_t) value * 2; 322 dst[next] = digits[i + 1]; 323 dst[next - 1] = digits[i]; 324 } 325 326 /* Add sign. */ 327 if (negative) dst[0] = '-'; 328 return length; 329 } 330 331 /* Convert a string into a long long. Returns 1 if the string could be parsed 332 * into a (non-overflowing) long long, 0 otherwise. The value will be set to 333 * the parsed value when appropriate. */ 334 int string2ll(const char *s, size_t slen, long long *value) { 335 const char *p = s; 336 size_t plen = 0; 337 int negative = 0; 338 unsigned long long v; 339 340 if (plen == slen) 341 return 0; 342 343 /* Special case: first and only digit is 0. */ 344 if (slen == 1 && p[0] == '0') { 345 if (value != NULL) *value = 0; 346 return 1; 347 } 348 349 if (p[0] == '-') { 350 negative = 1; 351 p++; plen++; 352 353 /* Abort on only a negative sign. */ 354 if (plen == slen) 355 return 0; 356 } 357 358 /* First digit should be 1-9, otherwise the string should just be 0. */ 359 if (p[0] >= '1' && p[0] <= '9') { 360 v = p[0]-'0'; 361 p++; plen++; 362 } else if (p[0] == '0' && slen == 1) { 363 *value = 0; 364 return 1; 365 } else { 366 return 0; 367 } 368 369 while (plen < slen && p[0] >= '0' && p[0] <= '9') { 370 if (v > (ULLONG_MAX / 10)) /* Overflow. */ 371 return 0; 372 v *= 10; 373 374 if (v > (ULLONG_MAX - (p[0]-'0'))) /* Overflow. */ 375 return 0; 376 v += p[0]-'0'; 377 378 p++; plen++; 379 } 380 381 /* Return if not all bytes were used. */ 382 if (plen < slen) 383 return 0; 384 385 if (negative) { 386 if (v > ((unsigned long long)(-(LLONG_MIN+1))+1)) /* Overflow. */ 387 return 0; 388 if (value != NULL) *value = -v; 389 } else { 390 if (v > LLONG_MAX) /* Overflow. */ 391 return 0; 392 if (value != NULL) *value = v; 393 } 394 return 1; 395 } 396 397 /* Convert a string into a long. Returns 1 if the string could be parsed into a 398 * (non-overflowing) long, 0 otherwise. The value will be set to the parsed 399 * value when appropriate. */ 400 int string2l(const char *s, size_t slen, long *lval) { 401 long long llval; 402 403 if (!string2ll(s,slen,&llval)) 404 return 0; 405 406 if (llval < LONG_MIN || llval > LONG_MAX) 407 return 0; 408 409 *lval = (long)llval; 410 return 1; 411 } 412 413 /* Convert a double to a string representation. Returns the number of bytes 414 * required. The representation should always be parsable by strtod(3). */ 415 int d2string(char *buf, size_t len, double value) { 416 if (isnan(value)) { 417 len = snprintf(buf,len,"nan"); 418 } else if (isinf(value)) { 419 if (value < 0) 420 len = snprintf(buf,len,"-inf"); 421 else 422 len = snprintf(buf,len,"inf"); 423 } else if (value == 0) { 424 /* See: http://en.wikipedia.org/wiki/Signed_zero, "Comparisons". */ 425 if (1.0/value < 0) 426 len = snprintf(buf,len,"-0"); 427 else 428 len = snprintf(buf,len,"0"); 429 } else { 430 #if (DBL_MANT_DIG >= 52) && (LLONG_MAX == 0x7fffffffffffffffLL) 431 /* Check if the float is in a safe range to be casted into a 432 * long long. We are assuming that long long is 64 bit here. 433 * Also we are assuming that there are no implementations around where 434 * double has precision < 52 bit. 435 * 436 * Under this assumptions we test if a double is inside an interval 437 * where casting to long long is safe. Then using two castings we 438 * make sure the decimal part is zero. If all this is true we use 439 * integer printing function that is much faster. */ 440 double min = -4503599627370495; /* (2^52)-1 */ 441 double max = 4503599627370496; /* -(2^52) */ 442 if (value > min && value < max && value == ((double)((long long)value))) 443 len = ll2string(buf,len,(long long)value); 444 else 445 #endif 446 len = snprintf(buf,len,"%.17g",value); 447 } 448 449 return len; 450 } 451 452 /* Generate the Redis "Run ID", a SHA1-sized random number that identifies a 453 * given execution of Redis, so that if you are talking with an instance 454 * having run_id == A, and you reconnect and it has run_id == B, you can be 455 * sure that it is either a different instance or it was restarted. */ 456 void getRandomHexChars(char *p, unsigned int len) { 457 char *charset = "0123456789abcdef"; 458 unsigned int j; 459 460 /* Global state. */ 461 static int seed_initialized = 0; 462 static unsigned char seed[20]; /* The SHA1 seed, from /dev/urandom. */ 463 static uint64_t counter = 0; /* The counter we hash with the seed. */ 464 465 if (!seed_initialized) { 466 /* Initialize a seed and use SHA1 in counter mode, where we hash 467 * the same seed with a progressive counter. For the goals of this 468 * function we just need non-colliding strings, there are no 469 * cryptographic security needs. */ 470 FILE *fp = fopen("/dev/urandom","r"); 471 if (fp && fread(seed,sizeof(seed),1,fp) == 1) 472 seed_initialized = 1; 473 if (fp) fclose(fp); 474 } 475 476 if (seed_initialized) { 477 while(len) { 478 unsigned char digest[20]; 479 SHA1_CTX ctx; 480 unsigned int copylen = len > 20 ? 20 : len; 481 482 SHA1Init(&ctx); 483 SHA1Update(&ctx, seed, sizeof(seed)); 484 SHA1Update(&ctx, (unsigned char*)&counter,sizeof(counter)); 485 SHA1Final(digest, &ctx); 486 counter++; 487 488 memcpy(p,digest,copylen); 489 /* Convert to hex digits. */ 490 for (j = 0; j < copylen; j++) p[j] = charset[p[j] & 0x0F]; 491 len -= copylen; 492 p += copylen; 493 } 494 } else { 495 /* If we can't read from /dev/urandom, do some reasonable effort 496 * in order to create some entropy, since this function is used to 497 * generate run_id and cluster instance IDs */ 498 char *x = p; 499 unsigned int l = len; 500 struct timeval tv; 501 pid_t pid = getpid(); 502 503 /* Use time and PID to fill the initial array. */ 504 gettimeofday(&tv,NULL); 505 if (l >= sizeof(tv.tv_usec)) { 506 memcpy(x,&tv.tv_usec,sizeof(tv.tv_usec)); 507 l -= sizeof(tv.tv_usec); 508 x += sizeof(tv.tv_usec); 509 } 510 if (l >= sizeof(tv.tv_sec)) { 511 memcpy(x,&tv.tv_sec,sizeof(tv.tv_sec)); 512 l -= sizeof(tv.tv_sec); 513 x += sizeof(tv.tv_sec); 514 } 515 if (l >= sizeof(pid)) { 516 memcpy(x,&pid,sizeof(pid)); 517 l -= sizeof(pid); 518 x += sizeof(pid); 519 } 520 /* Finally xor it with rand() output, that was already seeded with 521 * time() at startup, and convert to hex digits. */ 522 for (j = 0; j < len; j++) { 523 p[j] ^= rand(); 524 p[j] = charset[p[j] & 0x0F]; 525 } 526 } 527 } 528 529 /* Given the filename, return the absolute path as an SDS string, or NULL 530 * if it fails for some reason. Note that "filename" may be an absolute path 531 * already, this will be detected and handled correctly. 532 * 533 * The function does not try to normalize everything, but only the obvious 534 * case of one or more "../" appearning at the start of "filename" 535 * relative path. */ 536 sds getAbsolutePath(char *filename) { 537 char cwd[1024]; 538 sds abspath; 539 sds relpath = sdsnew(filename); 540 541 relpath = sdstrim(relpath," \r\n\t"); 542 if (relpath[0] == '/') return relpath; /* Path is already absolute. */ 543 544 /* If path is relative, join cwd and relative path. */ 545 if (getcwd(cwd,sizeof(cwd)) == NULL) { 546 sdsfree(relpath); 547 return NULL; 548 } 549 abspath = sdsnew(cwd); 550 if (sdslen(abspath) && abspath[sdslen(abspath)-1] != '/') 551 abspath = sdscat(abspath,"/"); 552 553 /* At this point we have the current path always ending with "/", and 554 * the trimmed relative path. Try to normalize the obvious case of 555 * trailing ../ elements at the start of the path. 556 * 557 * For every "../" we find in the filename, we remove it and also remove 558 * the last element of the cwd, unless the current cwd is "/". */ 559 while (sdslen(relpath) >= 3 && 560 relpath[0] == '.' && relpath[1] == '.' && relpath[2] == '/') 561 { 562 sdsrange(relpath,3,-1); 563 if (sdslen(abspath) > 1) { 564 char *p = abspath + sdslen(abspath)-2; 565 int trimlen = 1; 566 567 while(*p != '/') { 568 p--; 569 trimlen++; 570 } 571 sdsrange(abspath,0,-(trimlen+1)); 572 } 573 } 574 575 /* Finally glue the two parts together. */ 576 abspath = sdscatsds(abspath,relpath); 577 sdsfree(relpath); 578 return abspath; 579 } 580 581 /* Return true if the specified path is just a file basename without any 582 * relative or absolute path. This function just checks that no / or \ 583 * character exists inside the specified path, that's enough in the 584 * environments where Redis runs. */ 585 int pathIsBaseName(char *path) { 586 return strchr(path,'/') == NULL && strchr(path,'\\') == NULL; 587 } 588 589 #ifdef REDIS_TEST 590 #include <assert.h> 591 592 static void test_string2ll(void) { 593 char buf[32]; 594 long long v; 595 596 /* May not start with +. */ 597 strcpy(buf,"+1"); 598 assert(string2ll(buf,strlen(buf),&v) == 0); 599 600 /* Leading space. */ 601 strcpy(buf," 1"); 602 assert(string2ll(buf,strlen(buf),&v) == 0); 603 604 /* Trailing space. */ 605 strcpy(buf,"1 "); 606 assert(string2ll(buf,strlen(buf),&v) == 0); 607 608 /* May not start with 0. */ 609 strcpy(buf,"01"); 610 assert(string2ll(buf,strlen(buf),&v) == 0); 611 612 strcpy(buf,"-1"); 613 assert(string2ll(buf,strlen(buf),&v) == 1); 614 assert(v == -1); 615 616 strcpy(buf,"0"); 617 assert(string2ll(buf,strlen(buf),&v) == 1); 618 assert(v == 0); 619 620 strcpy(buf,"1"); 621 assert(string2ll(buf,strlen(buf),&v) == 1); 622 assert(v == 1); 623 624 strcpy(buf,"99"); 625 assert(string2ll(buf,strlen(buf),&v) == 1); 626 assert(v == 99); 627 628 strcpy(buf,"-99"); 629 assert(string2ll(buf,strlen(buf),&v) == 1); 630 assert(v == -99); 631 632 strcpy(buf,"-9223372036854775808"); 633 assert(string2ll(buf,strlen(buf),&v) == 1); 634 assert(v == LLONG_MIN); 635 636 strcpy(buf,"-9223372036854775809"); /* overflow */ 637 assert(string2ll(buf,strlen(buf),&v) == 0); 638 639 strcpy(buf,"9223372036854775807"); 640 assert(string2ll(buf,strlen(buf),&v) == 1); 641 assert(v == LLONG_MAX); 642 643 strcpy(buf,"9223372036854775808"); /* overflow */ 644 assert(string2ll(buf,strlen(buf),&v) == 0); 645 } 646 647 static void test_string2l(void) { 648 char buf[32]; 649 long v; 650 651 /* May not start with +. */ 652 strcpy(buf,"+1"); 653 assert(string2l(buf,strlen(buf),&v) == 0); 654 655 /* May not start with 0. */ 656 strcpy(buf,"01"); 657 assert(string2l(buf,strlen(buf),&v) == 0); 658 659 strcpy(buf,"-1"); 660 assert(string2l(buf,strlen(buf),&v) == 1); 661 assert(v == -1); 662 663 strcpy(buf,"0"); 664 assert(string2l(buf,strlen(buf),&v) == 1); 665 assert(v == 0); 666 667 strcpy(buf,"1"); 668 assert(string2l(buf,strlen(buf),&v) == 1); 669 assert(v == 1); 670 671 strcpy(buf,"99"); 672 assert(string2l(buf,strlen(buf),&v) == 1); 673 assert(v == 99); 674 675 strcpy(buf,"-99"); 676 assert(string2l(buf,strlen(buf),&v) == 1); 677 assert(v == -99); 678 679 #if LONG_MAX != LLONG_MAX 680 strcpy(buf,"-2147483648"); 681 assert(string2l(buf,strlen(buf),&v) == 1); 682 assert(v == LONG_MIN); 683 684 strcpy(buf,"-2147483649"); /* overflow */ 685 assert(string2l(buf,strlen(buf),&v) == 0); 686 687 strcpy(buf,"2147483647"); 688 assert(string2l(buf,strlen(buf),&v) == 1); 689 assert(v == LONG_MAX); 690 691 strcpy(buf,"2147483648"); /* overflow */ 692 assert(string2l(buf,strlen(buf),&v) == 0); 693 #endif 694 } 695 696 static void test_ll2string(void) { 697 char buf[32]; 698 long long v; 699 int sz; 700 701 v = 0; 702 sz = ll2string(buf, sizeof buf, v); 703 assert(sz == 1); 704 assert(!strcmp(buf, "0")); 705 706 v = -1; 707 sz = ll2string(buf, sizeof buf, v); 708 assert(sz == 2); 709 assert(!strcmp(buf, "-1")); 710 711 v = 99; 712 sz = ll2string(buf, sizeof buf, v); 713 assert(sz == 2); 714 assert(!strcmp(buf, "99")); 715 716 v = -99; 717 sz = ll2string(buf, sizeof buf, v); 718 assert(sz == 3); 719 assert(!strcmp(buf, "-99")); 720 721 v = -2147483648; 722 sz = ll2string(buf, sizeof buf, v); 723 assert(sz == 11); 724 assert(!strcmp(buf, "-2147483648")); 725 726 v = LLONG_MIN; 727 sz = ll2string(buf, sizeof buf, v); 728 assert(sz == 20); 729 assert(!strcmp(buf, "-9223372036854775808")); 730 731 v = LLONG_MAX; 732 sz = ll2string(buf, sizeof buf, v); 733 assert(sz == 19); 734 assert(!strcmp(buf, "9223372036854775807")); 735 } 736 737 #define UNUSED(x) (void)(x) 738 int utilTest(int argc, char **argv) { 739 UNUSED(argc); 740 UNUSED(argv); 741 742 test_string2ll(); 743 test_string2l(); 744 test_ll2string(); 745 return 0; 746 } 747 #endif 748