110dd2532Schristos /*
210dd2532Schristos * Copyright (c) 1984 through 2008, William LeFebvre
310dd2532Schristos * All rights reserved.
410dd2532Schristos *
510dd2532Schristos * Redistribution and use in source and binary forms, with or without
610dd2532Schristos * modification, are permitted provided that the following conditions are met:
710dd2532Schristos *
810dd2532Schristos * * Redistributions of source code must retain the above copyright
910dd2532Schristos * notice, this list of conditions and the following disclaimer.
1010dd2532Schristos *
1110dd2532Schristos * * Redistributions in binary form must reproduce the above
1210dd2532Schristos * copyright notice, this list of conditions and the following disclaimer
1310dd2532Schristos * in the documentation and/or other materials provided with the
1410dd2532Schristos * distribution.
1510dd2532Schristos *
1610dd2532Schristos * * Neither the name of William LeFebvre nor the names of other
1710dd2532Schristos * contributors may be used to endorse or promote products derived from
1810dd2532Schristos * this software without specific prior written permission.
1910dd2532Schristos *
2010dd2532Schristos * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2110dd2532Schristos * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2210dd2532Schristos * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
2310dd2532Schristos * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2410dd2532Schristos * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2510dd2532Schristos * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2610dd2532Schristos * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2710dd2532Schristos * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2810dd2532Schristos * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2910dd2532Schristos * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
3010dd2532Schristos * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3110dd2532Schristos */
3210dd2532Schristos
3310dd2532Schristos /*
3410dd2532Schristos * Top users/processes display for Unix
3510dd2532Schristos * Version 3
3610dd2532Schristos */
3710dd2532Schristos
3810dd2532Schristos /*
3910dd2532Schristos * This file contains various handy utilities used by top.
4010dd2532Schristos */
4110dd2532Schristos
4210dd2532Schristos #include "os.h"
4310dd2532Schristos #include <ctype.h>
440ca1e3e0Sdholland #include <math.h>
4510dd2532Schristos #ifdef HAVE_STDARG_H
4610dd2532Schristos #include <stdarg.h>
4710dd2532Schristos #else
4810dd2532Schristos #undef DEBUG
4910dd2532Schristos #endif
5010dd2532Schristos #include "top.h"
5110dd2532Schristos #include "utils.h"
5210dd2532Schristos
5310dd2532Schristos static int
alldigits(char * s)5410dd2532Schristos alldigits(char *s)
5510dd2532Schristos
5610dd2532Schristos {
5710dd2532Schristos int ch;
5810dd2532Schristos
5910dd2532Schristos while ((ch = *s++) != '\0')
6010dd2532Schristos {
6110dd2532Schristos if (!isdigit(ch))
6210dd2532Schristos {
6310dd2532Schristos return 0;
6410dd2532Schristos }
6510dd2532Schristos }
6610dd2532Schristos return 1;
6710dd2532Schristos }
6810dd2532Schristos
6910dd2532Schristos int
atoiwi(char * str)7010dd2532Schristos atoiwi(char *str)
7110dd2532Schristos
7210dd2532Schristos {
7310dd2532Schristos register int len;
7410dd2532Schristos
7510dd2532Schristos len = strlen(str);
7610dd2532Schristos if (len != 0)
7710dd2532Schristos {
7810dd2532Schristos if (strncmp(str, "infinity", len) == 0 ||
7910dd2532Schristos strncmp(str, "all", len) == 0 ||
8010dd2532Schristos strncmp(str, "maximum", len) == 0)
8110dd2532Schristos {
8210dd2532Schristos return(Infinity);
8310dd2532Schristos }
8410dd2532Schristos else if (alldigits(str))
8510dd2532Schristos {
8610dd2532Schristos return(atoi(str));
8710dd2532Schristos }
8810dd2532Schristos else
8910dd2532Schristos {
9010dd2532Schristos return(Invalid);
9110dd2532Schristos }
9210dd2532Schristos }
9310dd2532Schristos return(0);
9410dd2532Schristos }
9510dd2532Schristos
9610dd2532Schristos /*
9710dd2532Schristos * itoa - convert integer (decimal) to ascii string for positive numbers
9810dd2532Schristos * only (we don't bother with negative numbers since we know we
9910dd2532Schristos * don't use them).
10010dd2532Schristos */
10110dd2532Schristos
10210dd2532Schristos /*
10310dd2532Schristos * How do we know that 16 will suffice?
10410dd2532Schristos * Because the biggest number that we will
10510dd2532Schristos * ever convert will be 2^32-1, which is 10
10610dd2532Schristos * digits.
10710dd2532Schristos */
10810dd2532Schristos
10910dd2532Schristos char *
itoa(int val)11010dd2532Schristos itoa(int val)
11110dd2532Schristos
11210dd2532Schristos {
11310dd2532Schristos register char *ptr;
11410dd2532Schristos static char buffer[16]; /* result is built here */
11510dd2532Schristos /* 16 is sufficient since the largest number
11610dd2532Schristos we will ever convert will be 2^32-1,
11710dd2532Schristos which is 10 digits. */
11810dd2532Schristos
11910dd2532Schristos ptr = buffer + sizeof(buffer);
12010dd2532Schristos *--ptr = '\0';
12110dd2532Schristos if (val == 0)
12210dd2532Schristos {
12310dd2532Schristos *--ptr = '0';
12410dd2532Schristos }
12510dd2532Schristos else while (val != 0)
12610dd2532Schristos {
12710dd2532Schristos *--ptr = (val % 10) + '0';
12810dd2532Schristos val /= 10;
12910dd2532Schristos }
13010dd2532Schristos return(ptr);
13110dd2532Schristos }
13210dd2532Schristos
13310dd2532Schristos /*
13410dd2532Schristos * itoa7(val) - like itoa, except the number is right justified in a 7
13510dd2532Schristos * character field. This code is a duplication of itoa instead of
13610dd2532Schristos * a front end to a more general routine for efficiency.
13710dd2532Schristos */
13810dd2532Schristos
13910dd2532Schristos char *
itoa_w(int val,int w)14010dd2532Schristos itoa_w(int val, int w)
14110dd2532Schristos
14210dd2532Schristos {
14310dd2532Schristos char *ptr;
14410dd2532Schristos char *eptr;
14510dd2532Schristos static char buffer[16]; /* result is built here */
14610dd2532Schristos /* 16 is sufficient since the largest number
14710dd2532Schristos we will ever convert will be 2^32-1,
14810dd2532Schristos which is 10 digits. */
14910dd2532Schristos
15010dd2532Schristos if (w > 15)
15110dd2532Schristos {
15210dd2532Schristos w = 15;
15310dd2532Schristos }
15410dd2532Schristos eptr = ptr = buffer + sizeof(buffer);
15510dd2532Schristos *--ptr = '\0';
15610dd2532Schristos if (val == 0)
15710dd2532Schristos {
15810dd2532Schristos *--ptr = '0';
15910dd2532Schristos }
16010dd2532Schristos else while (val != 0)
16110dd2532Schristos {
16210dd2532Schristos *--ptr = (val % 10) + '0';
16310dd2532Schristos val /= 10;
16410dd2532Schristos }
16510dd2532Schristos while (ptr >= eptr - w)
16610dd2532Schristos {
16710dd2532Schristos *--ptr = ' ';
16810dd2532Schristos }
16910dd2532Schristos return(ptr);
17010dd2532Schristos }
17110dd2532Schristos
17210dd2532Schristos char *
itoa7(int val)17310dd2532Schristos itoa7(int val)
17410dd2532Schristos
17510dd2532Schristos {
17610dd2532Schristos return itoa_w(val, 7);
17710dd2532Schristos }
17810dd2532Schristos
17910dd2532Schristos /*
18010dd2532Schristos * digits(val) - return number of decimal digits in val. Only works for
18110dd2532Schristos * positive numbers. If val < 0 then digits(val) == 0, but
18210dd2532Schristos * digits(0) == 1.
18310dd2532Schristos */
18410dd2532Schristos
18510dd2532Schristos int
digits(int val)18610dd2532Schristos digits(int val)
18710dd2532Schristos
18810dd2532Schristos {
18910dd2532Schristos register int cnt = 0;
19010dd2532Schristos
19110dd2532Schristos if (val == 0)
19210dd2532Schristos {
19310dd2532Schristos return 1;
19410dd2532Schristos }
19510dd2532Schristos while (val > 0)
19610dd2532Schristos {
19710dd2532Schristos cnt++;
19810dd2532Schristos val /= 10;
19910dd2532Schristos }
20010dd2532Schristos return(cnt);
20110dd2532Schristos }
20210dd2532Schristos
20310dd2532Schristos /*
20410dd2532Schristos * printable(char *str) - make the string pointed to by "str" into one that is
20510dd2532Schristos * printable (i.e.: all ascii), by converting all non-printable
20610dd2532Schristos * characters into '?'. Replacements are done in place and a pointer
20710dd2532Schristos * to the original buffer is returned.
20810dd2532Schristos */
20910dd2532Schristos
21010dd2532Schristos char *
printable(char * str)21110dd2532Schristos printable(char *str)
21210dd2532Schristos
21310dd2532Schristos {
21410dd2532Schristos register char *ptr;
21510dd2532Schristos register int ch;
21610dd2532Schristos
21710dd2532Schristos ptr = str;
21810dd2532Schristos while ((ch = *ptr) != '\0')
21910dd2532Schristos {
22010dd2532Schristos if (!isprint(ch))
22110dd2532Schristos {
22210dd2532Schristos *ptr = '?';
22310dd2532Schristos }
22410dd2532Schristos ptr++;
22510dd2532Schristos }
22610dd2532Schristos return(str);
22710dd2532Schristos }
22810dd2532Schristos
22910dd2532Schristos /*
23010dd2532Schristos * strcpyend(to, from) - copy string "from" into "to" and return a pointer
23110dd2532Schristos * to the END of the string "to".
23210dd2532Schristos */
23310dd2532Schristos
23410dd2532Schristos char *
strcpyend(char * to,const char * from)235213e7ef1Schristos strcpyend(char *to, const char *from)
23610dd2532Schristos
23710dd2532Schristos {
23810dd2532Schristos while ((*to++ = *from++) != '\0');
23910dd2532Schristos return(--to);
24010dd2532Schristos }
24110dd2532Schristos
24210dd2532Schristos /*
24310dd2532Schristos * char *
244213e7ef1Schristos * homogenize(const char *str)
24510dd2532Schristos *
24610dd2532Schristos * Remove unwanted characters from "str" and make everything lower case.
24710dd2532Schristos * Newly allocated string is returned: the original is not altered.
24810dd2532Schristos */
24910dd2532Schristos
homogenize(const char * str)250213e7ef1Schristos char *homogenize(const char *str)
25110dd2532Schristos
25210dd2532Schristos {
25310dd2532Schristos char *ans;
25410dd2532Schristos char *fr;
25510dd2532Schristos char *to;
25610dd2532Schristos int ch;
25710dd2532Schristos
25856a682bfSchristos to = fr = ans = estrdup(str);
25910dd2532Schristos while ((ch = *fr++) != '\0')
26010dd2532Schristos {
26110dd2532Schristos if (isalnum(ch))
26210dd2532Schristos {
26310dd2532Schristos *to++ = tolower(ch);
26410dd2532Schristos }
26510dd2532Schristos }
26610dd2532Schristos
26710dd2532Schristos *to = '\0';
26810dd2532Schristos return ans;
26910dd2532Schristos }
27010dd2532Schristos
27110dd2532Schristos /*
27210dd2532Schristos * string_index(string, array) - find string in array and return index
27310dd2532Schristos */
27410dd2532Schristos
27510dd2532Schristos int
string_index(const char * string,const char ** array)276213e7ef1Schristos string_index(const char *string, const char **array)
27710dd2532Schristos
27810dd2532Schristos {
27910dd2532Schristos register int i = 0;
28010dd2532Schristos
28110dd2532Schristos while (*array != NULL)
28210dd2532Schristos {
28310dd2532Schristos if (strcmp(string, *array) == 0)
28410dd2532Schristos {
28510dd2532Schristos return(i);
28610dd2532Schristos }
28710dd2532Schristos array++;
28810dd2532Schristos i++;
28910dd2532Schristos }
29010dd2532Schristos return(-1);
29110dd2532Schristos }
29210dd2532Schristos
29310dd2532Schristos /*
29410dd2532Schristos * char *string_list(char **strings)
29510dd2532Schristos *
29610dd2532Schristos * Create a comma-separated list of the strings in the NULL-terminated
29710dd2532Schristos * "strings". Returned string is malloc-ed and should be freed when the
29810dd2532Schristos * caller is done. Note that this is not an efficient function.
29910dd2532Schristos */
30010dd2532Schristos
string_list(const char ** strings)301213e7ef1Schristos char *string_list(const char **strings)
30210dd2532Schristos
30310dd2532Schristos {
30410dd2532Schristos int cnt = 0;
305213e7ef1Schristos const char **pp;
306213e7ef1Schristos const char *p;
30710dd2532Schristos char *result = NULL;
30810dd2532Schristos char *resp = NULL;
30910dd2532Schristos
31010dd2532Schristos pp = strings;
31110dd2532Schristos while ((p = *pp++) != NULL)
31210dd2532Schristos {
31310dd2532Schristos cnt += strlen(p) + 2;
31410dd2532Schristos }
31510dd2532Schristos
31610dd2532Schristos if (cnt > 0)
31710dd2532Schristos {
31856a682bfSchristos resp = result = emalloc(cnt);
31910dd2532Schristos pp = strings;
32010dd2532Schristos while ((p = *pp++) != NULL)
32110dd2532Schristos {
32210dd2532Schristos resp = strcpyend(resp, p);
32310dd2532Schristos if (*pp != NULL)
32410dd2532Schristos {
32510dd2532Schristos resp = strcpyend(resp, ", ");
32610dd2532Schristos }
32710dd2532Schristos }
32810dd2532Schristos }
32910dd2532Schristos
33010dd2532Schristos return result;
33110dd2532Schristos }
33210dd2532Schristos
33310dd2532Schristos /*
33410dd2532Schristos * argparse(line, cntp) - parse arguments in string "line", separating them
33510dd2532Schristos * out into an argv-like array, and setting *cntp to the number of
33610dd2532Schristos * arguments encountered. This is a simple parser that doesn't understand
33710dd2532Schristos * squat about quotes.
33810dd2532Schristos */
33910dd2532Schristos
34010dd2532Schristos char **
argparse(char * line,int * cntp)34110dd2532Schristos argparse(char *line, int *cntp)
34210dd2532Schristos
34310dd2532Schristos {
34410dd2532Schristos register char *from;
34510dd2532Schristos register char *to;
34610dd2532Schristos register int cnt;
34710dd2532Schristos register int ch;
34810dd2532Schristos int length;
34910dd2532Schristos int lastch;
35010dd2532Schristos register char **argv;
35110dd2532Schristos char **argarray;
35210dd2532Schristos char *args;
35310dd2532Schristos
35410dd2532Schristos /* unfortunately, the only real way to do this is to go thru the
35510dd2532Schristos input string twice. */
35610dd2532Schristos
35710dd2532Schristos /* step thru the string counting the white space sections */
35810dd2532Schristos from = line;
35910dd2532Schristos lastch = cnt = length = 0;
36010dd2532Schristos while ((ch = *from++) != '\0')
36110dd2532Schristos {
36210dd2532Schristos length++;
36310dd2532Schristos if (ch == ' ' && lastch != ' ')
36410dd2532Schristos {
36510dd2532Schristos cnt++;
36610dd2532Schristos }
36710dd2532Schristos lastch = ch;
36810dd2532Schristos }
36910dd2532Schristos
37010dd2532Schristos /* add three to the count: one for the initial "dummy" argument,
37110dd2532Schristos one for the last argument and one for NULL */
37210dd2532Schristos cnt += 3;
37310dd2532Schristos
37410dd2532Schristos /* allocate a char * array to hold the pointers */
37556a682bfSchristos argarray = emalloc(cnt * sizeof(char *));
37610dd2532Schristos
37710dd2532Schristos /* allocate another array to hold the strings themselves */
37856a682bfSchristos args = emalloc(length+2);
37910dd2532Schristos
38010dd2532Schristos /* initialization for main loop */
38110dd2532Schristos from = line;
38210dd2532Schristos to = args;
38310dd2532Schristos argv = argarray;
38410dd2532Schristos lastch = '\0';
38510dd2532Schristos
38610dd2532Schristos /* create a dummy argument to keep getopt happy */
38710dd2532Schristos *argv++ = to;
38810dd2532Schristos *to++ = '\0';
38910dd2532Schristos cnt = 2;
39010dd2532Schristos
39110dd2532Schristos /* now build argv while copying characters */
39210dd2532Schristos *argv++ = to;
39310dd2532Schristos while ((ch = *from++) != '\0')
39410dd2532Schristos {
39510dd2532Schristos if (ch != ' ')
39610dd2532Schristos {
39710dd2532Schristos if (lastch == ' ')
39810dd2532Schristos {
39910dd2532Schristos *to++ = '\0';
40010dd2532Schristos *argv++ = to;
40110dd2532Schristos cnt++;
40210dd2532Schristos }
40310dd2532Schristos *to++ = ch;
40410dd2532Schristos }
40510dd2532Schristos lastch = ch;
40610dd2532Schristos }
40710dd2532Schristos *to++ = '\0';
40810dd2532Schristos
40910dd2532Schristos /* set cntp and return the allocated array */
41010dd2532Schristos *cntp = cnt;
41110dd2532Schristos return(argarray);
41210dd2532Schristos }
41310dd2532Schristos
41410dd2532Schristos /*
41510dd2532Schristos * percentages(cnt, out, new, old, diffs) - calculate percentage change
41610dd2532Schristos * between array "old" and "new", putting the percentages i "out".
41710dd2532Schristos * "cnt" is size of each array and "diffs" is used for scratch space.
41810dd2532Schristos * The array "old" is updated on each call.
41910dd2532Schristos * The routine assumes modulo arithmetic. This function is especially
42010dd2532Schristos * useful on BSD mchines for calculating cpu state percentages.
42110dd2532Schristos */
42210dd2532Schristos
42310dd2532Schristos long
percentages(int cnt,int * out,long * new,long * old,long * diffs)42410dd2532Schristos percentages(int cnt, int *out, long *new, long *old, long *diffs)
42510dd2532Schristos
42610dd2532Schristos {
42710dd2532Schristos register int i;
42810dd2532Schristos register long change;
42910dd2532Schristos register long total_change;
43010dd2532Schristos register long *dp;
43110dd2532Schristos long half_total;
43210dd2532Schristos
43310dd2532Schristos /* initialization */
43410dd2532Schristos total_change = 0;
43510dd2532Schristos dp = diffs;
43610dd2532Schristos
43710dd2532Schristos /* calculate changes for each state and the overall change */
43810dd2532Schristos for (i = 0; i < cnt; i++)
43910dd2532Schristos {
44010dd2532Schristos if ((change = *new - *old) < 0)
44110dd2532Schristos {
44210dd2532Schristos /* this only happens when the counter wraps */
44310dd2532Schristos change = (int)
44410dd2532Schristos ((unsigned long)*new-(unsigned long)*old);
44510dd2532Schristos }
44610dd2532Schristos total_change += (*dp++ = change);
44710dd2532Schristos *old++ = *new++;
44810dd2532Schristos }
44910dd2532Schristos
45010dd2532Schristos /* avoid divide by zero potential */
45110dd2532Schristos if (total_change == 0)
45210dd2532Schristos {
45310dd2532Schristos total_change = 1;
45410dd2532Schristos }
45510dd2532Schristos
45610dd2532Schristos /* calculate percentages based on overall change, rounding up */
45710dd2532Schristos half_total = total_change / 2l;
45810dd2532Schristos for (i = 0; i < cnt; i++)
45910dd2532Schristos {
46010dd2532Schristos *out++ = (int)((*diffs++ * 1000 + half_total) / total_change);
46110dd2532Schristos }
46210dd2532Schristos
46310dd2532Schristos /* return the total in case the caller wants to use it */
46410dd2532Schristos return(total_change);
46510dd2532Schristos }
46610dd2532Schristos
46710dd2532Schristos /*
46810dd2532Schristos * errmsg(errnum) - return an error message string appropriate to the
46910dd2532Schristos * error number "errnum". This is a substitute for the System V
47010dd2532Schristos * function "strerror". There appears to be no reliable way to
47110dd2532Schristos * determine if "strerror" exists at compile time, so I make do
47210dd2532Schristos * by providing something of similar functionality. For those
47310dd2532Schristos * systems that have strerror and NOT errlist, define
47410dd2532Schristos * -DHAVE_STRERROR in the module file and this function will
47510dd2532Schristos * use strerror.
47610dd2532Schristos */
47710dd2532Schristos
47810dd2532Schristos /* externs referenced by errmsg */
47910dd2532Schristos
48010dd2532Schristos #ifndef HAVE_STRERROR
48110dd2532Schristos #if !HAVE_DECL_SYS_ERRLIST
48210dd2532Schristos extern char *sys_errlist[];
48310dd2532Schristos #endif
48410dd2532Schristos
48510dd2532Schristos extern int sys_nerr;
48610dd2532Schristos #endif
48710dd2532Schristos
488213e7ef1Schristos const char *
errmsg(int errnum)48910dd2532Schristos errmsg(int errnum)
49010dd2532Schristos
49110dd2532Schristos {
49210dd2532Schristos #ifdef HAVE_STRERROR
49310dd2532Schristos char *msg = strerror(errnum);
49410dd2532Schristos if (msg != NULL)
49510dd2532Schristos {
49610dd2532Schristos return msg;
49710dd2532Schristos }
49810dd2532Schristos #else
49910dd2532Schristos if (errnum > 0 && errnum < sys_nerr)
50010dd2532Schristos {
50110dd2532Schristos return((char *)(sys_errlist[errnum]));
50210dd2532Schristos }
50310dd2532Schristos #endif
50410dd2532Schristos return("No error");
50510dd2532Schristos }
50610dd2532Schristos
50710dd2532Schristos /* format_percent(v) - format a double as a percentage in a manner that
50810dd2532Schristos * does not exceed 5 characters (excluding any trailing
50910dd2532Schristos * percent sign). Since it is possible for the value
51010dd2532Schristos * to exceed 100%, we format such values with no fractional
51110dd2532Schristos * component to fit within the 5 characters.
51210dd2532Schristos */
51310dd2532Schristos
51410dd2532Schristos char *
format_percent(double v)51510dd2532Schristos format_percent(double v)
51610dd2532Schristos
51710dd2532Schristos {
51810dd2532Schristos static char result[10];
51910dd2532Schristos
52010dd2532Schristos /* enumerate the possibilities */
52110dd2532Schristos if (v < 0 || v >= 100000.)
52210dd2532Schristos {
52310dd2532Schristos /* we dont want to try extreme values */
52410dd2532Schristos strcpy(result, " ???");
52510dd2532Schristos }
52610dd2532Schristos else if (v > 99.99)
52710dd2532Schristos {
52810dd2532Schristos sprintf(result, "%5.0f", v);
52910dd2532Schristos }
53010dd2532Schristos else
53110dd2532Schristos {
53210dd2532Schristos sprintf(result, "%5.2f", v);
53310dd2532Schristos }
53410dd2532Schristos
53510dd2532Schristos return result;
53610dd2532Schristos }
53710dd2532Schristos
53810dd2532Schristos /* format_time(seconds) - format number of seconds into a suitable
53910dd2532Schristos * display that will fit within 6 characters. Note that this
54010dd2532Schristos * routine builds its string in a static area. If it needs
54110dd2532Schristos * to be called more than once without overwriting previous data,
54210dd2532Schristos * then we will need to adopt a technique similar to the
54310dd2532Schristos * one used for format_k.
54410dd2532Schristos */
54510dd2532Schristos
54610dd2532Schristos /* Explanation:
54710dd2532Schristos We want to keep the output within 6 characters. For low values we use
54810dd2532Schristos the format mm:ss. For values that exceed 999:59, we switch to a format
54910dd2532Schristos that displays hours and fractions: hhh.tH. For values that exceed
55010dd2532Schristos 999.9, we use hhhh.t and drop the "H" designator. For values that
55110dd2532Schristos exceed 9999.9, we use "???".
55210dd2532Schristos */
55310dd2532Schristos
55410dd2532Schristos char *
format_time(long seconds)55510dd2532Schristos format_time(long seconds)
55610dd2532Schristos
55710dd2532Schristos {
55810dd2532Schristos static char result[10];
55910dd2532Schristos
56010dd2532Schristos /* sanity protection */
56110dd2532Schristos if (seconds < 0 || seconds > (99999l * 360l))
56210dd2532Schristos {
56310dd2532Schristos strcpy(result, " ???");
56410dd2532Schristos }
56510dd2532Schristos else if (seconds >= (1000l * 60l))
56610dd2532Schristos {
56710dd2532Schristos /* alternate (slow) method displaying hours and tenths */
56810dd2532Schristos sprintf(result, "%5.1fH", (double)seconds / (double)(60l * 60l));
56910dd2532Schristos
57010dd2532Schristos /* It is possible that the sprintf took more than 6 characters.
57110dd2532Schristos If so, then the "H" appears as result[6]. If not, then there
57210dd2532Schristos is a \0 in result[6]. Either way, it is safe to step on.
57310dd2532Schristos */
57410dd2532Schristos result[6] = '\0';
57510dd2532Schristos }
57610dd2532Schristos else
57710dd2532Schristos {
57810dd2532Schristos /* standard method produces MMM:SS */
57910dd2532Schristos /* we avoid printf as must as possible to make this quick */
58010dd2532Schristos sprintf(result, "%3ld:%02ld", seconds / 60l, seconds % 60l);
58110dd2532Schristos }
58210dd2532Schristos return(result);
58310dd2532Schristos }
58410dd2532Schristos
58510dd2532Schristos /*
58610dd2532Schristos * format_k(amt) - format a kilobyte memory value, returning a string
58710dd2532Schristos * suitable for display. Returns a pointer to a static
58810dd2532Schristos * area that changes each call. "amt" is converted to a
58910dd2532Schristos * string with a trailing "K". If "amt" is 10000 or greater,
59010dd2532Schristos * then it is formatted as megabytes (rounded) with a
59110dd2532Schristos * trailing "M".
59210dd2532Schristos */
59310dd2532Schristos
59410dd2532Schristos /*
59510dd2532Schristos * Compromise time. We need to return a string, but we don't want the
59610dd2532Schristos * caller to have to worry about freeing a dynamically allocated string.
59710dd2532Schristos * Unfortunately, we can't just return a pointer to a static area as one
59810dd2532Schristos * of the common uses of this function is in a large call to sprintf where
59910dd2532Schristos * it might get invoked several times. Our compromise is to maintain an
60010dd2532Schristos * array of strings and cycle thru them with each invocation. We make the
60110dd2532Schristos * array large enough to handle the above mentioned case. The constant
60210dd2532Schristos * NUM_STRINGS defines the number of strings in this array: we can tolerate
60310dd2532Schristos * up to NUM_STRINGS calls before we start overwriting old information.
60410dd2532Schristos * Keeping NUM_STRINGS a power of two will allow an intelligent optimizer
60510dd2532Schristos * to convert the modulo operation into something quicker. What a hack!
60610dd2532Schristos */
60710dd2532Schristos
60810dd2532Schristos #define NUM_STRINGS 8
60910dd2532Schristos
61010dd2532Schristos char *
format_k(long amt)61110dd2532Schristos format_k(long amt)
61210dd2532Schristos
61310dd2532Schristos {
614*9ded70a5Smrg static char retarray[NUM_STRINGS][24];
615213e7ef1Schristos static int idx = 0;
61610dd2532Schristos register char *ret;
61710dd2532Schristos register char tag = 'K';
61810dd2532Schristos
619213e7ef1Schristos ret = retarray[idx];
620213e7ef1Schristos idx = (idx + 1) % NUM_STRINGS;
62110dd2532Schristos
62210dd2532Schristos if (amt >= 10000)
62310dd2532Schristos {
62410dd2532Schristos amt = (amt + 512) / 1024;
62510dd2532Schristos tag = 'M';
62610dd2532Schristos if (amt >= 10000)
62710dd2532Schristos {
62810dd2532Schristos amt = (amt + 512) / 1024;
62910dd2532Schristos tag = 'G';
63010dd2532Schristos }
63110dd2532Schristos }
63210dd2532Schristos
633213e7ef1Schristos snprintf(ret, sizeof(retarray[idx])-1, "%ld%c", amt, tag);
63410dd2532Schristos
63510dd2532Schristos return(ret);
63610dd2532Schristos }
63710dd2532Schristos
63810dd2532Schristos /*
63910dd2532Schristos * Time keeping functions.
64010dd2532Schristos */
64110dd2532Schristos
64210dd2532Schristos static struct timeval lasttime = { 0, 0 };
64310dd2532Schristos static unsigned int elapsed_msecs = 0;
64410dd2532Schristos
64510dd2532Schristos void
time_get(struct timeval * tv)64610dd2532Schristos time_get(struct timeval *tv)
64710dd2532Schristos
64810dd2532Schristos {
64910dd2532Schristos /* get the current time */
65010dd2532Schristos #ifdef HAVE_GETTIMEOFDAY
65110dd2532Schristos gettimeofday(tv, NULL);
65210dd2532Schristos #else
65310dd2532Schristos tv->tv_sec = (long)time(NULL);
65410dd2532Schristos tv->tv_usec = 0;
65510dd2532Schristos #endif
65610dd2532Schristos }
65710dd2532Schristos
65810dd2532Schristos void
time_mark(struct timeval * tv)65910dd2532Schristos time_mark(struct timeval *tv)
66010dd2532Schristos
66110dd2532Schristos {
66210dd2532Schristos struct timeval thistime;
66310dd2532Schristos struct timeval timediff;
66410dd2532Schristos
66510dd2532Schristos /* if the caller didnt provide one then use our own */
66610dd2532Schristos if (tv == NULL)
66710dd2532Schristos {
66810dd2532Schristos tv = &thistime;
66910dd2532Schristos }
67010dd2532Schristos
67110dd2532Schristos /* get the current time */
67210dd2532Schristos #ifdef HAVE_GETTIMEOFDAY
67310dd2532Schristos gettimeofday(tv, NULL);
67410dd2532Schristos #else
67510dd2532Schristos tv->tv_sec = (long)time(NULL);
67610dd2532Schristos tv->tv_usec = 0;
67710dd2532Schristos #endif
67810dd2532Schristos
67910dd2532Schristos /* calculate the difference */
68010dd2532Schristos timediff.tv_sec = tv->tv_sec - lasttime.tv_sec;
68110dd2532Schristos timediff.tv_usec = tv->tv_usec - lasttime.tv_usec;
68210dd2532Schristos if (timediff.tv_usec < 0) {
68310dd2532Schristos timediff.tv_sec--;
68410dd2532Schristos timediff.tv_usec += 1000000;
68510dd2532Schristos }
68610dd2532Schristos
68710dd2532Schristos /* convert to milliseconds */
68810dd2532Schristos elapsed_msecs = timediff.tv_sec * 1000 + timediff.tv_usec / 1000;
68910dd2532Schristos if (elapsed_msecs == 0)
69010dd2532Schristos {
69110dd2532Schristos elapsed_msecs = 1;
69210dd2532Schristos }
69310dd2532Schristos
69410dd2532Schristos /* save for next time */
69510dd2532Schristos lasttime = *tv;
69610dd2532Schristos }
69710dd2532Schristos
69810dd2532Schristos unsigned int
time_elapsed()69910dd2532Schristos time_elapsed()
70010dd2532Schristos
70110dd2532Schristos {
70210dd2532Schristos return elapsed_msecs;
70310dd2532Schristos }
70410dd2532Schristos
70510dd2532Schristos unsigned int
diff_per_second(unsigned int x,unsigned int y)70610dd2532Schristos diff_per_second(unsigned int x, unsigned int y)
70710dd2532Schristos
70810dd2532Schristos {
70910dd2532Schristos return (y > x ? UINT_MAX - y + x + 1 : x - y) * 1000 / elapsed_msecs;
71010dd2532Schristos }
71110dd2532Schristos
712a8f5847fSchristos void
double2tv(struct timeval * tv,double d)713a8f5847fSchristos double2tv(struct timeval *tv, double d)
714a8f5847fSchristos {
7150ca1e3e0Sdholland double di;
7160ca1e3e0Sdholland
7170ca1e3e0Sdholland di = floor(d);
7180ca1e3e0Sdholland tv->tv_sec = (time_t)di;
7190ca1e3e0Sdholland tv->tv_usec = (int)ceil((d - di) * 1000000.0);
720a8f5847fSchristos }
721a8f5847fSchristos
72210dd2532Schristos static int debug_on = 0;
72310dd2532Schristos
72410dd2532Schristos #ifdef DEBUG
72510dd2532Schristos FILE *debugfile;
72610dd2532Schristos #endif
72710dd2532Schristos
72810dd2532Schristos void
debug_set(int i)72910dd2532Schristos debug_set(int i)
73010dd2532Schristos
73110dd2532Schristos {
73210dd2532Schristos debug_on = i;
73310dd2532Schristos #ifdef DEBUG
73410dd2532Schristos debugfile = fopen("/tmp/top.debug", "w");
73510dd2532Schristos #endif
73610dd2532Schristos }
73710dd2532Schristos
73810dd2532Schristos #ifdef DEBUG
73910dd2532Schristos void
xdprintf(char * fmt,...)74010dd2532Schristos xdprintf(char *fmt, ...)
74110dd2532Schristos
74210dd2532Schristos {
74310dd2532Schristos va_list argp;
74410dd2532Schristos
74510dd2532Schristos va_start(argp, fmt);
74610dd2532Schristos
74710dd2532Schristos if (debug_on)
74810dd2532Schristos {
74910dd2532Schristos vfprintf(debugfile, fmt, argp);
75010dd2532Schristos fflush(debugfile);
75110dd2532Schristos }
75210dd2532Schristos
75310dd2532Schristos va_end(argp);
75410dd2532Schristos }
75510dd2532Schristos #endif
75610dd2532Schristos
757