uudecode: nuke duplicate base64_table[]. saves 65 bytes
[platform/upstream/busybox.git] / procps / nmeter.c
1 /*
2 ** Licensed under the GPL v2, see the file LICENSE in this tarball
3 **
4 ** Based on nanotop.c from floppyfw project
5 **
6 ** Contact me: vda.linux@googlemail.com */
7
8 //TODO:
9 // simplify code
10 // /proc/locks
11 // /proc/stat:
12 // disk_io: (3,0):(22272,17897,410702,4375,54750)
13 // btime 1059401962
14
15 #include "libbb.h"
16 #include <time.h>
17
18 typedef unsigned long long ullong;
19
20 enum { PROC_FILE_SIZE = 4096 };
21
22 typedef struct proc_file {
23         char *file;
24         //const char *name;
25         smallint last_gen;
26 } proc_file;
27
28 static const char *const proc_name[] = {
29         "stat",         // Must match the order of proc_file's!
30         "loadavg",
31         "net/dev",
32         "meminfo",
33         "diskstats",
34         "sys/fs/file-nr",
35 };
36
37 struct globals {
38         // Sample generation flip-flop
39         smallint gen;
40         // Linux 2.6? (otherwise assumes 2.4)
41         smallint is26;
42         // 1 if sample delay is not an integer fraction of a second
43         smallint need_seconds;
44         char *cur_outbuf;
45         const char *final_str;
46         int delta;
47         int deltanz;
48         struct timeval tv;
49 #define first_proc_file proc_stat
50         proc_file proc_stat;    // Must match the order of proc_name's!
51         proc_file proc_loadavg;
52         proc_file proc_net_dev;
53         proc_file proc_meminfo;
54         proc_file proc_diskstats;
55         proc_file proc_sys_fs_filenr;
56 };
57 #define G (*ptr_to_globals)
58 #define gen                (G.gen               )
59 #define is26               (G.is26              )
60 #define need_seconds       (G.need_seconds      )
61 #define cur_outbuf         (G.cur_outbuf        )
62 #define final_str          (G.final_str         )
63 #define delta              (G.delta             )
64 #define deltanz            (G.deltanz           )
65 #define tv                 (G.tv                )
66 #define proc_stat          (G.proc_stat         )
67 #define proc_loadavg       (G.proc_loadavg      )
68 #define proc_net_dev       (G.proc_net_dev      )
69 #define proc_meminfo       (G.proc_meminfo      )
70 #define proc_diskstats     (G.proc_diskstats    )
71 #define proc_sys_fs_filenr (G.proc_sys_fs_filenr)
72 #define INIT_G() do { \
73                 PTR_TO_GLOBALS = xzalloc(sizeof(G)); \
74                 cur_outbuf = outbuf; \
75                 final_str = "\n"; \
76                 deltanz = delta = 1000000; \
77         } while (0)
78
79 // We depend on this being a char[], not char* - we take sizeof() of it
80 #define outbuf bb_common_bufsiz1
81
82 static inline void reset_outbuf(void)
83 {
84         cur_outbuf = outbuf;
85 }
86
87 static inline int outbuf_count(void)
88 {
89         return cur_outbuf - outbuf;
90 }
91
92 static void print_outbuf(void)
93 {
94         int sz = cur_outbuf - outbuf;
95         if (sz > 0) {
96                 xwrite(1, outbuf, sz);
97                 cur_outbuf = outbuf;
98         }
99 }
100
101 static void put(const char *s)
102 {
103         int sz = strlen(s);
104         if (sz > outbuf + sizeof(outbuf) - cur_outbuf)
105                 sz = outbuf + sizeof(outbuf) - cur_outbuf;
106         memcpy(cur_outbuf, s, sz);
107         cur_outbuf += sz;
108 }
109
110 static void put_c(char c)
111 {
112         if (cur_outbuf < outbuf + sizeof(outbuf))
113                 *cur_outbuf++ = c;
114 }
115
116 static void put_question_marks(int count)
117 {
118         while (count--)
119                 put_c('?');
120 }
121
122 static void readfile_z(char *buf, int sz, const char* fname)
123 {
124 // open_read_close() will do two reads in order to be sure we are at EOF,
125 // and we don't need/want that.
126 //      sz = open_read_close(fname, buf, sz-1);
127
128         int fd = xopen(fname, O_RDONLY);
129         buf[0] = '\0';
130         if (fd >= 0) {
131                 sz = read(fd, buf, sz-1);
132                 if (sz > 0) buf[sz] = '\0';
133                 close(fd);
134         }
135 }
136
137 static const char* get_file(proc_file *pf)
138 {
139         if (pf->last_gen != gen) {
140                 pf->last_gen = gen;
141                 // We allocate PROC_FILE_SIZE bytes. This wastes memory,
142                 // but allows us to allocate only once (at first sample)
143                 // per proc file, and reuse buffer for each sample
144                 if (!pf->file)
145                         pf->file = xmalloc(PROC_FILE_SIZE);
146                 readfile_z(pf->file, PROC_FILE_SIZE, proc_name[pf - &first_proc_file]);
147         }
148         return pf->file;
149 }
150
151 static inline ullong read_after_slash(const char *p)
152 {
153         p = strchr(p, '/');
154         if (!p) return 0;
155         return strtoull(p+1, NULL, 10);
156 }
157
158 enum conv_type { conv_decimal, conv_slash };
159
160 // Reads decimal values from line. Values start after key, for example:
161 // "cpu  649369 0 341297 4336769..." - key is "cpu" here.
162 // Values are stored in vec[]. arg_ptr has list of positions
163 // we are interested in: for example: 1,2,5 - we want 1st, 2nd and 5th value.
164 static int vrdval(const char* p, const char* key,
165         enum conv_type conv, ullong *vec, va_list arg_ptr)
166 {
167         int indexline;
168         int indexnext;
169
170         p = strstr(p, key);
171         if (!p) return 1;
172
173         p += strlen(key);
174         indexline = 1;
175         indexnext = va_arg(arg_ptr, int);
176         while (1) {
177                 while (*p == ' ' || *p == '\t') p++;
178                 if (*p == '\n' || *p == '\0') break;
179
180                 if (indexline == indexnext) { // read this value
181                         *vec++ = conv==conv_decimal ?
182                                 strtoull(p, NULL, 10) :
183                                 read_after_slash(p);
184                         indexnext = va_arg(arg_ptr, int);
185                 }
186                 while (*p > ' ') p++; // skip over value
187                 indexline++;
188         }
189         return 0;
190 }
191
192 // Parses files with lines like "cpu0 21727 0 15718 1813856 9461 10485 0 0":
193 // rdval(file_contents, "string_to_find", result_vector, value#, value#...)
194 // value# start with 1
195 static int rdval(const char* p, const char* key, ullong *vec, ...)
196 {
197         va_list arg_ptr;
198         int result;
199
200         va_start(arg_ptr, vec);
201         result = vrdval(p, key, conv_decimal, vec, arg_ptr);
202         va_end(arg_ptr);
203
204         return result;
205 }
206
207 // Parses files with lines like "... ... ... 3/148 ...."
208 static int rdval_loadavg(const char* p, ullong *vec, ...)
209 {
210         va_list arg_ptr;
211         int result;
212
213         va_start(arg_ptr, vec);
214         result = vrdval(p, "", conv_slash, vec, arg_ptr);
215         va_end(arg_ptr);
216
217         return result;
218 }
219
220 // Parses /proc/diskstats
221 //   1  2 3   4  5        6(rd)  7      8     9     10(wr) 11     12 13     14
222 //   3  0 hda 51292 14441 841783 926052 25717 79650 843256 3029804 0 148459 3956933
223 //   3  1 hda1 0 0 0 0 <- ignore if only 4 fields
224 static int rdval_diskstats(const char* p, ullong *vec)
225 {
226         ullong rd = 0; // to avoid "warning: 'rd' might be used uninitialized"
227         int indexline = 0;
228         vec[0] = 0;
229         vec[1] = 0;
230         while (1) {
231                 indexline++;
232                 while (*p == ' ' || *p == '\t') p++;
233                 if (*p == '\0') break;
234                 if (*p == '\n') {
235                         indexline = 0;
236                         p++;
237                         continue;
238                 }
239                 if (indexline == 6) {
240                         rd = strtoull(p, NULL, 10);
241                 } else if (indexline == 10) {
242                         vec[0] += rd;  // TODO: *sectorsize (don't know how to find out sectorsize)
243                         vec[1] += strtoull(p, NULL, 10);
244                         while (*p != '\n' && *p != '\0') p++;
245                         continue;
246                 }
247                 while (*p > ' ') p++; // skip over value
248         }
249         return 0;
250 }
251
252 static void scale(ullong ul)
253 {
254         char buf[5];
255         smart_ulltoa5(ul, buf);
256         put(buf);
257 }
258
259
260 #define S_STAT(a) \
261 typedef struct a { \
262         struct s_stat *next; \
263         void (*collect)(struct a *s); \
264         const char *label;
265 #define S_STAT_END(a) } a;
266
267 S_STAT(s_stat)
268 S_STAT_END(s_stat)
269
270 static void collect_literal(s_stat *s)
271 {
272 }
273
274 static s_stat* init_literal(void)
275 {
276         s_stat *s = xmalloc(sizeof(s_stat));
277         s->collect = collect_literal;
278         return (s_stat*)s;
279 }
280
281 static s_stat* init_delay(const char *param)
282 {
283         delta = bb_strtoi(param, NULL, 0) * 1000;
284         deltanz = delta > 0 ? delta : 1;
285         need_seconds = (1000000%deltanz) != 0;
286         return NULL;
287 }
288
289 static s_stat* init_cr(const char *param)
290 {
291         final_str = "\r";
292         return (s_stat*)0;
293 }
294
295
296 //     user nice system idle  iowait irq  softirq (last 3 only in 2.6)
297 //cpu  649369 0 341297 4336769 11640 7122 1183
298 //cpuN 649369 0 341297 4336769 11640 7122 1183
299 enum { CPU_FIELDCNT = 7 };
300 S_STAT(cpu_stat)
301         ullong old[CPU_FIELDCNT];
302         int bar_sz;
303         char *bar;
304 S_STAT_END(cpu_stat)
305
306
307 static void collect_cpu(cpu_stat *s)
308 {
309         ullong data[CPU_FIELDCNT] = { 0, 0, 0, 0, 0, 0, 0 };
310         unsigned frac[CPU_FIELDCNT] = { 0, 0, 0, 0, 0, 0, 0 };
311         ullong all = 0;
312         int norm_all = 0;
313         int bar_sz = s->bar_sz;
314         char *bar = s->bar;
315         int i;
316
317         if (rdval(get_file(&proc_stat), "cpu ", data, 1, 2, 3, 4, 5, 6, 7)) {
318                 put_question_marks(bar_sz);
319                 return;
320         }
321
322         for (i = 0; i < CPU_FIELDCNT; i++) {
323                 ullong old = s->old[i];
324                 if (data[i] < old) old = data[i];               //sanitize
325                 s->old[i] = data[i];
326                 all += (data[i] -= old);
327         }
328
329         if (all) {
330                 for (i = 0; i < CPU_FIELDCNT; i++) {
331                         ullong t = bar_sz * data[i];
332                         norm_all += data[i] = t / all;
333                         frac[i] = t % all;
334                 }
335
336                 while (norm_all < bar_sz) {
337                         unsigned max = frac[0];
338                         int pos = 0;
339                         for (i = 1; i < CPU_FIELDCNT; i++) {
340                                 if (frac[i] > max) max = frac[i], pos = i;
341                         }
342                         frac[pos] = 0;  //avoid bumping up same value twice
343                         data[pos]++;
344                         norm_all++;
345                 }
346
347                 memset(bar, '.', bar_sz);
348                 memset(bar, 'S', data[2]); bar += data[2]; //sys
349                 memset(bar, 'U', data[0]); bar += data[0]; //usr
350                 memset(bar, 'N', data[1]); bar += data[1]; //nice
351                 memset(bar, 'D', data[4]); bar += data[4]; //iowait
352                 memset(bar, 'I', data[5]); bar += data[5]; //irq
353                 memset(bar, 'i', data[6]); bar += data[6]; //softirq
354         } else {
355                 memset(bar, '?', bar_sz);
356         }
357         put(s->bar);
358 }
359
360
361 static s_stat* init_cpu(const char *param)
362 {
363         int sz;
364         cpu_stat *s = xmalloc(sizeof(cpu_stat));
365         s->collect = collect_cpu;
366         sz = strtol(param, NULL, 0);
367         if (sz < 10) sz = 10;
368         if (sz > 1000) sz = 1000;
369         s->bar = xmalloc(sz+1);
370         s->bar[sz] = '\0';
371         s->bar_sz = sz;
372         return (s_stat*)s;
373 }
374
375
376 S_STAT(int_stat)
377         ullong old;
378         int no;
379 S_STAT_END(int_stat)
380
381 static void collect_int(int_stat *s)
382 {
383         ullong data[1];
384         ullong old;
385
386         if (rdval(get_file(&proc_stat), "intr", data, s->no)) {
387                 put_question_marks(4);
388                 return;
389         }
390
391         old = s->old;
392         if (data[0] < old) old = data[0];               //sanitize
393         s->old = data[0];
394         scale(data[0] - old);
395 }
396
397 static s_stat* init_int(const char *param)
398 {
399         int_stat *s = xmalloc(sizeof(int_stat));
400         s->collect = collect_int;
401         if (param[0]=='\0') {
402                 s->no = 1;
403         } else {
404                 int n = strtoul(param, NULL, 0);
405                 s->no = n+2;
406         }
407         return (s_stat*)s;
408 }
409
410
411 S_STAT(ctx_stat)
412         ullong old;
413 S_STAT_END(ctx_stat)
414
415 static void collect_ctx(ctx_stat *s)
416 {
417         ullong data[1];
418         ullong old;
419
420         if (rdval(get_file(&proc_stat), "ctxt", data, 1)) {
421                 put_question_marks(4);
422                 return;
423         }
424
425         old = s->old;
426         if (data[0] < old) old = data[0];               //sanitize
427         s->old = data[0];
428         scale(data[0] - old);
429 }
430
431 static s_stat* init_ctx(const char *param)
432 {
433         ctx_stat *s = xmalloc(sizeof(ctx_stat));
434         s->collect = collect_ctx;
435         return (s_stat*)s;
436 }
437
438
439 S_STAT(blk_stat)
440         const char* lookfor;
441         ullong old[2];
442 S_STAT_END(blk_stat)
443
444 static void collect_blk(blk_stat *s)
445 {
446         ullong data[2];
447         int i;
448
449         if (is26) {
450                 i = rdval_diskstats(get_file(&proc_diskstats), data);
451         } else {
452                 i = rdval(get_file(&proc_stat), s->lookfor, data, 1, 2);
453                 // Linux 2.4 reports bio in Kbytes, convert to sectors:
454                 data[0] *= 2;
455                 data[1] *= 2;
456         }
457         if (i) {
458                 put_question_marks(9);
459                 return;
460         }
461
462         for (i=0; i<2; i++) {
463                 ullong old = s->old[i];
464                 if (data[i] < old) old = data[i];               //sanitize
465                 s->old[i] = data[i];
466                 data[i] -= old;
467         }
468         scale(data[0]*512); // TODO: *sectorsize
469         put_c(' ');
470         scale(data[1]*512);
471 }
472
473 static s_stat* init_blk(const char *param)
474 {
475         blk_stat *s = xmalloc(sizeof(blk_stat));
476         s->collect = collect_blk;
477         s->lookfor = "page";
478         return (s_stat*)s;
479 }
480
481
482 S_STAT(fork_stat)
483         ullong old;
484 S_STAT_END(fork_stat)
485
486 static void collect_thread_nr(fork_stat *s)
487 {
488         ullong data[1];
489
490         if (rdval_loadavg(get_file(&proc_loadavg), data, 4)) {
491                 put_question_marks(4);
492                 return;
493         }
494         scale(data[0]);
495 }
496
497 static void collect_fork(fork_stat *s)
498 {
499         ullong data[1];
500         ullong old;
501
502         if (rdval(get_file(&proc_stat), "processes", data, 1)) {
503                 put_question_marks(4);
504                 return;
505         }
506
507         old = s->old;
508         if (data[0] < old) old = data[0];       //sanitize
509         s->old = data[0];
510         scale(data[0] - old);
511 }
512
513 static s_stat* init_fork(const char *param)
514 {
515         fork_stat *s = xmalloc(sizeof(fork_stat));
516         if (*param == 'n') {
517                 s->collect = collect_thread_nr;
518         } else {
519                 s->collect = collect_fork;
520         }
521         return (s_stat*)s;
522 }
523
524
525 S_STAT(if_stat)
526         ullong old[4];
527         const char *device;
528         char *device_colon;
529 S_STAT_END(if_stat)
530
531 static void collect_if(if_stat *s)
532 {
533         ullong data[4];
534         int i;
535
536         if (rdval(get_file(&proc_net_dev), s->device_colon, data, 1, 3, 9, 11)) {
537                 put_question_marks(10);
538                 return;
539         }
540
541         for (i=0; i<4; i++) {
542                 ullong old = s->old[i];
543                 if (data[i] < old) old = data[i];               //sanitize
544                 s->old[i] = data[i];
545                 data[i] -= old;
546         }
547         put_c(data[1] ? '*' : ' ');
548         scale(data[0]);
549         put_c(data[3] ? '*' : ' ');
550         scale(data[2]);
551 }
552
553 static s_stat* init_if(const char *device)
554 {
555         if_stat *s = xmalloc(sizeof(if_stat));
556
557         if (!device || !device[0])
558                 bb_show_usage();
559         s->collect = collect_if;
560
561         s->device = device;
562         s->device_colon = xmalloc(strlen(device)+2);
563         strcpy(s->device_colon, device);
564         strcat(s->device_colon, ":");
565         return (s_stat*)s;
566 }
567
568
569 S_STAT(mem_stat)
570         char opt;
571 S_STAT_END(mem_stat)
572
573 // "Memory" value should not include any caches.
574 // IOW: neither "ls -laR /" nor heavy read/write activity
575 //      should affect it. We'd like to also include any
576 //      long-term allocated kernel-side mem, but it is hard
577 //      to figure out. For now, bufs, cached & slab are
578 //      counted as "free" memory
579 //2.6.16:
580 //MemTotal:       773280 kB
581 //MemFree:         25912 kB - genuinely free
582 //Buffers:        320672 kB - cache
583 //Cached:         146396 kB - cache
584 //SwapCached:          0 kB
585 //Active:         183064 kB
586 //Inactive:       356892 kB
587 //HighTotal:           0 kB
588 //HighFree:            0 kB
589 //LowTotal:       773280 kB
590 //LowFree:         25912 kB
591 //SwapTotal:      131064 kB
592 //SwapFree:       131064 kB
593 //Dirty:              48 kB
594 //Writeback:           0 kB
595 //Mapped:          96620 kB
596 //Slab:           200668 kB - takes 7 Mb on my box fresh after boot,
597 //                            but includes dentries and inodes
598 //                            (== can take arbitrary amount of mem)
599 //CommitLimit:    517704 kB
600 //Committed_AS:   236776 kB
601 //PageTables:       1248 kB
602 //VmallocTotal:   516052 kB
603 //VmallocUsed:      3852 kB
604 //VmallocChunk:   512096 kB
605 //HugePages_Total:     0
606 //HugePages_Free:      0
607 //Hugepagesize:     4096 kB
608 static void collect_mem(mem_stat *s)
609 {
610         ullong m_total = 0;
611         ullong m_free = 0;
612         ullong m_bufs = 0;
613         ullong m_cached = 0;
614         ullong m_slab = 0;
615
616         if (rdval(get_file(&proc_meminfo), "MemTotal:", &m_total, 1)) {
617                 put_question_marks(4);
618                 return;
619         }
620         if (s->opt == 'f') {
621                 scale(m_total << 10);
622                 return;
623         }
624
625         if (rdval(proc_meminfo.file, "MemFree:", &m_free  , 1)
626          || rdval(proc_meminfo.file, "Buffers:", &m_bufs  , 1)
627          || rdval(proc_meminfo.file, "Cached:",  &m_cached, 1)
628          || rdval(proc_meminfo.file, "Slab:",    &m_slab  , 1)
629         ) {
630                 put_question_marks(4);
631                 return;
632         }
633
634         m_free += m_bufs + m_cached + m_slab;
635         switch (s->opt) {
636         case 'f':
637                 scale(m_free << 10); break;
638         default:
639                 scale((m_total - m_free) << 10); break;
640         }
641 }
642
643 static s_stat* init_mem(const char *param)
644 {
645         mem_stat *s = xmalloc(sizeof(mem_stat));
646         s->collect = collect_mem;
647         s->opt = param[0];
648         return (s_stat*)s;
649 }
650
651
652 S_STAT(swp_stat)
653 S_STAT_END(swp_stat)
654
655 static void collect_swp(swp_stat *s)
656 {
657         ullong s_total[1];
658         ullong s_free[1];
659         if (rdval(get_file(&proc_meminfo), "SwapTotal:", s_total, 1)
660          || rdval(proc_meminfo.file,       "SwapFree:" , s_free,  1)
661         ) {
662                 put_question_marks(4);
663                 return;
664         }
665         scale((s_total[0]-s_free[0]) << 10);
666 }
667
668 static s_stat* init_swp(const char *param)
669 {
670         swp_stat *s = xmalloc(sizeof(swp_stat));
671         s->collect = collect_swp;
672         return (s_stat*)s;
673 }
674
675
676 S_STAT(fd_stat)
677 S_STAT_END(fd_stat)
678
679 static void collect_fd(fd_stat *s)
680 {
681         ullong data[2];
682
683         if (rdval(get_file(&proc_sys_fs_filenr), "", data, 1, 2)) {
684                 put_question_marks(4);
685                 return;
686         }
687
688         scale(data[0] - data[1]);
689 }
690
691 static s_stat* init_fd(const char *param)
692 {
693         fd_stat *s = xmalloc(sizeof(fd_stat));
694         s->collect = collect_fd;
695         return (s_stat*)s;
696 }
697
698
699 S_STAT(time_stat)
700         int prec;
701         int scale;
702 S_STAT_END(time_stat)
703
704 static void collect_time(time_stat *s)
705 {
706         char buf[sizeof("12:34:56.123456")];
707         struct tm* tm;
708         int us = tv.tv_usec + s->scale/2;
709         time_t t = tv.tv_sec;
710
711         if (us >= 1000000) {
712                 t++;
713                 us -= 1000000;
714         }
715         tm = localtime(&t);
716
717         sprintf(buf, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec);
718         if (s->prec)
719                 sprintf(buf+8, ".%0*d", s->prec, us / s->scale);
720         put(buf);
721 }
722
723 static s_stat* init_time(const char *param)
724 {
725         int prec;
726         time_stat *s = xmalloc(sizeof(time_stat));
727
728         s->collect = collect_time;
729         prec = param[0]-'0';
730         if (prec < 0) prec = 0;
731         else if (prec > 6) prec = 6;
732         s->prec = prec;
733         s->scale = 1;
734         while (prec++ < 6)
735                 s->scale *= 10;
736         return (s_stat*)s;
737 }
738
739 static void collect_info(s_stat *s)
740 {
741         gen ^= 1;
742         while (s) {
743                 put(s->label);
744                 s->collect(s);
745                 s = s->next;
746         }
747 }
748
749
750 typedef s_stat* init_func(const char *param);
751
752 static const char options[] = "ncmsfixptbdr";
753 static init_func *const init_functions[] = {
754         init_if,
755         init_cpu,
756         init_mem,
757         init_swp,
758         init_fd,
759         init_int,
760         init_ctx,
761         init_fork,
762         init_time,
763         init_blk,
764         init_delay,
765         init_cr,
766 };
767
768 int nmeter_main(int argc, char **argv);
769 int nmeter_main(int argc, char **argv)
770 {
771         char buf[32];
772         s_stat *first = NULL;
773         s_stat *last = NULL;
774         s_stat *s;
775         char *cur, *prev;
776
777         INIT_G();
778
779         xchdir("/proc");
780
781         if (argc != 2)
782                 bb_show_usage();
783
784         if (open_read_close("version", buf, sizeof(buf)) > 0)
785                 is26 = (strstr(buf, " 2.4.")==NULL);
786
787         // Can use argv[1] directly, but this will mess up
788         // parameters as seen by e.g. ps. Making a copy...
789         cur = xstrdup(argv[1]);
790         while (1) {
791                 char *param, *p;
792                 prev = cur;
793  again:
794                 cur = strchr(cur, '%');
795                 if (!cur)
796                         break;
797                 if (cur[1] == '%') {    // %%
798                         strcpy(cur, cur+1);
799                         cur++;
800                         goto again;
801                 }
802                 *cur++ = '\0';          // overwrite %
803                 if (cur[0] == '[') {
804                         // format: %[foptstring]
805                         cur++;
806                         p = strchr(options, cur[0]);
807                         param = cur+1;
808                         while (cur[0] != ']') {
809                                 if (!cur[0])
810                                         bb_show_usage();
811                                 cur++;
812                         }
813                         *cur++ = '\0';  // overwrite [
814                 } else {
815                         // format: %NNNNNNf
816                         param = cur;
817                         while (cur[0] >= '0' && cur[0] <= '9')
818                                 cur++;
819                         if (!cur[0])
820                                 bb_show_usage();
821                         p = strchr(options, cur[0]);
822                         *cur++ = '\0';  // overwrite format char
823                 }
824                 if (!p)
825                         bb_show_usage();
826                 s = init_functions[p-options](param);
827                 if (s) {
828                         s->label = prev;
829                         s->next = 0;
830                         if (!first)
831                                 first = s;
832                         else
833                                 last->next = s;
834                         last = s;
835                 } else {
836                         // %NNNNd or %r option. remove it from string
837                         strcpy(prev + strlen(prev), cur);
838                         cur = prev;
839                 }
840         }
841         if (prev[0]) {
842                 s = init_literal();
843                 s->label = prev;
844                 s->next = 0;
845                 if (!first)
846                         first = s;
847                 else
848                         last->next = s;
849                 last = s;
850         }
851
852         // Generate first samples but do not print them, they're bogus
853         collect_info(first);
854         reset_outbuf();
855         if (delta >= 0) {
856                 gettimeofday(&tv, NULL);
857                 usleep(delta > 1000000 ? 1000000 : delta - tv.tv_usec%deltanz);
858         }
859
860         while (1) {
861                 gettimeofday(&tv, NULL);
862                 collect_info(first);
863                 put(final_str);
864                 print_outbuf();
865
866                 // Negative delta -> no usleep at all
867                 // This will hog the CPU but you can have REALLY GOOD
868                 // time resolution ;)
869                 // TODO: detect and avoid useless updates
870                 // (like: nothing happens except time)
871                 if (delta >= 0) {
872                         int rem;
873                         // can be commented out, will sacrifice sleep time precision a bit
874                         gettimeofday(&tv, NULL);
875                         if (need_seconds)
876                                 rem = delta - ((ullong)tv.tv_sec*1000000 + tv.tv_usec) % deltanz;
877                         else
878                                 rem = delta - tv.tv_usec%deltanz;
879                         // Sometimes kernel wakes us up just a tiny bit earlier than asked
880                         // Do not go to very short sleep in this case
881                         if (rem < delta/128) {
882                                 rem += delta;
883                         }
884                         usleep(rem);
885                 }
886         }
887
888         /*return 0;*/
889 }