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