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