xref: /openbsd-src/usr.bin/top/utils.c (revision 47911bd667ac77dc523b8a13ef40b012dbffa741)
1 /*	$OpenBSD: utils.c,v 1.6 2002/07/15 17:20:36 deraadt Exp $	*/
2 
3 /*
4  *  Top users/processes display for Unix
5  *  Version 3
6  *
7  * Copyright (c) 1984, 1989, William LeFebvre, Rice University
8  * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22  * IN NO EVENT SHALL THE AUTHOR OR HIS EMPLOYER BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 /*
32  *  This file contains various handy utilities used by top.
33  */
34 
35 #include <sys/types.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <stdlib.h>
39 #include <unistd.h>
40 
41 #include "top.h"
42 
43 int atoiwi(str)
44 
45 char *str;
46 
47 {
48     int len;
49 
50     len = strlen(str);
51     if (len != 0)
52     {
53 	if (strncmp(str, "infinity", len) == 0 ||
54 	    strncmp(str, "all",      len) == 0 ||
55 	    strncmp(str, "maximum",  len) == 0)
56 	{
57 	    return(Infinity);
58 	}
59 	else if (str[0] == '-')
60 	{
61 	    return(Invalid);
62 	}
63 	else
64 	{
65 	    return(atoi(str));
66 	}
67     }
68     return(0);
69 }
70 
71 /*
72  *  itoa - convert integer (decimal) to ascii string for positive numbers
73  *  	   only (we don't bother with negative numbers since we know we
74  *	   don't use them).
75  */
76 
77 				/*
78 				 * How do we know that 16 will suffice?
79 				 * Because the biggest number that we will
80 				 * ever convert will be 2^32-1, which is 10
81 				 * digits.
82 				 */
83 
84 char *itoa(val)
85 
86 int val;
87 
88 {
89     char *ptr;
90     static char buffer[16];	/* result is built here */
91     				/* 16 is sufficient since the largest number
92 				   we will ever convert will be 2^32-1,
93 				   which is 10 digits. */
94 
95     ptr = buffer + sizeof(buffer);
96     *--ptr = '\0';
97     if (val == 0)
98     {
99 	*--ptr = '0';
100     }
101     else while (val != 0)
102     {
103 	*--ptr = (val % 10) + '0';
104 	val /= 10;
105     }
106     return(ptr);
107 }
108 
109 /*
110  *  itoa7(val) - like itoa, except the number is right justified in a 7
111  *	character field.  This code is a duplication of itoa instead of
112  *	a front end to a more general routine for efficiency.
113  */
114 
115 char *itoa7(val)
116 
117 int val;
118 
119 {
120     char *ptr;
121     static char buffer[25];	/* result is built here */
122 
123     ptr = buffer + sizeof(buffer);
124     *--ptr = '\0';
125     if (val == 0)
126     {
127 	*--ptr = '0';
128     }
129     else while (val != 0)
130     {
131 	*--ptr = (val % 10) + '0';
132 	val /= 10;
133     }
134     while (ptr > buffer + sizeof(buffer) - 7)
135     {
136 	*--ptr = ' ';
137     }
138     return(ptr);
139 }
140 
141 /*
142  *  digits(val) - return number of decimal digits in val.  Only works for
143  *	positive numbers.  If val <= 0 then digits(val) == 0.
144  */
145 
146 int digits(val)
147 
148 int val;
149 
150 {
151     int cnt = 0;
152 
153     while (val > 0)
154     {
155 	cnt++;
156 	val /= 10;
157     }
158     return(cnt);
159 }
160 
161 /*
162  *  strecpy(to, from) - copy string "from" into "to" and return a pointer
163  *	to the END of the string "to".
164  */
165 
166 char *strecpy(to, from)
167 
168 char *to;
169 char *from;
170 
171 {
172     while ((*to++ = *from++) != '\0');
173     return(--to);
174 }
175 
176 /*
177  * string_index(string, array) - find string in array and return index
178  */
179 
180 int string_index(string, array)
181 
182 char *string;
183 char **array;
184 
185 {
186     int i = 0;
187 
188     while (*array != NULL)
189     {
190 	if (strcmp(string, *array) == 0)
191 	{
192 	    return(i);
193 	}
194 	array++;
195 	i++;
196     }
197     return(-1);
198 }
199 
200 /*
201  * argparse(line, cntp) - parse arguments in string "line", separating them
202  *	out into an argv-like array, and setting *cntp to the number of
203  *	arguments encountered.  This is a simple parser that doesn't understand
204  *	squat about quotes.
205  */
206 
207 char **argparse(line, cntp)
208 
209 char *line;
210 int *cntp;
211 
212 {
213     char *from;
214     char *to;
215     int cnt;
216     int ch;
217     int length;
218     int lastch;
219     char **argv;
220     char **argarray;
221     char *args;
222 
223     /* unfortunately, the only real way to do this is to go thru the
224        input string twice. */
225 
226     /* step thru the string counting the white space sections */
227     from = line;
228     lastch = cnt = length = 0;
229     while ((ch = *from++) != '\0')
230     {
231 	length++;
232 	if (ch == ' ' && lastch != ' ')
233 	{
234 	    cnt++;
235 	}
236 	lastch = ch;
237     }
238 
239     /* add three to the count:  one for the initial "dummy" argument,
240        one for the last argument and one for NULL */
241     cnt += 3;
242 
243     /* allocate a char * array to hold the pointers */
244     argarray = (char **)malloc(cnt * sizeof(char *));
245 
246     /* allocate another array to hold the strings themselves */
247     args = (char *)malloc(length+2);
248 
249     /* initialization for main loop */
250     from = line;
251     to = args;
252     argv = argarray;
253     lastch = '\0';
254 
255     /* create a dummy argument to keep getopt happy */
256     *argv++ = to;
257     *to++ = '\0';
258     cnt = 2;
259 
260     /* now build argv while copying characters */
261     *argv++ = to;
262     while ((ch = *from++) != '\0')
263     {
264 	if (ch != ' ')
265 	{
266 	    if (lastch == ' ')
267 	    {
268 		*to++ = '\0';
269 		*argv++ = to;
270 		cnt++;
271 	    }
272 	    *to++ = ch;
273 	}
274 	lastch = ch;
275     }
276     *to++ = '\0';
277 
278     /* set cntp and return the allocated array */
279     *cntp = cnt;
280     return(argarray);
281 }
282 
283 /*
284  *  percentages(cnt, out, new, old, diffs) - calculate percentage change
285  *	between array "old" and "new", putting the percentages i "out".
286  *	"cnt" is size of each array and "diffs" is used for scratch space.
287  *	The array "old" is updated on each call.
288  *	The routine assumes modulo arithmetic.  This function is especially
289  *	useful on BSD mchines for calculating cpu state percentages.
290  */
291 
292 int percentages(cnt, out, new, old, diffs)
293 
294 int cnt;
295 int *out;
296 long *new;
297 long *old;
298 long *diffs;
299 
300 {
301     int i;
302     long change;
303     long total_change;
304     long *dp;
305     long half_total;
306 
307     /* initialization */
308     total_change = 0;
309     dp = diffs;
310 
311     /* calculate changes for each state and the overall change */
312     for (i = 0; i < cnt; i++)
313     {
314 	if ((change = *new - *old) < 0)
315 	{
316 	    /* this only happens when the counter wraps */
317 	    change = ((unsigned int)*new-(unsigned int)*old);
318 	}
319 	total_change += (*dp++ = change);
320 	*old++ = *new++;
321     }
322 
323     /* avoid divide by zero potential */
324     if (total_change == 0)
325     {
326 	total_change = 1;
327     }
328 
329     /* calculate percentages based on overall change, rounding up */
330     half_total = total_change / 2l;
331     for (i = 0; i < cnt; i++)
332     {
333 	*out++ = ((*diffs++ * 1000 + half_total) / total_change);
334     }
335 
336     /* return the total in case the caller wants to use it */
337     return(total_change);
338 }
339 
340 /* format_time(seconds) - format number of seconds into a suitable
341  *		display that will fit within 6 characters.  Note that this
342  *		routine builds its string in a static area.  If it needs
343  *		to be called more than once without overwriting previous data,
344  *		then we will need to adopt a technique similar to the
345  *		one used for format_k.
346  */
347 
348 /* Explanation:
349    We want to keep the output within 6 characters.  For low values we use
350    the format mm:ss.  For values that exceed 999:59, we switch to a format
351    that displays hours and fractions:  hhh.tH.  For values that exceed
352    999.9, we use hhhh.t and drop the "H" designator.  For values that
353    exceed 9999.9, we use "???".
354  */
355 
356 char *format_time(seconds)
357 
358 time_t seconds;
359 
360 {
361     static char result[10];
362 
363     /* sanity protection */
364     if (seconds < 0 || seconds > (99999l * 360l))
365     {
366 	strlcpy(result, "   ???", sizeof result);
367     }
368     else if (seconds >= (1000l * 60l))
369     {
370 	/* alternate (slow) method displaying hours and tenths */
371 	snprintf(result, sizeof(result), "%5.1fH",
372 		(double)seconds / (double)(60l * 60l));
373 
374 	/* It is possible that the sprintf took more than 6 characters.
375 	   If so, then the "H" appears as result[6].  If not, then there
376 	   is a \0 in result[6].  Either way, it is safe to step on.
377 	 */
378 	result[6] = '\0';
379     }
380     else
381     {
382 	/* standard method produces MMM:SS */
383 	/* we avoid printf as must as possible to make this quick */
384 	snprintf(result, sizeof(result), "%3d:%02d", seconds / 60,
385 		seconds % 60);
386     }
387     return(result);
388 }
389 
390 /*
391  * format_k(amt) - format a kilobyte memory value, returning a string
392  *		suitable for display.  Returns a pointer to a static
393  *		area that changes each call.  "amt" is converted to a
394  *		string with a trailing "K".  If "amt" is 10000 or greater,
395  *		then it is formatted as megabytes (rounded) with a
396  *		trailing "M".
397  */
398 
399 /*
400  * Compromise time.  We need to return a string, but we don't want the
401  * caller to have to worry about freeing a dynamically allocated string.
402  * Unfortunately, we can't just return a pointer to a static area as one
403  * of the common uses of this function is in a large call to sprintf where
404  * it might get invoked several times.  Our compromise is to maintain an
405  * array of strings and cycle thru them with each invocation.  We make the
406  * array large enough to handle the above mentioned case.  The constant
407  * NUM_STRINGS defines the number of strings in this array:  we can tolerate
408  * up to NUM_STRINGS calls before we start overwriting old information.
409  * Keeping NUM_STRINGS a power of two will allow an intelligent optimizer
410  * to convert the modulo operation into something quicker.  What a hack!
411  */
412 
413 #define NUM_STRINGS 8
414 
415 char *format_k(amt)
416 
417 int amt;
418 
419 {
420     static char retarray[NUM_STRINGS][16];
421     static int index = 0;
422     char *p;
423     char *ret;
424     char tag = 'K';
425 
426     p = ret = retarray[index];
427     index = (index + 1) % NUM_STRINGS;
428 
429     if (amt >= 10000)
430     {
431 	amt = (amt + 512) / 1024;
432 	tag = 'M';
433 	if (amt >= 10000)
434 	{
435 	    amt = (amt + 512) / 1024;
436 	    tag = 'G';
437 	}
438     }
439 
440     p = strecpy(p, itoa(amt));
441     *p++ = tag;
442     *p = '\0';
443 
444     return(ret);
445 }
446