1/*
2 *  Top users/processes display for Unix
3 *  Version 3
4 *
5 *  This program may be freely redistributed,
6 *  but this entire comment MUST remain intact.
7 *
8 *  Copyright (c) 1984, 1989, William LeFebvre, Rice University
9 *  Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
10 *
11 * $FreeBSD$
12 */
13
14/*
15 *  This file contains various handy utilities used by top.
16 */
17
18#include "top.h"
19#include "os.h"
20
21int atoiwi(str)
22
23char *str;
24
25{
26    register int len;
27
28    len = strlen(str);
29    if (len != 0)
30    {
31	if (strncmp(str, "infinity", len) == 0 ||
32	    strncmp(str, "all",      len) == 0 ||
33	    strncmp(str, "maximum",  len) == 0)
34	{
35	    return(Infinity);
36	}
37	else if (str[0] == '-')
38	{
39	    return(Invalid);
40	}
41	else
42	{
43	    return(atoi(str));
44	}
45    }
46    return(0);
47}
48
49/*
50 *  itoa - convert integer (decimal) to ascii string for positive numbers
51 *  	   only (we don't bother with negative numbers since we know we
52 *	   don't use them).
53 */
54
55				/*
56				 * How do we know that 16 will suffice?
57				 * Because the biggest number that we will
58				 * ever convert will be 2^32-1, which is 10
59				 * digits.
60				 */
61
62char *itoa(val)
63
64register int val;
65
66{
67    register char *ptr;
68    static char buffer[16];	/* result is built here */
69    				/* 16 is sufficient since the largest number
70				   we will ever convert will be 2^32-1,
71				   which is 10 digits. */
72
73    ptr = buffer + sizeof(buffer);
74    *--ptr = '\0';
75    if (val == 0)
76    {
77	*--ptr = '0';
78    }
79    else while (val != 0)
80    {
81	*--ptr = (val % 10) + '0';
82	val /= 10;
83    }
84    return(ptr);
85}
86
87/*
88 *  itoa7(val) - like itoa, except the number is right justified in a 7
89 *	character field.  This code is a duplication of itoa instead of
90 *	a front end to a more general routine for efficiency.
91 */
92
93char *itoa7(val)
94
95register int val;
96
97{
98    register char *ptr;
99    static char buffer[16];	/* result is built here */
100    				/* 16 is sufficient since the largest number
101				   we will ever convert will be 2^32-1,
102				   which is 10 digits. */
103
104    ptr = buffer + sizeof(buffer);
105    *--ptr = '\0';
106    if (val == 0)
107    {
108	*--ptr = '0';
109    }
110    else while (val != 0)
111    {
112	*--ptr = (val % 10) + '0';
113	val /= 10;
114    }
115    while (ptr > buffer + sizeof(buffer) - 7)
116    {
117	*--ptr = ' ';
118    }
119    return(ptr);
120}
121
122/*
123 *  digits(val) - return number of decimal digits in val.  Only works for
124 *	positive numbers.  If val <= 0 then digits(val) == 0.
125 */
126
127int digits(val)
128
129int val;
130
131{
132    register int cnt = 0;
133
134    while (val > 0)
135    {
136	cnt++;
137	val /= 10;
138    }
139    return(cnt);
140}
141
142/*
143 *  strecpy(to, from) - copy string "from" into "to" and return a pointer
144 *	to the END of the string "to".
145 */
146
147char *strecpy(to, from)
148
149register char *to;
150register char *from;
151
152{
153    while ((*to++ = *from++) != '\0');
154    return(--to);
155}
156
157/*
158 * string_index(string, array) - find string in array and return index
159 */
160
161int string_index(string, array)
162
163char *string;
164char **array;
165
166{
167    register int i = 0;
168
169    while (*array != NULL)
170    {
171	if (strcmp(string, *array) == 0)
172	{
173	    return(i);
174	}
175	array++;
176	i++;
177    }
178    return(-1);
179}
180
181/*
182 * argparse(line, cntp) - parse arguments in string "line", separating them
183 *	out into an argv-like array, and setting *cntp to the number of
184 *	arguments encountered.  This is a simple parser that doesn't understand
185 *	squat about quotes.
186 */
187
188char **argparse(line, cntp)
189
190char *line;
191int *cntp;
192
193{
194    register char *from;
195    register char *to;
196    register int cnt;
197    register int ch;
198    int length;
199    int lastch;
200    register char **argv;
201    char **argarray;
202    char *args;
203
204    /* unfortunately, the only real way to do this is to go thru the
205       input string twice. */
206
207    /* step thru the string counting the white space sections */
208    from = line;
209    lastch = cnt = length = 0;
210    while ((ch = *from++) != '\0')
211    {
212	length++;
213	if (ch == ' ' && lastch != ' ')
214	{
215	    cnt++;
216	}
217	lastch = ch;
218    }
219
220    /* add three to the count:  one for the initial "dummy" argument,
221       one for the last argument and one for NULL */
222    cnt += 3;
223
224    /* allocate a char * array to hold the pointers */
225    argarray = (char **)malloc(cnt * sizeof(char *));
226
227    /* allocate another array to hold the strings themselves */
228    args = (char *)malloc(length+2);
229
230    /* initialization for main loop */
231    from = line;
232    to = args;
233    argv = argarray;
234    lastch = '\0';
235
236    /* create a dummy argument to keep getopt happy */
237    *argv++ = to;
238    *to++ = '\0';
239    cnt = 2;
240
241    /* now build argv while copying characters */
242    *argv++ = to;
243    while ((ch = *from++) != '\0')
244    {
245	if (ch != ' ')
246	{
247	    if (lastch == ' ')
248	    {
249		*to++ = '\0';
250		*argv++ = to;
251		cnt++;
252	    }
253	    *to++ = ch;
254	}
255	lastch = ch;
256    }
257    *to++ = '\0';
258
259    /* set cntp and return the allocated array */
260    *cntp = cnt;
261    return(argarray);
262}
263
264/*
265 *  percentages(cnt, out, new, old, diffs) - calculate percentage change
266 *	between array "old" and "new", putting the percentages i "out".
267 *	"cnt" is size of each array and "diffs" is used for scratch space.
268 *	The array "old" is updated on each call.
269 *	The routine assumes modulo arithmetic.  This function is especially
270 *	useful on BSD mchines for calculating cpu state percentages.
271 */
272
273long percentages(cnt, out, new, old, diffs)
274
275int cnt;
276int *out;
277register long *new;
278register long *old;
279long *diffs;
280
281{
282    register int i;
283    register long change;
284    register long total_change;
285    register long *dp;
286    long half_total;
287
288    /* initialization */
289    total_change = 0;
290    dp = diffs;
291
292    /* calculate changes for each state and the overall change */
293    for (i = 0; i < cnt; i++)
294    {
295	if ((change = *new - *old) < 0)
296	{
297	    /* this only happens when the counter wraps */
298	    change = (int)
299		((unsigned long)*new-(unsigned long)*old);
300	}
301	total_change += (*dp++ = change);
302	*old++ = *new++;
303    }
304
305    /* avoid divide by zero potential */
306    if (total_change == 0)
307    {
308	total_change = 1;
309    }
310
311    /* calculate percentages based on overall change, rounding up */
312    half_total = total_change / 2l;
313
314    /* Do not divide by 0. Causes Floating point exception */
315    if(total_change) {
316        for (i = 0; i < cnt; i++)
317        {
318          *out++ = (int)((*diffs++ * 1000 + half_total) / total_change);
319        }
320    }
321
322    /* return the total in case the caller wants to use it */
323    return(total_change);
324}
325
326/*
327 * errmsg(errnum) - return an error message string appropriate to the
328 *           error number "errnum".  This is a substitute for the System V
329 *           function "strerror".  There appears to be no reliable way to
330 *           determine if "strerror" exists at compile time, so I make do
331 *           by providing something of similar functionality.  For those
332 *           systems that have strerror and NOT errlist, define
333 *           -DHAVE_STRERROR in the module file and this function will
334 *           use strerror.
335 */
336
337/* externs referenced by errmsg */
338
339#ifndef HAVE_STRERROR
340#ifndef SYS_ERRLIST_DECLARED
341#define SYS_ERRLIST_DECLARED
342extern char *sys_errlist[];
343#endif
344
345extern int sys_nerr;
346#endif
347
348char *errmsg(errnum)
349
350int errnum;
351
352{
353#ifdef HAVE_STRERROR
354    char *msg = strerror(errnum);
355    if (msg != NULL)
356    {
357	return msg;
358    }
359#else
360    if (errnum > 0 && errnum < sys_nerr)
361    {
362	return((char *)sys_errlist[errnum]);
363    }
364#endif
365    return("No error");
366}
367
368/* format_time(seconds) - format number of seconds into a suitable
369 *		display that will fit within 6 characters.  Note that this
370 *		routine builds its string in a static area.  If it needs
371 *		to be called more than once without overwriting previous data,
372 *		then we will need to adopt a technique similar to the
373 *		one used for format_k.
374 */
375
376/* Explanation:
377   We want to keep the output within 6 characters.  For low values we use
378   the format mm:ss.  For values that exceed 999:59, we switch to a format
379   that displays hours and fractions:  hhh.tH.  For values that exceed
380   999.9, we use hhhh.t and drop the "H" designator.  For values that
381   exceed 9999.9, we use "???".
382 */
383
384char *format_time(seconds)
385
386long seconds;
387
388{
389    register int value;
390    register int digit;
391    register char *ptr;
392    static char result[10];
393
394    /* sanity protection */
395    if (seconds < 0 || seconds > (99999l * 360l))
396    {
397	strcpy(result, "   ???");
398    }
399    else if (seconds >= (1000l * 60l))
400    {
401	/* alternate (slow) method displaying hours and tenths */
402	sprintf(result, "%5.1fH", (double)seconds / (double)(60l * 60l));
403
404	/* It is possible that the sprintf took more than 6 characters.
405	   If so, then the "H" appears as result[6].  If not, then there
406	   is a \0 in result[6].  Either way, it is safe to step on.
407	 */
408	result[6] = '\0';
409    }
410    else
411    {
412	/* standard method produces MMM:SS */
413	/* we avoid printf as must as possible to make this quick */
414	sprintf(result, "%3ld:%02ld",
415	    (long)(seconds / 60), (long)(seconds % 60));
416    }
417    return(result);
418}
419
420/*
421 * format_k(amt) - format a kilobyte memory value, returning a string
422 *		suitable for display.  Returns a pointer to a static
423 *		area that changes each call.  "amt" is converted to a
424 *		string with a trailing "K".  If "amt" is 10000 or greater,
425 *		then it is formatted as megabytes (rounded) with a
426 *		trailing "M".
427 */
428
429/*
430 * Compromise time.  We need to return a string, but we don't want the
431 * caller to have to worry about freeing a dynamically allocated string.
432 * Unfortunately, we can't just return a pointer to a static area as one
433 * of the common uses of this function is in a large call to sprintf where
434 * it might get invoked several times.  Our compromise is to maintain an
435 * array of strings and cycle thru them with each invocation.  We make the
436 * array large enough to handle the above mentioned case.  The constant
437 * NUM_STRINGS defines the number of strings in this array:  we can tolerate
438 * up to NUM_STRINGS calls before we start overwriting old information.
439 * Keeping NUM_STRINGS a power of two will allow an intelligent optimizer
440 * to convert the modulo operation into something quicker.  What a hack!
441 */
442
443#define NUM_STRINGS 8
444
445char *format_k(amt)
446
447int amt;
448
449{
450    static char retarray[NUM_STRINGS][16];
451    static int index = 0;
452    register char *p;
453    register char *ret;
454    register char tag = 'K';
455
456    p = ret = retarray[index];
457    index = (index + 1) % NUM_STRINGS;
458
459    if (amt >= 10000)
460    {
461	amt = (amt + 512) / 1024;
462	tag = 'M';
463	if (amt >= 10000)
464	{
465	    amt = (amt + 512) / 1024;
466	    tag = 'G';
467	}
468    }
469
470    p = strecpy(p, itoa(amt));
471    *p++ = tag;
472    *p = '\0';
473
474    return(ret);
475}
476
477char *format_k2(amt)
478
479unsigned long long amt;
480
481{
482    static char retarray[NUM_STRINGS][16];
483    static int index = 0;
484    register char *p;
485    register char *ret;
486    register char tag = 'K';
487
488    p = ret = retarray[index];
489    index = (index + 1) % NUM_STRINGS;
490
491    if (amt >= 100000)
492    {
493	amt = (amt + 512) / 1024;
494	tag = 'M';
495	if (amt >= 100000)
496	{
497	    amt = (amt + 512) / 1024;
498	    tag = 'G';
499	}
500    }
501
502    p = strecpy(p, itoa((int)amt));
503    *p++ = tag;
504    *p = '\0';
505
506    return(ret);
507}
508