util.c
上传用户:liugui
上传日期:2007-01-04
资源大小:822k
文件大小:15k
源码类别:

代理服务器

开发平台:

Unix_Linux

  1. /*
  2.  * $Id: util.c,v 1.65.2.1 1999/02/12 19:38:16 wessels Exp $
  3.  *
  4.  * DEBUG: 
  5.  * AUTHOR: Harvest Derived
  6.  *
  7.  * SQUID Internet Object Cache  http://squid.nlanr.net/Squid/
  8.  * ----------------------------------------------------------
  9.  *
  10.  *  Squid is the result of efforts by numerous individuals from the
  11.  *  Internet community.  Development is led by Duane Wessels of the
  12.  *  National Laboratory for Applied Network Research and funded by the
  13.  *  National Science Foundation.  Squid is Copyrighted (C) 1998 by
  14.  *  Duane Wessels and the University of California San Diego.  Please
  15.  *  see the COPYRIGHT file for full details.  Squid incorporates
  16.  *  software developed and/or copyrighted by other sources.  Please see
  17.  *  the CREDITS file for full details.
  18.  *
  19.  *  This program is free software; you can redistribute it and/or modify
  20.  *  it under the terms of the GNU General Public License as published by
  21.  *  the Free Software Foundation; either version 2 of the License, or
  22.  *  (at your option) any later version.
  23.  *  
  24.  *  This program is distributed in the hope that it will be useful,
  25.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  26.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  27.  *  GNU General Public License for more details.
  28.  *  
  29.  *  You should have received a copy of the GNU General Public License
  30.  *  along with this program; if not, write to the Free Software
  31.  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
  32.  *
  33.  */
  34. #define _etext etext
  35. #include "config.h"
  36. #if HAVE_STDIO_H
  37. #include <stdio.h>
  38. #endif
  39. #if HAVE_STDLIB_H
  40. #include <stdlib.h>
  41. #endif
  42. #if HAVE_STRING_H
  43. #include <string.h>
  44. #endif
  45. #if HAVE_CTYPE_H
  46. #include <ctype.h>
  47. #endif
  48. #if HAVE_UNISTD_H
  49. #include <unistd.h>
  50. #endif
  51. #if HAVE_GNUMALLLOC_H
  52. #include <gnumalloc.h>
  53. #elif HAVE_MALLOC_H && !defined(_SQUID_FREEBSD_) && !defined(_SQUID_NEXT_)
  54. #include <malloc.h>
  55. #endif
  56. #if HAVE_ERRNO_H
  57. #include <errno.h>
  58. #endif
  59. #if HAVE_MATH_H
  60. #include <math.h>
  61. #endif
  62. #if HAVE_ASSERT_H
  63. #include <assert.h>
  64. #endif
  65. #include "util.h"
  66. #include "snprintf.h"
  67. void (*failure_notify) (const char *) = NULL;
  68. static char msg[128];
  69. extern int sys_nerr;
  70. #if MEM_GEN_TRACE
  71. static FILE *tracefp = NULL;
  72. void
  73. log_trace_init(char *fn)
  74. {
  75.     tracefp = fopen(fn, "a+");
  76.     if (!tracefp) {
  77. perror("log_trace_init");
  78. exit(1);
  79.     }
  80. }
  81. void
  82. log_trace_done()
  83. {
  84.     fclose(tracefp);
  85.     tracefp = NULL;
  86. }
  87. #endif
  88. #if XMALLOC_STATISTICS
  89. #define DBG_MAXSIZE   (1024*1024)
  90. #define DBG_GRAIN     (16)
  91. #define DBG_MAXINDEX  (DBG_MAXSIZE/DBG_GRAIN)
  92. #define DBG_INDEX(sz) (sz<DBG_MAXSIZE?(sz+DBG_GRAIN-1)/DBG_GRAIN:DBG_MAXINDEX)
  93. static int malloc_sizes[DBG_MAXINDEX + 1];
  94. static int dbg_stat_init = 0;
  95. static void
  96. stat_init(void)
  97. {
  98.     int i;
  99.     for (i = 0; i <= DBG_MAXINDEX; i++)
  100. malloc_sizes[i] = 0;
  101.     dbg_stat_init = 1;
  102. }
  103. static int
  104. malloc_stat(int sz)
  105. {
  106.     if (!dbg_stat_init)
  107. stat_init();
  108.     return malloc_sizes[DBG_INDEX(sz)] += 1;
  109. }
  110. void
  111. malloc_statistics(void (*func) (int, int, void *), void *data)
  112. {
  113.     int i;
  114.     for (i = 0; i <= DBG_MAXSIZE; i += DBG_GRAIN)
  115. func(i, malloc_sizes[DBG_INDEX(i)], data);
  116. }
  117. #endif /* XMALLOC_STATISTICS */
  118. #if XMALLOC_TRACE
  119. char *xmalloc_file = "";
  120. int xmalloc_line = 0;
  121. char *xmalloc_func = "";
  122. static int xmalloc_count = 0;
  123. int xmalloc_trace = 0; /* Enable with -m option */
  124. size_t xmalloc_total = 0;
  125. #undef xmalloc
  126. #undef xfree
  127. #undef xxfree
  128. #undef xrealloc
  129. #undef xcalloc
  130. #undef xstrdup
  131. #endif
  132. #if XMALLOC_DEBUG
  133. #define DBG_ARRY_SZ (1<<10)
  134. #define DBG_ARRY_BKTS (1<<8)
  135. static void *(*malloc_ptrs)[DBG_ARRY_SZ];
  136. static int malloc_size[DBG_ARRY_BKTS][DBG_ARRY_SZ];
  137. #if XMALLOC_TRACE
  138. static char *malloc_file[DBG_ARRY_BKTS][DBG_ARRY_SZ];
  139. static short malloc_line[DBG_ARRY_BKTS][DBG_ARRY_SZ];
  140. static int malloc_count[DBG_ARRY_BKTS][DBG_ARRY_SZ];
  141. #endif
  142. static int dbg_initd = 0;
  143. #define DBG_HASH_BUCKET(ptr)   (((((int)ptr)>>4)+(((int)ptr)>>12)+(((int)ptr)>>20))&0xFF)
  144. static void
  145. check_init(void)
  146. {
  147.     int B = 0, I = 0;
  148.     /* calloc the ptrs so that we don't see them when hunting lost memory */
  149.     malloc_ptrs = calloc(DBG_ARRY_BKTS, sizeof(*malloc_ptrs));
  150.     for (B = 0; B < DBG_ARRY_BKTS; B++) {
  151. for (I = 0; I < DBG_ARRY_SZ; I++) {
  152.     malloc_ptrs[B][I] = NULL;
  153.     malloc_size[B][I] = 0;
  154. #if XMALLOC_TRACE
  155.     malloc_file[B][I] = NULL;
  156.     malloc_line[B][I] = 0;
  157.     malloc_count[B][I] = 0;
  158. #endif
  159. }
  160.     }
  161.     dbg_initd = 1;
  162. }
  163. static void
  164. check_free(void *s)
  165. {
  166.     int B, I;
  167.     B = DBG_HASH_BUCKET(s);
  168.     for (I = 0; I < DBG_ARRY_SZ; I++) {
  169. if (malloc_ptrs[B][I] != s)
  170.     continue;
  171. malloc_ptrs[B][I] = NULL;
  172. malloc_size[B][I] = 0;
  173. #if XMALLOC_TRACE
  174. malloc_file[B][I] = NULL;
  175. malloc_line[B][I] = 0;
  176. malloc_count[B][I] = 0;
  177. #endif
  178. break;
  179.     }
  180.     if (I == DBG_ARRY_SZ) {
  181. snprintf(msg, 128, "xfree: ERROR: s=%p not found!", s);
  182. (*failure_notify) (msg);
  183.     }
  184. }
  185. static void
  186. check_malloc(void *p, size_t sz)
  187. {
  188.     void *P, *Q;
  189.     int B, I;
  190.     if (!dbg_initd)
  191. check_init();
  192.     B = DBG_HASH_BUCKET(p);
  193.     for (I = 0; I < DBG_ARRY_SZ; I++) {
  194. if (!(P = malloc_ptrs[B][I]))
  195.     continue;
  196. Q = P + malloc_size[B][I];
  197. if (P <= p && p < Q) {
  198.     snprintf(msg, 128, "xmalloc: ERROR: p=%p falls in P=%p+%d",
  199. p, P, malloc_size[B][I]);
  200.     (*failure_notify) (msg);
  201. }
  202.     }
  203.     for (I = 0; I < DBG_ARRY_SZ; I++) {
  204. if (malloc_ptrs[B][I])
  205.     continue;
  206. malloc_ptrs[B][I] = p;
  207. malloc_size[B][I] = (int) sz;
  208. #if XMALLOC_TRACE
  209. malloc_file[B][I] = xmalloc_file;
  210. malloc_line[B][I] = xmalloc_line;
  211. malloc_count[B][I] = xmalloc_count;
  212. #endif
  213. break;
  214.     }
  215.     if (I == DBG_ARRY_SZ)
  216. (*failure_notify) ("xmalloc: debug out of array space!");
  217. }
  218. #endif
  219. #if XMALLOC_TRACE && !HAVE_MALLOCBLKSIZE
  220. size_t
  221. xmallocblksize(void *p)
  222. {
  223.     int B, I;
  224.     B = DBG_HASH_BUCKET(p);
  225.     for (I = 0; I < DBG_ARRY_SZ; I++) {
  226. if (malloc_ptrs[B][I] == p)
  227.     return malloc_size[B][I];
  228.     }
  229.     return 0;
  230. }
  231. #endif
  232. #ifdef XMALLOC_TRACE
  233. static char *
  234. malloc_file_name(void *p)
  235. {
  236.     int B, I;
  237.     B = DBG_HASH_BUCKET(p);
  238.     for (I = 0; I < DBG_ARRY_SZ; I++) {
  239. if (malloc_ptrs[B][I] == p)
  240.     return malloc_file[B][I];
  241.     }
  242.     return 0;
  243. }
  244. int
  245. malloc_line_number(void *p)
  246. {
  247.     int B, I;
  248.     B = DBG_HASH_BUCKET(p);
  249.     for (I = 0; I < DBG_ARRY_SZ; I++) {
  250. if (malloc_ptrs[B][I] == p)
  251.     return malloc_line[B][I];
  252.     }
  253.     return 0;
  254. }
  255. int
  256. malloc_number(void *p)
  257. {
  258.     int B, I;
  259.     B = DBG_HASH_BUCKET(p);
  260.     for (I = 0; I < DBG_ARRY_SZ; I++) {
  261. if (malloc_ptrs[B][I] == p)
  262.     return malloc_count[B][I];
  263.     }
  264.     return 0;
  265. }
  266. static void
  267. xmalloc_show_trace(void *p, int sign)
  268. {
  269.     int statMemoryAccounted();
  270.     static size_t last_total = 0, last_accounted = 0, last_mallinfo = 0;
  271.     size_t accounted = statMemoryAccounted();
  272.     size_t mi = 0;
  273.     size_t sz;
  274. #if HAVE_MALLINFO
  275.     struct mallinfo mp = mallinfo();
  276.     mi = mp.uordblks + mp.usmblks + mp.hblkhd;
  277. #endif
  278.     sz = xmallocblksize(p) * sign;
  279.     xmalloc_total += sz;
  280.     xmalloc_count += sign > 0;
  281.     if (xmalloc_trace) {
  282. fprintf(stderr, "%c%8p size=%5d/%d acc=%5d/%d mallinfo=%5d/%d %s:%d %s",
  283.     sign > 0 ? '+' : '-', p,
  284.     (int) xmalloc_total - last_total, (int) xmalloc_total,
  285.     (int) accounted - last_accounted, (int) accounted,
  286.     (int) mi - last_mallinfo, (int) mi,
  287.     xmalloc_file, xmalloc_line, xmalloc_func);
  288. if (sign < 0)
  289.     fprintf(stderr, " (%d %s:%d)n", malloc_number(p), malloc_file_name(p), malloc_line_number(p));
  290. else
  291.     fprintf(stderr, " %dn", xmalloc_count);
  292.     }
  293.     last_total = xmalloc_total;
  294.     last_accounted = accounted;
  295.     last_mallinfo = mi;
  296. }
  297. short malloc_refs[DBG_ARRY_BKTS][DBG_ARRY_SZ];
  298. #define XMALLOC_LEAK_ALIGN (4)
  299. static void
  300. xmalloc_scan_region(void *start, int size, int depth)
  301. {
  302.     int B, I;
  303.     char *ptr = start;
  304.     char *end = ptr + size - XMALLOC_LEAK_ALIGN;
  305.     static int sum = 0;
  306.     while (ptr <= end) {
  307. void *p = *(void **) ptr;
  308. if (p && p != start) {
  309.     B = DBG_HASH_BUCKET(p);
  310.     for (I = 0; I < DBG_ARRY_SZ; I++) {
  311. if (malloc_ptrs[B][I] == p) {
  312.     if (!malloc_refs[B][I]++) {
  313. /* A new reference */
  314. fprintf(stderr, "%*s%p %s:%d size %d allocation %dn",
  315.     depth, "",
  316.     malloc_ptrs[B][I], malloc_file[B][I],
  317.     malloc_line[B][I], malloc_size[B][I],
  318.     malloc_count[B][I]);
  319. sum += malloc_size[B][I];
  320. xmalloc_scan_region(malloc_ptrs[B][I], malloc_size[B][I], depth + 1);
  321. if (depth == 0) {
  322.     if (sum != malloc_size[B][I])
  323. fprintf(stderr, "=== %d bytesn", sum);
  324.     sum = 0;
  325. }
  326. #if XMALLOC_SHOW_ALL_REFERENCES
  327.     } else {
  328. /* We have already scanned this pointer... */
  329. fprintf(stderr, "%*s%p %s:%d size %d allocation %d ... (%d)n",
  330.     depth * 2, "",
  331.     malloc_ptrs[B][I], malloc_file[B][I],
  332.     malloc_line[B][I], malloc_size[B][I],
  333.     malloc_count[B][I], malloc_refs[B][I]);
  334. #endif
  335.     }
  336. }
  337.     }
  338. }
  339. ptr += XMALLOC_LEAK_ALIGN;
  340.     }
  341. }
  342. void
  343. xmalloc_find_leaks(void)
  344. {
  345.     int B, I;
  346.     int leak_sum = 0;
  347.     extern void _etext;
  348.     fprintf(stderr, "----- Memory map ----n");
  349.     xmalloc_scan_region(&_etext, (void *) sbrk(0) - (void *) &_etext, 0);
  350.     for (B = 0; B < DBG_ARRY_BKTS; B++) {
  351. for (I = 0; I < DBG_ARRY_SZ; I++) {
  352.     if (malloc_ptrs[B][I] && malloc_refs[B][I] == 0) {
  353. /* Found a leak... */
  354. fprintf(stderr, "Leak found: %p", malloc_ptrs[B][I]);
  355. fprintf(stderr, " %s", malloc_file[B][I]);
  356. fprintf(stderr, ":%d", malloc_line[B][I]);
  357. fprintf(stderr, " size %d", malloc_size[B][I]);
  358. fprintf(stderr, " allocation %dn", malloc_count[B][I]);
  359. leak_sum += malloc_size[B][I];
  360.     }
  361. }
  362.     }
  363.     if (leak_sum) {
  364. fprintf(stderr, "Total leaked memory: %dn", leak_sum);
  365.     } else {
  366. fprintf(stderr, "No memory leaks detectedn");
  367.     }
  368.     fprintf(stderr, "----------------------n");
  369. }
  370. #endif /* XMALLOC_TRACE */
  371. /*
  372.  *  xmalloc() - same as malloc(3).  Used for portability.
  373.  *  Never returns NULL; fatal on error.
  374.  */
  375. void *
  376. xmalloc(size_t sz)
  377. {
  378.     void *p;
  379.     if (sz < 1)
  380. sz = 1;
  381.     if ((p = malloc(sz)) == NULL) {
  382. if (failure_notify) {
  383.     snprintf(msg, 128, "xmalloc: Unable to allocate %d bytes!n",
  384. (int) sz);
  385.     (*failure_notify) (msg);
  386. } else {
  387.     perror("malloc");
  388. }
  389. exit(1);
  390.     }
  391. #if XMALLOC_DEBUG
  392.     check_malloc(p, sz);
  393. #endif
  394. #if XMALLOC_STATISTICS
  395.     malloc_stat(sz);
  396. #endif
  397. #if XMALLOC_TRACE
  398.     xmalloc_show_trace(p, 1);
  399. #endif
  400. #if MEM_GEN_TRACE
  401.     if (tracefp)
  402. fprintf(tracefp, "m:%d:%pn", sz, p);
  403. #endif
  404.     return (p);
  405. }
  406. /*
  407.  *  xfree() - same as free(3).  Will not call free(3) if s == NULL.
  408.  */
  409. void
  410. xfree(void *s)
  411. {
  412. #if XMALLOC_TRACE
  413.     xmalloc_show_trace(s, -1);
  414. #endif
  415. #if XMALLOC_DEBUG
  416.     check_free(s);
  417. #endif
  418.     if (s != NULL)
  419. free(s);
  420. #if MEM_GEN_TRACE
  421.     if (tracefp && s)
  422. fprintf(tracefp, "f:%pn", s);
  423. #endif
  424. }
  425. /* xxfree() - like xfree(), but we already know s != NULL */
  426. void
  427. xxfree(void *s)
  428. {
  429. #if XMALLOC_TRACE
  430.     xmalloc_show_trace(s, -1);
  431. #endif
  432. #if XMALLOC_DEBUG
  433.     check_free(s);
  434. #endif
  435.     free(s);
  436. #if MEM_GEN_TRACE
  437.     if (tracefp && s)
  438. fprintf(tracefp, "f:%pn", s);
  439. #endif
  440. }
  441. /*
  442.  *  xrealloc() - same as realloc(3). Used for portability.
  443.  *  Never returns NULL; fatal on error.
  444.  */
  445. void *
  446. xrealloc(void *s, size_t sz)
  447. {
  448.     void *p;
  449. #if XMALLOC_TRACE
  450.     xmalloc_show_trace(s, -1);
  451. #endif
  452.     if (sz < 1)
  453. sz = 1;
  454. #if XMALLOC_DEBUG
  455.     if (s != NULL)
  456. check_free(s);
  457. #endif
  458.     if ((p = realloc(s, sz)) == NULL) {
  459. if (failure_notify) {
  460.     snprintf(msg, 128, "xrealloc: Unable to reallocate %d bytes!n",
  461. (int) sz);
  462.     (*failure_notify) (msg);
  463. } else {
  464.     perror("realloc");
  465. }
  466. exit(1);
  467.     }
  468. #if XMALLOC_DEBUG
  469.     check_malloc(p, sz);
  470. #endif
  471. #if XMALLOC_STATISTICS
  472.     malloc_stat(sz);
  473. #endif
  474. #if XMALLOC_TRACE
  475.     xmalloc_show_trace(p, 1);
  476. #endif
  477. #if MEM_GEN_TRACE
  478.     if (tracefp) /* new ptr, old ptr, new size */
  479. fprintf(tracefp, "r:%p:%p:%dn", p, s, sz);
  480. #endif
  481.     return (p);
  482. }
  483. /*
  484.  *  xcalloc() - same as calloc(3).  Used for portability.
  485.  *  Never returns NULL; fatal on error.
  486.  */
  487. void *
  488. xcalloc(int n, size_t sz)
  489. {
  490.     void *p;
  491.     if (n < 1)
  492. n = 1;
  493.     if (sz < 1)
  494. sz = 1;
  495.     if ((p = calloc(n, sz)) == NULL) {
  496. if (failure_notify) {
  497.     snprintf(msg, 128, "xcalloc: Unable to allocate %d blocks of %d bytes!n",
  498. (int) n, (int) sz);
  499.     (*failure_notify) (msg);
  500. } else {
  501.     perror("xcalloc");
  502. }
  503. exit(1);
  504.     }
  505. #if XMALLOC_DEBUG
  506.     check_malloc(p, sz * n);
  507. #endif
  508. #if XMALLOC_STATISTICS
  509.     malloc_stat(sz);
  510. #endif
  511. #if XMALLOC_TRACE
  512.     xmalloc_show_trace(p, 1);
  513. #endif
  514. #if MEM_GEN_TRACE
  515.     if (tracefp)
  516. fprintf(tracefp, "c:%d:%d:%pn", n, sz, p);
  517. #endif
  518.     return (p);
  519. }
  520. /*
  521.  *  xstrdup() - same as strdup(3).  Used for portability.
  522.  *  Never returns NULL; fatal on error.
  523.  */
  524. char *
  525. xstrdup(const char *s)
  526. {
  527.     size_t sz;
  528.     if (s == NULL) {
  529. if (failure_notify) {
  530.     (*failure_notify) ("xstrdup: tried to dup a NULL pointer!n");
  531. } else {
  532.     fprintf(stderr, "xstrdup: tried to dup a NULL pointer!n");
  533. }
  534. exit(1);
  535.     }
  536.     /* copy string, including terminating character */
  537.     sz = strlen(s) + 1;
  538.     return memcpy(xmalloc(sz), s, sz);
  539. }
  540. /*
  541.  *  xstrndup() - string dup with length limit.
  542.  */
  543. char *
  544. xstrndup(const char *s, size_t n)
  545. {
  546.     size_t sz;
  547.     assert(s);
  548.     assert(n);
  549.     sz = strlen(s) + 1;
  550.     if (sz > n)
  551. sz = n;
  552.     return xstrncpy(xmalloc(sz), s, sz);
  553. }
  554. /*
  555.  * xstrerror() - strerror() wrapper
  556.  */
  557. const char *
  558. xstrerror(void)
  559. {
  560.     static char xstrerror_buf[BUFSIZ];
  561.     if (errno < 0 || errno >= sys_nerr)
  562. return ("Unknown");
  563.     snprintf(xstrerror_buf, BUFSIZ, "(%d) %s", errno, strerror(errno));
  564.     return xstrerror_buf;
  565. }
  566. #if NOT_NEEDED
  567. /*
  568.  * xbstrerror with argument for late notification */
  569. const char *
  570. xbstrerror(int err)
  571. {
  572.     static char xbstrerror_buf[BUFSIZ];
  573.     if (err < 0 || err >= sys_nerr)
  574. return ("Unknown");
  575.     snprintf(xbstrerror_buf, BUFSIZ, "(%d) %s", err, strerror(err));
  576.     return xbstrerror_buf;
  577. }
  578. #endif
  579. void
  580. Tolower(char *q)
  581. {
  582.     char *s = q;
  583.     while (*s) {
  584. *s = tolower((unsigned char) *s);
  585. s++;
  586.     }
  587. }
  588. int
  589. tvSubMsec(struct timeval t1, struct timeval t2)
  590. {
  591.     return (t2.tv_sec - t1.tv_sec) * 1000 +
  592. (t2.tv_usec - t1.tv_usec) / 1000;
  593. }
  594. int
  595. tvSubUsec(struct timeval t1, struct timeval t2)
  596. {
  597.     return (t2.tv_sec - t1.tv_sec) * 1000000 +
  598. (t2.tv_usec - t1.tv_usec);
  599. }
  600. double
  601. tvSubDsec(struct timeval t1, struct timeval t2)
  602. {
  603.     return (double) (t2.tv_sec - t1.tv_sec) +
  604. (double) (t2.tv_usec - t1.tv_usec) / 1000000.0;
  605. }
  606. /*
  607.  *  xstrncpy() - similar to strncpy(3) but terminates string
  608.  *  always with '' if (n != 0 and dst != NULL), 
  609.  *  and doesn't do padding
  610.  */
  611. char *
  612. xstrncpy(char *dst, const char *src, size_t n)
  613. {
  614.     char *r = dst;
  615.     if (!n || !dst)
  616. return dst;
  617.     if (src)
  618. while (--n != 0 && *src != '')
  619.     *dst++ = *src++;
  620.     *dst = '';
  621.     return r;
  622. }
  623. /* returns the number of leading white spaces in str; handy in skipping ws */
  624. size_t
  625. xcountws(const char *str)
  626. {
  627.     size_t count = 0;
  628.     if (str) {
  629. while (xisspace(*str)) {
  630.     str++;
  631.     count++;
  632. }
  633.     }
  634.     return count;
  635. }
  636. /* somewhat safer calculation of %s */
  637. double
  638. xpercent(double part, double whole)
  639. {
  640.     return xdiv(100 * part, whole);
  641. }
  642. int
  643. xpercentInt(double part, double whole)
  644. {
  645. #if HAVE_RINT
  646.     return (int) rint(xpercent(part, whole));
  647. #else
  648.     /* SCO 3.2v4.2 doesn't have rint() -- mauri@mbp.ee */
  649.     return (int) floor(xpercent(part, whole)+0.5);
  650. #endif
  651. }
  652. /* somewhat safer division */
  653. double
  654. xdiv(double nom, double denom)
  655. {
  656.     return (denom != 0.0) ? nom / denom : -1.0;
  657. }
  658. /* integer to string */
  659. const char *
  660. xitoa(int num)
  661. {
  662.     static char buf[24]; /* 2^64 = 18446744073709551616 */
  663.     snprintf(buf, sizeof(buf), "%d", num);
  664.     return buf;
  665. }