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