Annotation of src/usr.bin/top/utils.c, Revision 1.8
1.8 ! deraadt 1: /* $OpenBSD: utils.c,v 1.7 2003/04/25 21:40:52 deraadt Exp $ */
1.1 downsj 2:
3: /*
4: * Top users/processes display for Unix
5: * Version 3
6: *
1.6 deraadt 7: * Copyright (c) 1984, 1989, William LeFebvre, Rice University
8: * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
1.1 downsj 9: *
1.6 deraadt 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.
1.1 downsj 29: */
30:
31: /*
32: * This file contains various handy utilities used by top.
33: */
34:
1.2 downsj 35: #include <sys/types.h>
36: #include <stdio.h>
37: #include <string.h>
38: #include <stdlib.h>
39: #include <unistd.h>
40:
1.1 downsj 41: #include "top.h"
42:
43: int atoiwi(str)
44:
45: char *str;
46:
47: {
1.4 mpech 48: int len;
1.1 downsj 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:
1.4 mpech 86: int val;
1.1 downsj 87:
88: {
1.4 mpech 89: char *ptr;
1.1 downsj 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:
1.4 mpech 117: int val;
1.1 downsj 118:
119: {
1.4 mpech 120: char *ptr;
1.2 downsj 121: static char buffer[25]; /* result is built here */
1.1 downsj 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: {
1.4 mpech 151: int cnt = 0;
1.1 downsj 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:
1.4 mpech 168: char *to;
169: char *from;
1.1 downsj 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: {
1.4 mpech 186: int i = 0;
1.1 downsj 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: {
1.4 mpech 213: char *from;
214: char *to;
215: int cnt;
216: int ch;
1.1 downsj 217: int length;
218: int lastch;
1.4 mpech 219: char **argv;
1.1 downsj 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:
1.2 downsj 292: int percentages(cnt, out, new, old, diffs)
1.1 downsj 293:
294: int cnt;
295: int *out;
1.4 mpech 296: long *new;
297: long *old;
1.3 niklas 298: long *diffs;
1.1 downsj 299:
300: {
1.4 mpech 301: int i;
302: long change;
303: long total_change;
304: long *dp;
1.3 niklas 305: long half_total;
1.1 downsj 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 */
1.2 downsj 317: change = ((unsigned int)*new-(unsigned int)*old);
1.1 downsj 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: {
1.2 downsj 333: *out++ = ((*diffs++ * 1000 + half_total) / total_change);
1.1 downsj 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:
1.2 downsj 358: time_t seconds;
1.1 downsj 359:
360: {
361: static char result[10];
362:
363: /* sanity protection */
364: if (seconds < 0 || seconds > (99999l * 360l))
365: {
1.5 deraadt 366: strlcpy(result, " ???", sizeof result);
1.1 downsj 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:
1.7 deraadt 374: /* It is possible that the snprintf took more than 6 characters.
1.1 downsj 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 */
1.2 downsj 384: snprintf(result, sizeof(result), "%3d:%02d", seconds / 60,
385: seconds % 60);
1.1 downsj 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
1.8 ! deraadt 403: * of the common uses of this function is in a large call to snprintf where
1.1 downsj 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;
1.4 mpech 422: char *p;
423: char *ret;
424: char tag = 'K';
1.1 downsj 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: }