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