wmbiff: fixed possible null-pointer dereference.
[dockapps.git] / wmtop / wmtop.c
blobbf065ded8752d47dab5e21d013d943cc99b6704b
1 /******************************************/
2 /* WMTOP - Mini top in a dock app */
3 /******************************************/
5 /*
6 * wmtop.c -- WindowMaker process view dock app
7 * Derived by Dan Piponi dan@tanelorn.demon.co.uk
8 * http://www.tanelorn.demon.co.uk
9 * http://wmtop.sourceforge.net
10 * from code originally contained in wmsysmon by Dave Clark (clarkd@skynet.ca)
11 * This software is licensed through the GNU General Public License.
15 * Ensure there's an operating system defined. There is *no* default
16 * because every OS has it's own way of revealing CPU/memory usage.
18 #if defined(FREEBSD)
19 #define OS_DEFINED
20 #endif /* defined(FREEBSD) */
22 #if defined(LINUX)
23 #define OS_DEFINED
24 #endif /* defined(LINUX) */
26 #if !defined(OS_DEFINED)
27 #error No operating system selected
28 #endif /* !defined(OS_DEFINED) */
30 #define _BSD_SOURCE
32 /******************************************/
33 /* Includes */
34 /******************************************/
36 #include <stdlib.h>
37 #include <stdio.h>
38 #include <time.h>
39 #include <dirent.h>
40 #include <string.h>
41 #include <fcntl.h>
42 #include <unistd.h>
43 #include <ctype.h>
44 #include <math.h>
45 #include <limits.h>
46 #include <errno.h>
47 #include <signal.h>
49 #if defined(PARANOID)
50 #include <assert.h>
51 #endif /* defined(PARANOID) */
53 #include <sys/wait.h>
54 #include <sys/stat.h>
55 #include <sys/param.h>
56 #include <sys/types.h>
57 #include <sys/ioctl.h>
58 #include <sys/time.h>
60 #include <X11/Xlib.h>
61 #include <X11/xpm.h>
62 #include <X11/extensions/shape.h>
63 #include <X11/keysym.h>
65 #include <regex.h>
67 #include <libdockapp/wmgeneral.h>
68 #include <libdockapp/misc.h>
69 #include "xpm/wmtop-default.xpm"
70 #include "xpm/wmtop-lcd.xpm"
71 #include "xpm/wmtop-neon1.xpm"
72 #include "xpm/wmtop-neon2.xpm"
73 #include "xpm/wmtop-rainbow.xpm"
75 /******************************************/
76 /* Defines */
77 /******************************************/
80 * XXX: I shouldn't really use this WMTOP_BUFLENGTH variable but scanf is so
81 * lame and it'll take me a while to write a replacement.
83 #define WMTOP_BUFLENGTH 1024
85 #if defined(LINUX)
86 #define PROCFS_TEMPLATE "/proc/%d/stat"
87 #define PROCFS_CMDLINE_TEMPLATE "/proc/%d/cmdline"
88 #endif /* defined(LINUX) */
90 #if defined(FREEBSD)
91 #define PROCFS_TEMPLATE "/proc/%d/status"
92 #endif /* defined(FREEBSD) */
94 /******************************************/
95 /* Globals */
96 /******************************************/
98 regex_t *exclusion_expression = 0;
99 uid_t user = (uid_t) -1;
100 char *process_command = 0;
102 * Default mode: zero=cpu one=memory
104 int mode = 0;
107 * Number and default artistic styles.
109 int nstyles = 5;
110 int style = 0;
112 char wmtop_mask_bits[64*64];
113 int wmtop_mask_width = 64;
114 int wmtop_mask_height = 64;
116 int update_rate = 1000000;
117 int refresh_rate = 100000;
119 extern char **environ;
121 char *ProgName;
123 /******************************************/
124 /* Debug */
125 /******************************************/
127 #if defined(DEBUG)
129 * Memory handler
131 int g_malloced = 0;
133 void *wmtop_malloc(int n) {
134 int *p = (int *)malloc(sizeof(int)+n);
135 p[0] = n;
136 g_malloced += n;
137 return (void *)(p+1);
140 void wmtop_free(void *n) {
141 int *p = (int *)n;
142 g_malloced -= p[-1];
143 free(p-1);
146 void show_memory() {
147 fprintf(stderr,"%d bytes allocated\n",g_malloced);
149 #else /* defined(DEBUG) */
150 #define wmtop_malloc malloc
151 #define wmtop_free free
152 #endif /* defined(DEBUG) */
154 char *wmtop_strdup(const char *s) {
155 return strcpy((char *)wmtop_malloc(strlen(s)+1),s);
158 /******************************************/
159 /* Structures */
160 /******************************************/
162 struct {
163 char **pixmap;
164 char *description;
165 } styles[] = {
166 { wmtop_default_xpm, "Light emitting diode (default)" },
167 { wmtop_lcd_xpm, "Liquid crystal display" },
168 { wmtop_rainbow_xpm, "Rainbow display" },
169 { wmtop_neon1_xpm, "Neon lights" },
170 { wmtop_neon2_xpm, "More neon lights" },
173 struct process {
174 #if defined(PARANOID)
175 long id;
176 #endif /* defined(PARANOID) */
178 * Store processes in a doubly linked list
180 struct process *next;
181 struct process *previous;
183 pid_t pid;
184 char *name;
185 float amount;
186 unsigned long user_time;
187 unsigned long kernel_time;
188 unsigned long previous_user_time;
189 unsigned long previous_kernel_time;
190 unsigned long vsize;
191 long rss;
192 int time_stamp;
193 int counted;
196 /******************************************/
197 /* Process class */
198 /******************************************/
201 * Global pointer to head of process list
203 struct process *first_process = 0;
205 int g_time = 0;
207 struct process *find_process(pid_t pid) {
208 struct process *p = first_process;
209 while (p) {
210 if (p->pid==pid)
211 return p;
212 p = p->next;
214 return 0;
218 * Create a new process object and insert it into the process list
220 struct process *new_process(int p) {
221 struct process *process;
222 process = wmtop_malloc(sizeof(struct process));
224 #if defined(PARANOID)
225 process->id = 0x0badfeed;
226 #endif /* defined(PARANOID) */
229 * Do stitching necessary for doubly linked list
231 process->name = 0;
232 process->previous = 0;
233 process->next = first_process;
234 if (process->next)
235 process->next->previous = process;
236 first_process = process;
238 process->pid = p;
239 process->time_stamp = 0;
240 process->previous_user_time = ULONG_MAX;
241 process->previous_kernel_time = ULONG_MAX;
242 process->counted = 1;
244 /* process_find_name(process);*/
246 return process;
249 /******************************************/
250 /* Functions */
251 /******************************************/
253 void wmtop_routine(int, char **);
254 int process_parse_procfs(struct process *);
255 int update_process_table(void);
256 int calculate_cpu(struct process *);
257 void process_cleanup(void);
258 void delete_process(struct process *);
259 void draw_processes(void);
260 unsigned long calc_cpu_total(void);
261 void calc_cpu_each(unsigned long total);
262 #if defined(LINUX)
263 unsigned long calc_mem_total(void);
264 void calc_mem_each(unsigned long total);
265 #endif
266 int process_find_top_three(struct process **);
267 void draw_bar(int, int, int, int, float, int, int);
268 void blit_string(char *, int, int);
269 void usage(void);
270 void printversion(void);
272 /******************************************/
273 /* Main */
274 /******************************************/
276 int main(int argc, char *argv[]) {
277 int i;
278 struct stat sbuf;
281 * Make sure we have a /proc filesystem. No point in continuing if we
282 * haven't!
284 if (stat("/proc",&sbuf)<0) {
285 fprintf(stderr,
286 "No /proc filesystem present. Unable to obtain processor info.\n");
287 exit(1);
291 * Parse Command Line
294 ProgName = argv[0];
295 if (strlen(ProgName) >= 5)
296 ProgName += strlen(ProgName) - 5;
298 for (i = 1; i<argc; i++) {
299 char *arg = argv[i];
301 if (*arg=='-') {
302 switch (arg[1]) {
303 case 'x' :
304 if (argc>i+1) {
305 static regex_t reg;
306 exclusion_expression = &reg;
307 regcomp(exclusion_expression,argv[i+1],REG_EXTENDED);
308 i++;
309 } else {
310 usage();
311 exit(1);
313 break;
314 case 'c' :
315 if (argc>i+1) {
316 process_command = argv[i+1];
317 i++;
318 break;
319 } else {
320 usage();
321 exit(1);
323 #if defined(LINUX)
324 case 'm':
326 * Display memory
328 mode = 1;
329 break;
330 #endif /* defined(LINUX) */
331 case 'd' :
332 if (strcmp(arg+1, "display")) {
333 usage();
334 exit(1);
336 break;
337 case 'g' :
338 if (strcmp(arg+1, "geometry")) {
339 usage();
340 exit(1);
342 break;
343 case 'v' :
344 printversion();
345 exit(0);
346 break;
347 case 'U' :
348 user = getuid();
349 break;
350 case 's':
351 if (argc > (i+1)) {
352 update_rate = (atoi(argv[i+1]) * 1000);
353 i++;
355 break;
356 case 'r':
357 if (argc > (i+1)) {
358 refresh_rate = (atoi(argv[i+1]) * 1000);
359 i++;
361 break;
362 case 'a':
363 if (argc > (i+1)) {
364 if (atoi(argv[i+1]) < 1 || atoi(argv[i+1]) > nstyles) {
365 usage();
366 exit(1);
368 style = atoi(argv[i+1]) - 1;
369 i++;
371 break;
372 default:
373 usage();
374 exit(0);
375 break;
380 wmtop_routine(argc, argv);
382 return 0;
385 /******************************************/
386 /* Main routine */
387 /******************************************/
389 void wmtop_routine(int argc, char **argv) {
390 XEvent Event;
391 struct timeval tv={0,0};
392 struct timeval last={0,0};
393 int count = update_rate;
395 createXBMfromXPM(wmtop_mask_bits, styles[style].pixmap, wmtop_mask_width, wmtop_mask_height);
397 openXwindow(argc, argv, styles[style].pixmap, wmtop_mask_bits, wmtop_mask_width, wmtop_mask_height);
400 while (1) {
402 waitpid(0, NULL, WNOHANG);
404 if (count>=update_rate) {
405 memcpy(&last,&tv,sizeof(tv));
408 * Update display
410 draw_processes();
412 RedrawWindow();
413 count = 0;
417 * X Events
419 while (XPending(display)) {
420 XNextEvent(display, &Event);
421 switch (Event.type) {
422 case Expose:
423 RedrawWindow();
424 break;
425 case DestroyNotify:
426 XCloseDisplay(display);
427 exit(0);
428 case ButtonPress:
429 #if defined(LINUX)
430 if (Event.xbutton.button==1)
431 mode = !mode;
432 #endif
433 if (Event.xbutton.button==2) {
434 if (user==(uid_t)-1)
435 user=getuid();
436 else
437 user=-1;
439 if (Event.xbutton.button==3 && process_command)
440 execCommand(process_command);
441 break;
444 usleep(refresh_rate);
445 count = count + refresh_rate;
449 /******************************************/
450 /* Extract information from /proc */
451 /******************************************/
454 * These are the guts that extract information out of /proc.
455 * Anyone hoping to port wmtop should look here first.
457 int process_parse_procfs(struct process *process) {
458 char line[WMTOP_BUFLENGTH],filename[WMTOP_BUFLENGTH],procname[WMTOP_BUFLENGTH];
459 int ps;
460 struct stat sbuf;
461 unsigned long user_time,kernel_time;
462 int rc;
463 #if defined(LINUX)
464 char *r,*q;
465 char deparenthesised_name[WMTOP_BUFLENGTH];
466 int endl;
467 #endif /* defined(LINUX) */
468 #if defined(FREEBSD)
469 /* TODO: needs analysis. Probably needs same data type fix as LINUX (use
470 * long types). Need to check FreeBSD docs and test. -wbk */
471 int us,um,ks,km;
472 #endif /* defined(FREEBSD) */
474 #if defined(PARANOID)
475 assert(process->id==0x0badfeed);
476 #endif /* defined(PARANOID) */
478 sprintf(filename,PROCFS_TEMPLATE,process->pid);
481 * Permissions of /proc filesystem are permissions of process too
483 if (user!=(uid_t)-1) {
484 stat(filename,&sbuf);
485 if (sbuf.st_uid!=user)
486 return 1;
489 ps = open(filename,O_RDONLY);
490 if (ps<0)
492 * The process must have finished in the last few jiffies!
494 return 1;
497 * Mark process as up-to-date.
499 process->time_stamp = g_time;
501 rc = read(ps,line,sizeof(line));
502 close(ps);
503 if (rc<0)
504 return 1;
506 #if defined(LINUX)
508 * Extract cpu times from data in /proc filesystem.
509 * For conversion types see man proc(5).
511 rc = sscanf(line,"%*s %s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %lu %lu %*s %*s %*s %*s %*s %*s %*s %lu %ld",
512 procname,
513 &process->user_time,&process->kernel_time,
514 &process->vsize,&process->rss);
515 if (rc<5)
516 return 1;
518 * Remove parentheses from the process name stored in /proc/ under Linux...
520 r = procname+1;
521 /* remove any "kdeinit: " */
522 if (r == strstr(r, "kdeinit"))
524 sprintf(filename,PROCFS_CMDLINE_TEMPLATE,process->pid);
527 * Permissions of /proc filesystem are permissions of process too
529 if (user!=(uid_t)-1) {
530 stat(filename,&sbuf);
531 if (sbuf.st_uid!=user)
532 return 1;
535 ps = open(filename,O_RDONLY);
536 if (ps<0)
538 * The process must have finished in the last few jiffies!
540 return 1;
542 endl = read(ps,line,sizeof(line));
543 close(ps);
545 /* null terminate the input */
546 line[endl]=0;
547 /* account for "kdeinit: " */
548 if ((char*)line == strstr(line, "kdeinit: "))
549 r = ((char*)line)+9;
550 else
551 r = (char*)line;
553 q = deparenthesised_name;
554 /* stop at space */
555 while (*r && *r!=' ')
556 *q++ = *r++;
557 *q = 0;
559 else
561 q = deparenthesised_name;
562 while (*r && *r!=')')
563 *q++ = *r++;
564 *q = 0;
567 if (process->name)
568 wmtop_free(process->name);
569 process->name = wmtop_strdup(deparenthesised_name);
570 #endif /* defined(LINUX) */
572 #if defined(FREEBSD)
574 * Extract cpu times from data in /proc/<pid>/stat
575 * XXX: Process name extractor for FreeBSD is untested right now.
577 * [TODO: FREEBSD code probably needs similar data type changes to
578 * those made for LINUX above. Need to check docs. -wbk]
580 rc = sscanf(line,"%s %*s %*s %*s %*s %*s %*s %*s %d,%d %d,%d",
581 procname,
582 &us,&um,&ks,&km);
583 if (rc<5)
584 return 1;
585 if (process->name)
586 wmtop_free(process->name);
587 process->name = wmtop_strdup(procname);
588 process->user_time = us*1000+um/1000;
589 process->kernel_time = ks*1000+km/1000;
590 #endif /* defined(FREEBSD) */
592 /* not portable (especially unsuitable for redistributable executables.
593 * On some systems, getpagesize() is a preprocessor macro).
595 process->rss *= getpagesize();
597 if (process->previous_user_time==ULONG_MAX)
598 process->previous_user_time = process->user_time;
599 if (process->previous_kernel_time==ULONG_MAX)
600 process->previous_kernel_time = process->kernel_time;
602 user_time = process->user_time-process->previous_user_time;
603 kernel_time = process->kernel_time-process->previous_kernel_time;
605 process->previous_user_time = process->user_time;
606 process->previous_kernel_time = process->kernel_time;
608 process->user_time = user_time;
609 process->kernel_time = kernel_time;
611 return 0;
614 /******************************************/
615 /* Update process table */
616 /******************************************/
618 int update_process_table() {
619 DIR *dir;
620 struct dirent *entry;
622 if (!(dir = opendir("/proc")))
623 return 1;
626 * Get list of processes from /proc directory
628 while ((entry = readdir(dir))) {
629 pid_t pid;
631 if (!entry) {
633 * Problem reading list of processes
635 closedir(dir);
636 return 1;
639 if (sscanf(entry->d_name,"%d",&pid)>0) {
640 struct process *p;
641 p = find_process(pid);
642 if (!p)
643 p = new_process(pid);
645 calculate_cpu(p);
649 closedir(dir);
651 return 0;
654 /******************************************/
655 /* Get process structure for process pid */
656 /******************************************/
659 * This function seems to hog all of the CPU time. I can't figure out why - it
660 * doesn't do much.
662 int calculate_cpu(struct process *process) {
663 int rc;
665 #if defined(PARANOID)
666 assert(process->id==0x0badfeed);
667 #endif /* defined(PARANOID) */
669 rc = process_parse_procfs(process);
670 if (rc)
671 return 1;
674 * Check name against the exclusion list
676 if (process->counted && exclusion_expression && !regexec(exclusion_expression,process->name,0,0,0))
677 process->counted = 0;
679 return 0;
682 /******************************************/
683 /* Strip dead process entries */
684 /******************************************/
686 void process_cleanup() {
688 struct process *p = first_process;
689 while (p) {
690 struct process *current = p;
692 #if defined(PARANOID)
693 assert(p->id==0x0badfeed);
694 #endif /* defined(PARANOID) */
696 p = p->next;
698 * Delete processes that have died
700 if (current->time_stamp!=g_time)
701 delete_process(current);
705 /******************************************/
706 /* Destroy and remove a process */
707 /******************************************/
709 void delete_process(struct process *p) {
710 #if defined(PARANOID)
711 assert(p->id==0x0badfeed);
714 * Ensure that deleted processes aren't reused.
716 p->id = 0x007babe;
717 #endif /* defined(PARANOID) */
720 * Maintain doubly linked list.
722 if (p->next)
723 p->next->previous = p->previous;
724 if (p->previous)
725 p->previous->next = p->next;
726 else
727 first_process = p->next;
729 if (p->name)
730 wmtop_free(p->name);
731 wmtop_free(p);
734 /******************************************/
735 /* Generate display */
736 /******************************************/
738 void draw_processes() {
739 int i,n;
740 struct process *best[3] = { 0, 0, 0 };
741 unsigned long total;
744 * Invalidate time stamps
746 ++g_time;
748 update_process_table();
750 switch (mode) {
751 case 0:
752 total = calc_cpu_total();
753 calc_cpu_each(total);
754 break;
755 #if defined(LINUX)
756 case 1:
757 total = calc_mem_total();
758 calc_mem_each(total);
759 break;
760 #endif
763 process_cleanup();
766 * Find the top three!
768 n = process_find_top_three(best);
770 for (i = 0; i<3; ++i) {
771 int j;
772 char s[10];
773 strcpy(s," ");
774 if (i<n) {
775 for (j = 0; j<9; ++j) {
776 char c;
777 c = best[i]->name[j];
778 if (c)
779 s[j] = c;
780 else
781 break;
783 draw_bar(0, 97, 55, 6, best[i]->amount, 4, 13+i*20);
784 } else
785 draw_bar(0, 97, 55, 6, 0, 4, 13+i*20);
786 blit_string(s,4,4+i*20);
789 #if defined(DEBUG)
790 show_memory();
791 #endif
794 /******************************************/
795 /* Calculate cpu total */
796 /******************************************/
798 unsigned long calc_cpu_total() {
799 unsigned long total,t;
800 static unsigned long previous_total = ULONG_MAX;
801 #if defined(LINUX)
802 int rc;
803 int ps;
804 char line[WMTOP_BUFLENGTH];
805 unsigned long cpu,nice,system,idle;
807 ps = open("/proc/stat",O_RDONLY);
808 rc = read(ps,line,sizeof(line));
809 close(ps);
810 if (rc<0)
811 return 0;
812 sscanf(line,"%*s %lu %lu %lu %lu",&cpu,&nice,&system,&idle);
813 total = cpu+nice+system+idle;
814 #endif /* defined(LINUX) */
816 #if defined(FREEBSD)
817 struct timeval tv;
819 gettimeofday(&tv,0);
820 total = tv.tv_sec*1000+tv.tv_usec/1000;
821 #endif /* defined(FREEBSD) */
823 t = total-previous_total;
824 previous_total = total;
825 if (t<0)
826 t = 0;
828 return t;
831 /******************************************/
832 /* Calculate each processes cpu */
833 /******************************************/
835 void calc_cpu_each(unsigned long total) {
836 struct process *p = first_process;
837 while (p) {
839 #if defined(PARANOID)
840 assert(p->id==0x0badfeed);
841 #endif /* defined(PARANOID) */
843 p->amount = total ? 100*(float)(p->user_time+p->kernel_time)/total : 0;
844 p = p->next;
848 /******************************************/
849 /* Calculate total memory */
850 /******************************************/
852 #if defined(LINUX)
853 /* INT_MAX won't always hold total system RAM, especially on a 64 bit system. */
854 unsigned long calc_mem_total() {
855 int ps;
856 char line[1024];
857 char *ptr;
858 int rc;
860 ps = open("/proc/meminfo",O_RDONLY);
861 rc = read(ps,line,sizeof(line));
862 close(ps);
863 if (rc<0)
864 return 0;
866 if ((ptr = strstr(line, "Mem:")) != NULL) {
867 ptr += 4;
868 return atoi(ptr);
869 } else if ((ptr = strstr(line, "MemTotal:")) != NULL) {
870 /* The "Mem:" line has been removed in Linux 2.6 */
871 ptr += 9;
872 return atoi(ptr) << 10; /* MemTotal is given in kiB */
873 } else {
874 return 0;
877 #endif /* defined(LINUX) */
879 /******************************************/
880 /* Calculate each processes memory */
881 /******************************************/
883 #if defined(LINUX)
884 void calc_mem_each(unsigned long total) {
885 struct process *p = first_process;
886 while (p) {
887 p->amount = 100*(double)p->rss/total;
888 p = p->next;
891 #endif /* defined(LINUX) */
893 /******************************************/
894 /* Find the top three processes */
895 /******************************************/
898 * Result is stored in decreasing order in best[0-2].
900 int process_find_top_three(struct process **best) {
901 struct process *p = first_process;
902 int n = 0;
905 * Insertion sort approach to skim top 3
907 while (p) {
908 if (p->counted && p->amount>0 && (!best[0] || p->amount>best[0]->amount)) {
909 best[2] = best[1];
910 best[1] = best[0];
911 best[0] = p;
912 ++n;
913 } else if (p->counted && p->amount>0 && (!best[1] || p->amount>best[1]->amount)) {
914 best[2] = best[1];
915 best[1] = p;
916 ++n;
917 } else if (p->counted && p->amount>0 && (!best[2] || p->amount>best[2]->amount)) {
918 ++n;
919 best[2] = p;
922 p = p->next;
925 return n>3 ? 3 : n;
928 /******************************************/
929 /* Blit bar at co-ordinates */
930 /******************************************/
932 void draw_bar(int sx, int sy, int w, int h, float percent, int dx, int dy) {
933 int tx;
935 if (percent<=100)
936 tx = w * (float)percent / 100;
937 else
938 tx = w;
940 if (tx>0)
941 copyXPMArea(sx, sy, tx, h, dx, dy);
942 if (tx<w)
943 copyXPMArea(sx+tx, sy+h, w-tx, h, dx+tx, dy);
946 /******************************************/
947 /* Blit string at co-ordinates */
948 /******************************************/
950 void blit_string(char *name, int x, int y) {
951 int i;
952 int c;
953 int k;
955 k = x;
956 for ( i = 0; name[i]; i++) {
957 c = toupper(name[i]);
958 if (c >= 'A' && c <= 'J') {
959 c -= 'A';
960 copyXPMArea(c*6,73,6,7,k,y);
961 } else if (c>='K' && c<='T') {
962 c -= 'K';
963 copyXPMArea(c*6,81,6,7,k,y);
964 } else if (c>='U' && c<='Z') {
965 c -= 'U';
966 copyXPMArea(c*6,89,6,7,k,y);
967 } else if (c>='0' && c<='9') {
968 c -= '0';
969 copyXPMArea(c*6,65,6,7,k,y);
970 } else {
971 copyXPMArea(36,89,6,7,k,y);
973 k += 6;
977 /******************************************/
978 /* Usage */
979 /******************************************/
981 void usage(void) {
982 int i;
983 fprintf(stderr,"\nWMtop - Dan Piponi <dan@tanelorn.demon.co.uk> http://www.tanelorn.demon.co.uk\n\n");
984 fprintf(stderr,"usage:\n");
985 fprintf(stderr," -display <display name>\n");
986 fprintf(stderr," -geometry +XPOS+YPOS initial window position\n");
987 fprintf(stderr," -s <...> sample rate in milliseconds (default:%d)\n", update_rate/1000);
988 fprintf(stderr," -r <...> refresh rate in milliseconds (default:%d)\n", refresh_rate/1000);
989 fprintf(stderr," -U display user processes only\n");
990 fprintf(stderr," -x <...> exclude matching processes\n");
991 fprintf(stderr," -c <...> command\n");
992 #if defined(LINUX)
993 fprintf(stderr," -m display memory usage\n");
994 #endif /* defined(LINUX) */
995 fprintf(stderr," -v print version number\n");
996 fprintf(stderr," -a <1..%d> select artistic style\n", nstyles);
997 fprintf(stderr,"\n");
998 fprintf(stderr,"The artistic style is one of:\n");
999 for (i = 0; i<nstyles; ++i)
1000 fprintf(stderr," %d - %s\n",i+1,styles[i].description);
1003 /******************************************/
1004 /* Print version */
1005 /******************************************/
1007 void printversion(void) {
1008 fprintf(stderr, "wmtop v%s\n",PACKAGE_VERSION);