xref: /netbsd-src/external/bsd/top/dist/utils.c (revision 9ded70a5ef6bdad60231e03e3b27fe20e81315e4)
1 /*
2  * Copyright (c) 1984 through 2008, William LeFebvre
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *
11  *     * Redistributions in binary form must reproduce the above
12  * copyright notice, this list of conditions and the following disclaimer
13  * in the documentation and/or other materials provided with the
14  * distribution.
15  *
16  *     * Neither the name of William LeFebvre nor the names of other
17  * contributors may be used to endorse or promote products derived from
18  * this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 /*
34  *  Top users/processes display for Unix
35  *  Version 3
36  */
37 
38 /*
39  *  This file contains various handy utilities used by top.
40  */
41 
42 #include "os.h"
43 #include <ctype.h>
44 #include <math.h>
45 #ifdef HAVE_STDARG_H
46 #include <stdarg.h>
47 #else
48 #undef DEBUG
49 #endif
50 #include "top.h"
51 #include "utils.h"
52 
53 static int
alldigits(char * s)54 alldigits(char *s)
55 
56 {
57     int ch;
58 
59     while ((ch = *s++) != '\0')
60     {
61 	if (!isdigit(ch))
62 	{
63 	    return 0;
64 	}
65     }
66     return 1;
67 }
68 
69 int
atoiwi(char * str)70 atoiwi(char *str)
71 
72 {
73     register int len;
74 
75     len = strlen(str);
76     if (len != 0)
77     {
78 	if (strncmp(str, "infinity", len) == 0 ||
79 	    strncmp(str, "all",      len) == 0 ||
80 	    strncmp(str, "maximum",  len) == 0)
81 	{
82 	    return(Infinity);
83 	}
84 	else if (alldigits(str))
85 	{
86 	    return(atoi(str));
87 	}
88 	else
89 	{
90 	    return(Invalid);
91 	}
92     }
93     return(0);
94 }
95 
96 /*
97  *  itoa - convert integer (decimal) to ascii string for positive numbers
98  *  	   only (we don't bother with negative numbers since we know we
99  *	   don't use them).
100  */
101 
102 				/*
103 				 * How do we know that 16 will suffice?
104 				 * Because the biggest number that we will
105 				 * ever convert will be 2^32-1, which is 10
106 				 * digits.
107 				 */
108 
109 char *
itoa(int val)110 itoa(int val)
111 
112 {
113     register char *ptr;
114     static char buffer[16];	/* result is built here */
115     				/* 16 is sufficient since the largest number
116 				   we will ever convert will be 2^32-1,
117 				   which is 10 digits. */
118 
119     ptr = buffer + sizeof(buffer);
120     *--ptr = '\0';
121     if (val == 0)
122     {
123 	*--ptr = '0';
124     }
125     else while (val != 0)
126     {
127 	*--ptr = (val % 10) + '0';
128 	val /= 10;
129     }
130     return(ptr);
131 }
132 
133 /*
134  *  itoa7(val) - like itoa, except the number is right justified in a 7
135  *	character field.  This code is a duplication of itoa instead of
136  *	a front end to a more general routine for efficiency.
137  */
138 
139 char *
itoa_w(int val,int w)140 itoa_w(int val, int w)
141 
142 {
143     char *ptr;
144     char *eptr;
145     static char buffer[16];	/* result is built here */
146     				/* 16 is sufficient since the largest number
147 				   we will ever convert will be 2^32-1,
148 				   which is 10 digits. */
149 
150     if (w > 15)
151     {
152 	w = 15;
153     }
154     eptr = ptr = buffer + sizeof(buffer);
155     *--ptr = '\0';
156     if (val == 0)
157     {
158 	*--ptr = '0';
159     }
160     else while (val != 0)
161     {
162 	*--ptr = (val % 10) + '0';
163 	val /= 10;
164     }
165     while (ptr >= eptr - w)
166     {
167 	*--ptr = ' ';
168     }
169     return(ptr);
170 }
171 
172 char *
itoa7(int val)173 itoa7(int val)
174 
175 {
176     return itoa_w(val, 7);
177 }
178 
179 /*
180  *  digits(val) - return number of decimal digits in val.  Only works for
181  *	positive numbers.  If val < 0 then digits(val) == 0, but
182  *      digits(0) == 1.
183  */
184 
185 int
digits(int val)186 digits(int val)
187 
188 {
189     register int cnt = 0;
190 
191     if (val == 0)
192     {
193 	return 1;
194     }
195     while (val > 0)
196     {
197 	cnt++;
198 	val /= 10;
199     }
200     return(cnt);
201 }
202 
203 /*
204  *  printable(char *str) - make the string pointed to by "str" into one that is
205  *	printable (i.e.: all ascii), by converting all non-printable
206  *	characters into '?'.  Replacements are done in place and a pointer
207  *	to the original buffer is returned.
208  */
209 
210 char *
printable(char * str)211 printable(char *str)
212 
213 {
214     register char *ptr;
215     register int ch;
216 
217     ptr = str;
218     while ((ch = *ptr) != '\0')
219     {
220 	if (!isprint(ch))
221 	{
222 	    *ptr = '?';
223 	}
224 	ptr++;
225     }
226     return(str);
227 }
228 
229 /*
230  *  strcpyend(to, from) - copy string "from" into "to" and return a pointer
231  *	to the END of the string "to".
232  */
233 
234 char *
strcpyend(char * to,const char * from)235 strcpyend(char *to, const char *from)
236 
237 {
238     while ((*to++ = *from++) != '\0');
239     return(--to);
240 }
241 
242 /*
243  * char *
244  * homogenize(const char *str)
245  *
246  * Remove unwanted characters from "str" and make everything lower case.
247  * Newly allocated string is returned: the original is not altered.
248  */
249 
homogenize(const char * str)250 char *homogenize(const char *str)
251 
252 {
253     char *ans;
254     char *fr;
255     char *to;
256     int ch;
257 
258     to = fr = ans = estrdup(str);
259     while ((ch = *fr++) != '\0')
260     {
261 	if (isalnum(ch))
262 	{
263 	    *to++ = tolower(ch);
264 	}
265     }
266 
267     *to = '\0';
268     return ans;
269 }
270 
271 /*
272  * string_index(string, array) - find string in array and return index
273  */
274 
275 int
string_index(const char * string,const char ** array)276 string_index(const char *string, const char **array)
277 
278 {
279     register int i = 0;
280 
281     while (*array != NULL)
282     {
283 	if (strcmp(string, *array) == 0)
284 	{
285 	    return(i);
286 	}
287 	array++;
288 	i++;
289     }
290     return(-1);
291 }
292 
293 /*
294  * char *string_list(char **strings)
295  *
296  * Create a comma-separated list of the strings in the NULL-terminated
297  * "strings".  Returned string is malloc-ed and should be freed when the
298  * caller is done.  Note that this is not an efficient function.
299  */
300 
string_list(const char ** strings)301 char *string_list(const char **strings)
302 
303 {
304     int cnt = 0;
305     const char **pp;
306     const char *p;
307     char *result = NULL;
308     char *resp = NULL;
309 
310     pp = strings;
311     while ((p = *pp++) != NULL)
312     {
313 	cnt += strlen(p) + 2;
314     }
315 
316     if (cnt > 0)
317     {
318 	resp = result = emalloc(cnt);
319 	pp = strings;
320 	while ((p = *pp++) != NULL)
321 	{
322 	    resp = strcpyend(resp, p);
323 	    if (*pp != NULL)
324 	    {
325 		resp = strcpyend(resp, ", ");
326 	    }
327 	}
328     }
329 
330     return result;
331 }
332 
333 /*
334  * argparse(line, cntp) - parse arguments in string "line", separating them
335  *	out into an argv-like array, and setting *cntp to the number of
336  *	arguments encountered.  This is a simple parser that doesn't understand
337  *	squat about quotes.
338  */
339 
340 char **
argparse(char * line,int * cntp)341 argparse(char *line, int *cntp)
342 
343 {
344     register char *from;
345     register char *to;
346     register int cnt;
347     register int ch;
348     int length;
349     int lastch;
350     register char **argv;
351     char **argarray;
352     char *args;
353 
354     /* unfortunately, the only real way to do this is to go thru the
355        input string twice. */
356 
357     /* step thru the string counting the white space sections */
358     from = line;
359     lastch = cnt = length = 0;
360     while ((ch = *from++) != '\0')
361     {
362 	length++;
363 	if (ch == ' ' && lastch != ' ')
364 	{
365 	    cnt++;
366 	}
367 	lastch = ch;
368     }
369 
370     /* add three to the count:  one for the initial "dummy" argument,
371        one for the last argument and one for NULL */
372     cnt += 3;
373 
374     /* allocate a char * array to hold the pointers */
375     argarray = emalloc(cnt * sizeof(char *));
376 
377     /* allocate another array to hold the strings themselves */
378     args = emalloc(length+2);
379 
380     /* initialization for main loop */
381     from = line;
382     to = args;
383     argv = argarray;
384     lastch = '\0';
385 
386     /* create a dummy argument to keep getopt happy */
387     *argv++ = to;
388     *to++ = '\0';
389     cnt = 2;
390 
391     /* now build argv while copying characters */
392     *argv++ = to;
393     while ((ch = *from++) != '\0')
394     {
395 	if (ch != ' ')
396 	{
397 	    if (lastch == ' ')
398 	    {
399 		*to++ = '\0';
400 		*argv++ = to;
401 		cnt++;
402 	    }
403 	    *to++ = ch;
404 	}
405 	lastch = ch;
406     }
407     *to++ = '\0';
408 
409     /* set cntp and return the allocated array */
410     *cntp = cnt;
411     return(argarray);
412 }
413 
414 /*
415  *  percentages(cnt, out, new, old, diffs) - calculate percentage change
416  *	between array "old" and "new", putting the percentages i "out".
417  *	"cnt" is size of each array and "diffs" is used for scratch space.
418  *	The array "old" is updated on each call.
419  *	The routine assumes modulo arithmetic.  This function is especially
420  *	useful on BSD mchines for calculating cpu state percentages.
421  */
422 
423 long
percentages(int cnt,int * out,long * new,long * old,long * diffs)424 percentages(int cnt, int *out, long *new, long *old, long *diffs)
425 
426 {
427     register int i;
428     register long change;
429     register long total_change;
430     register long *dp;
431     long half_total;
432 
433     /* initialization */
434     total_change = 0;
435     dp = diffs;
436 
437     /* calculate changes for each state and the overall change */
438     for (i = 0; i < cnt; i++)
439     {
440 	if ((change = *new - *old) < 0)
441 	{
442 	    /* this only happens when the counter wraps */
443 	    change = (int)
444 		((unsigned long)*new-(unsigned long)*old);
445 	}
446 	total_change += (*dp++ = change);
447 	*old++ = *new++;
448     }
449 
450     /* avoid divide by zero potential */
451     if (total_change == 0)
452     {
453 	total_change = 1;
454     }
455 
456     /* calculate percentages based on overall change, rounding up */
457     half_total = total_change / 2l;
458     for (i = 0; i < cnt; i++)
459     {
460 	*out++ = (int)((*diffs++ * 1000 + half_total) / total_change);
461     }
462 
463     /* return the total in case the caller wants to use it */
464     return(total_change);
465 }
466 
467 /*
468  * errmsg(errnum) - return an error message string appropriate to the
469  *           error number "errnum".  This is a substitute for the System V
470  *           function "strerror".  There appears to be no reliable way to
471  *           determine if "strerror" exists at compile time, so I make do
472  *           by providing something of similar functionality.  For those
473  *           systems that have strerror and NOT errlist, define
474  *           -DHAVE_STRERROR in the module file and this function will
475  *           use strerror.
476  */
477 
478 /* externs referenced by errmsg */
479 
480 #ifndef HAVE_STRERROR
481 #if !HAVE_DECL_SYS_ERRLIST
482 extern char *sys_errlist[];
483 #endif
484 
485 extern int sys_nerr;
486 #endif
487 
488 const char *
errmsg(int errnum)489 errmsg(int errnum)
490 
491 {
492 #ifdef HAVE_STRERROR
493     char *msg = strerror(errnum);
494     if (msg != NULL)
495     {
496 	return msg;
497     }
498 #else
499     if (errnum > 0 && errnum < sys_nerr)
500     {
501 	return((char *)(sys_errlist[errnum]));
502     }
503 #endif
504     return("No error");
505 }
506 
507 /* format_percent(v) - format a double as a percentage in a manner that
508  *		does not exceed 5 characters (excluding any trailing
509  *		percent sign).  Since it is possible for the value
510  *		to exceed 100%, we format such values with no fractional
511  *		component to fit within the 5 characters.
512  */
513 
514 char *
format_percent(double v)515 format_percent(double v)
516 
517 {
518     static char result[10];
519 
520     /* enumerate the possibilities */
521     if (v < 0 || v >= 100000.)
522     {
523 	/* we dont want to try extreme values */
524 	strcpy(result, "  ???");
525     }
526     else if (v > 99.99)
527     {
528 	sprintf(result, "%5.0f", v);
529     }
530     else
531     {
532 	sprintf(result, "%5.2f", v);
533     }
534 
535     return result;
536 }
537 
538 /* format_time(seconds) - format number of seconds into a suitable
539  *		display that will fit within 6 characters.  Note that this
540  *		routine builds its string in a static area.  If it needs
541  *		to be called more than once without overwriting previous data,
542  *		then we will need to adopt a technique similar to the
543  *		one used for format_k.
544  */
545 
546 /* Explanation:
547    We want to keep the output within 6 characters.  For low values we use
548    the format mm:ss.  For values that exceed 999:59, we switch to a format
549    that displays hours and fractions:  hhh.tH.  For values that exceed
550    999.9, we use hhhh.t and drop the "H" designator.  For values that
551    exceed 9999.9, we use "???".
552  */
553 
554 char *
format_time(long seconds)555 format_time(long seconds)
556 
557 {
558     static char result[10];
559 
560     /* sanity protection */
561     if (seconds < 0 || seconds > (99999l * 360l))
562     {
563 	strcpy(result, "   ???");
564     }
565     else if (seconds >= (1000l * 60l))
566     {
567 	/* alternate (slow) method displaying hours and tenths */
568 	sprintf(result, "%5.1fH", (double)seconds / (double)(60l * 60l));
569 
570 	/* It is possible that the sprintf took more than 6 characters.
571 	   If so, then the "H" appears as result[6].  If not, then there
572 	   is a \0 in result[6].  Either way, it is safe to step on.
573 	 */
574 	result[6] = '\0';
575     }
576     else
577     {
578 	/* standard method produces MMM:SS */
579 	/* we avoid printf as must as possible to make this quick */
580 	sprintf(result, "%3ld:%02ld", seconds / 60l, seconds % 60l);
581     }
582     return(result);
583 }
584 
585 /*
586  * format_k(amt) - format a kilobyte memory value, returning a string
587  *		suitable for display.  Returns a pointer to a static
588  *		area that changes each call.  "amt" is converted to a
589  *		string with a trailing "K".  If "amt" is 10000 or greater,
590  *		then it is formatted as megabytes (rounded) with a
591  *		trailing "M".
592  */
593 
594 /*
595  * Compromise time.  We need to return a string, but we don't want the
596  * caller to have to worry about freeing a dynamically allocated string.
597  * Unfortunately, we can't just return a pointer to a static area as one
598  * of the common uses of this function is in a large call to sprintf where
599  * it might get invoked several times.  Our compromise is to maintain an
600  * array of strings and cycle thru them with each invocation.  We make the
601  * array large enough to handle the above mentioned case.  The constant
602  * NUM_STRINGS defines the number of strings in this array:  we can tolerate
603  * up to NUM_STRINGS calls before we start overwriting old information.
604  * Keeping NUM_STRINGS a power of two will allow an intelligent optimizer
605  * to convert the modulo operation into something quicker.  What a hack!
606  */
607 
608 #define NUM_STRINGS 8
609 
610 char *
format_k(long amt)611 format_k(long amt)
612 
613 {
614     static char retarray[NUM_STRINGS][24];
615     static int idx = 0;
616     register char *ret;
617     register char tag = 'K';
618 
619     ret = retarray[idx];
620     idx = (idx + 1) % NUM_STRINGS;
621 
622     if (amt >= 10000)
623     {
624 	amt = (amt + 512) / 1024;
625 	tag = 'M';
626 	if (amt >= 10000)
627 	{
628 	    amt = (amt + 512) / 1024;
629 	    tag = 'G';
630 	}
631     }
632 
633     snprintf(ret, sizeof(retarray[idx])-1, "%ld%c", amt, tag);
634 
635     return(ret);
636 }
637 
638 /*
639  * Time keeping functions.
640  */
641 
642 static struct timeval lasttime = { 0, 0 };
643 static unsigned int elapsed_msecs = 0;
644 
645 void
time_get(struct timeval * tv)646 time_get(struct timeval *tv)
647 
648 {
649     /* get the current time */
650 #ifdef HAVE_GETTIMEOFDAY
651     gettimeofday(tv, NULL);
652 #else
653     tv->tv_sec = (long)time(NULL);
654     tv->tv_usec = 0;
655 #endif
656 }
657 
658 void
time_mark(struct timeval * tv)659 time_mark(struct timeval *tv)
660 
661 {
662     struct timeval thistime;
663     struct timeval timediff;
664 
665     /* if the caller didnt provide one then use our own */
666     if (tv == NULL)
667     {
668 	tv = &thistime;
669     }
670 
671     /* get the current time */
672 #ifdef HAVE_GETTIMEOFDAY
673     gettimeofday(tv, NULL);
674 #else
675     tv->tv_sec = (long)time(NULL);
676     tv->tv_usec = 0;
677 #endif
678 
679     /* calculate the difference */
680     timediff.tv_sec = tv->tv_sec - lasttime.tv_sec;
681     timediff.tv_usec = tv->tv_usec - lasttime.tv_usec;
682     if (timediff.tv_usec < 0) {
683 	timediff.tv_sec--;
684 	timediff.tv_usec += 1000000;
685     }
686 
687     /* convert to milliseconds */
688     elapsed_msecs = timediff.tv_sec * 1000 + timediff.tv_usec / 1000;
689     if (elapsed_msecs == 0)
690     {
691 	elapsed_msecs = 1;
692     }
693 
694     /* save for next time */
695     lasttime = *tv;
696 }
697 
698 unsigned int
time_elapsed()699 time_elapsed()
700 
701 {
702     return elapsed_msecs;
703 }
704 
705 unsigned int
diff_per_second(unsigned int x,unsigned int y)706 diff_per_second(unsigned int x, unsigned int y)
707 
708 {
709     return (y > x ? UINT_MAX - y + x + 1 : x - y) * 1000 / elapsed_msecs;
710 }
711 
712 void
double2tv(struct timeval * tv,double d)713 double2tv(struct timeval *tv, double d)
714 {
715     double di;
716 
717     di = floor(d);
718     tv->tv_sec = (time_t)di;
719     tv->tv_usec = (int)ceil((d - di) * 1000000.0);
720 }
721 
722 static int debug_on = 0;
723 
724 #ifdef DEBUG
725 FILE *debugfile;
726 #endif
727 
728 void
debug_set(int i)729 debug_set(int i)
730 
731 {
732     debug_on = i;
733 #ifdef DEBUG
734     debugfile = fopen("/tmp/top.debug", "w");
735 #endif
736 }
737 
738 #ifdef DEBUG
739 void
xdprintf(char * fmt,...)740 xdprintf(char *fmt, ...)
741 
742 {
743     va_list argp;
744 
745     va_start(argp, fmt);
746 
747     if (debug_on)
748     {
749 	vfprintf(debugfile, fmt, argp);
750 	fflush(debugfile);
751     }
752 
753     va_end(argp);
754 }
755 #endif
756 
757