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