Annotation of src/usr.bin/top/utils.c, Revision 1.13
1.13 ! deraadt 1: /* $OpenBSD: utils.c,v 1.12 2003/06/20 16:53:15 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"
1.13 ! deraadt 42: #include "utils.h"
1.1 downsj 43:
1.9 pvalchev 44: int
45: atoiwi(char *str)
1.1 downsj 46: {
1.12 deraadt 47: size_t len;
1.1 downsj 48:
1.10 deraadt 49: len = strlen(str);
50: if (len != 0) {
51: if (strncmp(str, "infinity", len) == 0 ||
52: strncmp(str, "all", len) == 0 ||
53: strncmp(str, "maximum", len) == 0) {
54: return (Infinity);
55: } else if (str[0] == '-')
56: return (Invalid);
57: else
58: return (atoi(str));
1.1 downsj 59: }
1.10 deraadt 60: return (0);
1.1 downsj 61: }
62:
63: /*
1.11 millert 64: * itoa - convert integer (decimal) to ascii string.
1.1 downsj 65: */
1.9 pvalchev 66: char *
67: itoa(int val)
1.1 downsj 68: {
1.10 deraadt 69: static char buffer[16]; /* result is built here */
70:
71: /*
72: * 16 is sufficient since the largest number we will ever convert
73: * will be 2^32-1, which is 10 digits.
74: */
1.11 millert 75: (void)snprintf(buffer, sizeof(buffer), "%d", val);
76: return (buffer);
1.1 downsj 77: }
78:
79: /*
1.11 millert 80: * format_uid(uid) - like itoa, except for uid_t and the number is right
81: * justified in a 6 character field to match uname_field in top.c.
1.1 downsj 82: */
1.9 pvalchev 83: char *
1.11 millert 84: format_uid(uid_t uid)
1.1 downsj 85: {
1.11 millert 86: static char buffer[16]; /* result is built here */
1.1 downsj 87:
1.11 millert 88: /*
89: * 16 is sufficient since the largest uid we will ever convert
90: * will be 2^32-1, which is 10 digits.
91: */
92: (void)snprintf(buffer, sizeof(buffer), "%6u", uid);
93: return (buffer);
1.1 downsj 94: }
95:
96: /*
1.10 deraadt 97: * digits(val) - return number of decimal digits in val. Only works for
98: * positive numbers. If val <= 0 then digits(val) == 0.
1.1 downsj 99: */
1.9 pvalchev 100: int
101: digits(int val)
1.1 downsj 102: {
1.10 deraadt 103: int cnt = 0;
1.1 downsj 104:
1.10 deraadt 105: while (val > 0) {
106: cnt++;
107: val /= 10;
108: }
109: return (cnt);
1.1 downsj 110: }
111:
112: /*
113: * string_index(string, array) - find string in array and return index
114: */
1.9 pvalchev 115: int
116: string_index(char *string, char **array)
1.1 downsj 117: {
1.10 deraadt 118: int i = 0;
1.1 downsj 119:
1.10 deraadt 120: while (*array != NULL) {
121: if (strcmp(string, *array) == 0)
122: return (i);
123: array++;
124: i++;
1.1 downsj 125: }
1.10 deraadt 126: return (-1);
1.1 downsj 127: }
128:
129: /*
130: * argparse(line, cntp) - parse arguments in string "line", separating them
1.10 deraadt 131: * out into an argv-like array, and setting *cntp to the number of
132: * arguments encountered. This is a simple parser that doesn't understand
133: * squat about quotes.
1.1 downsj 134: */
1.9 pvalchev 135: char **
136: argparse(char *line, int *cntp)
1.1 downsj 137: {
1.10 deraadt 138: char **argv, **argarray, *args, *from, *to;
139: int cnt, ch, length, lastch;
140:
141: /*
142: * unfortunately, the only real way to do this is to go thru the
143: * input string twice.
144: */
145:
146: /* step thru the string counting the white space sections */
147: from = line;
148: lastch = cnt = length = 0;
149: while ((ch = *from++) != '\0') {
150: length++;
151: if (ch == ' ' && lastch != ' ')
152: cnt++;
153: lastch = ch;
1.1 downsj 154: }
155:
1.10 deraadt 156: /*
157: * add three to the count: one for the initial "dummy" argument, one
158: * for the last argument and one for NULL
159: */
160: cnt += 3;
161:
162: /* allocate a char * array to hold the pointers */
163: argarray = (char **) malloc(cnt * sizeof(char *));
164:
165: /* allocate another array to hold the strings themselves */
166: args = (char *) malloc(length + 2);
167:
168: /* initialization for main loop */
169: from = line;
170: to = args;
171: argv = argarray;
172: lastch = '\0';
173:
174: /* create a dummy argument to keep getopt happy */
175: *argv++ = to;
176: *to++ = '\0';
177: cnt = 2;
178:
179: /* now build argv while copying characters */
180: *argv++ = to;
181: while ((ch = *from++) != '\0') {
182: if (ch != ' ') {
183: if (lastch == ' ') {
184: *to++ = '\0';
185: *argv++ = to;
186: cnt++;
187: }
188: *to++ = ch;
189: }
190: lastch = ch;
1.1 downsj 191: }
1.10 deraadt 192: *to++ = '\0';
193:
194: /* set cntp and return the allocated array */
195: *cntp = cnt;
196: return (argarray);
1.1 downsj 197: }
198:
199: /*
1.10 deraadt 200: * percentages(cnt, out, new, old, diffs) - calculate percentage change
201: * between array "old" and "new", putting the percentages i "out".
202: * "cnt" is size of each array and "diffs" is used for scratch space.
203: * The array "old" is updated on each call.
204: * The routine assumes modulo arithmetic. This function is especially
205: * useful on BSD mchines for calculating cpu state percentages.
1.1 downsj 206: */
1.9 pvalchev 207: int
208: percentages(int cnt, int *out, long *new, long *old, long *diffs)
1.1 downsj 209: {
1.10 deraadt 210: long change, total_change, *dp, half_total;
211: int i;
212:
213: /* initialization */
214: total_change = 0;
215: dp = diffs;
216:
217: /* calculate changes for each state and the overall change */
218: for (i = 0; i < cnt; i++) {
219: if ((change = *new - *old) < 0) {
220: /* this only happens when the counter wraps */
221: change = ((unsigned int)*new - (unsigned int)*old);
222: }
223: total_change += (*dp++ = change);
224: *old++ = *new++;
1.1 downsj 225: }
226:
1.10 deraadt 227: /* avoid divide by zero potential */
228: if (total_change == 0)
229: total_change = 1;
230:
231: /* calculate percentages based on overall change, rounding up */
232: half_total = total_change / 2l;
233: for (i = 0; i < cnt; i++)
234: *out++ = ((*diffs++ * 1000 + half_total) / total_change);
235:
236: /* return the total in case the caller wants to use it */
237: return (total_change);
1.1 downsj 238: }
239:
1.10 deraadt 240: /*
241: * format_time(seconds) - format number of seconds into a suitable display
242: * that will fit within 6 characters. Note that this routine builds its
243: * string in a static area. If it needs to be called more than once without
244: * overwriting previous data, then we will need to adopt a technique similar
245: * to the one used for format_k.
1.1 downsj 246: */
247:
1.10 deraadt 248: /*
249: * Explanation: We want to keep the output within 6 characters. For low
250: * values we use the format mm:ss. For values that exceed 999:59, we switch
251: * to a format that displays hours and fractions: hhh.tH. For values that
252: * exceed 999.9, we use hhhh.t and drop the "H" designator. For values that
253: * exceed 9999.9, we use "???".
1.1 downsj 254: */
255:
1.9 pvalchev 256: char *
257: format_time(time_t seconds)
1.1 downsj 258: {
1.10 deraadt 259: static char result[10];
1.1 downsj 260:
1.10 deraadt 261: /* sanity protection */
262: if (seconds < 0 || seconds > (99999l * 360l)) {
263: strlcpy(result, " ???", sizeof result);
264: } else if (seconds >= (1000l * 60l)) {
265: /* alternate (slow) method displaying hours and tenths */
266: snprintf(result, sizeof(result), "%5.1fH",
267: (double) seconds / (double) (60l * 60l));
268:
269: /*
270: * It is possible that the snprintf took more than 6
271: * characters. If so, then the "H" appears as result[6]. If
272: * not, then there is a \0 in result[6]. Either way, it is
273: * safe to step on.
274: */
275: result[6] = '\0';
276: } else {
277: /* standard method produces MMM:SS */
278: /* we avoid printf as must as possible to make this quick */
279: snprintf(result, sizeof(result), "%3d:%02d", seconds / 60,
280: seconds % 60);
281: }
282: return (result);
1.1 downsj 283: }
284:
285: /*
286: * format_k(amt) - format a kilobyte memory value, returning a string
1.10 deraadt 287: * suitable for display. Returns a pointer to a static
288: * area that changes each call. "amt" is converted to a
289: * string with a trailing "K". If "amt" is 10000 or greater,
290: * then it is formatted as megabytes (rounded) with a
291: * trailing "M".
1.1 downsj 292: */
293:
294: /*
295: * Compromise time. We need to return a string, but we don't want the
296: * caller to have to worry about freeing a dynamically allocated string.
297: * Unfortunately, we can't just return a pointer to a static area as one
1.8 deraadt 298: * of the common uses of this function is in a large call to snprintf where
1.1 downsj 299: * it might get invoked several times. Our compromise is to maintain an
300: * array of strings and cycle thru them with each invocation. We make the
301: * array large enough to handle the above mentioned case. The constant
302: * NUM_STRINGS defines the number of strings in this array: we can tolerate
303: * up to NUM_STRINGS calls before we start overwriting old information.
304: * Keeping NUM_STRINGS a power of two will allow an intelligent optimizer
305: * to convert the modulo operation into something quicker. What a hack!
306: */
307:
308: #define NUM_STRINGS 8
309:
1.9 pvalchev 310: char *
311: format_k(int amt)
1.1 downsj 312: {
1.10 deraadt 313: static char retarray[NUM_STRINGS][16];
1.12 deraadt 314: static int idx = 0;
1.11 millert 315: char *ret, tag = 'K';
1.10 deraadt 316:
1.12 deraadt 317: ret = retarray[idx];
318: idx = (idx + 1) % NUM_STRINGS;
1.10 deraadt 319:
320: if (amt >= 10000) {
321: amt = (amt + 512) / 1024;
322: tag = 'M';
323: if (amt >= 10000) {
324: amt = (amt + 512) / 1024;
325: tag = 'G';
326: }
1.1 downsj 327: }
1.11 millert 328: snprintf(ret, sizeof(retarray[0]), "%d%c", amt, tag);
1.10 deraadt 329: return (ret);
1.1 downsj 330: }