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