top - Import 3.8beta1 sources into new vendor/TOP branch.
[dragonfly.git] / contrib / top / top.c
1 /*
2  * Copyright (c) 1984 through 2008, William LeFebvre
3  * All rights reserved.
4  * 
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  * 
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * 
11  *     * Redistributions in binary form must reproduce the above
12  * copyright notice, this list of conditions and the following disclaimer
13  * in the documentation and/or other materials provided with the
14  * distribution.
15  * 
16  *     * Neither the name of William LeFebvre nor the names of other
17  * contributors may be used to endorse or promote products derived from
18  * this software without specific prior written permission.
19  * 
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 char *copyright =
34     "Copyright (c) 1984 through 2008, William LeFebvre";
35
36 /*
37  * Changes to other files that we can do at the same time:
38  * screen.c:init_termcap: get rid of the "interactive" argument and have it
39  *      pass back something meaningful (such as success/failure/error).
40  */
41
42 #include "os.h"
43 #include <signal.h>
44 #include <setjmp.h>
45 #include <ctype.h>
46 #include <sys/types.h>
47 #include <sys/uio.h>
48 #include <unistd.h>
49
50 #ifdef HAVE_SYS_UTSNAME_H
51 #include <sys/utsname.h>
52 #endif
53
54 #ifdef HAVE_GETOPT_H
55 #include <getopt.h>
56 #endif
57
58 /* definitions */
59 #ifndef STDIN_FILENO
60 #define STDIN_FILENO 0
61 #endif
62
63 /* determine which type of signal functions to use */
64 /* cant have sigaction without sigprocmask */
65 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGPROCMASK)
66 #undef HAVE_SIGACTION
67 #endif
68 /* always use sigaction when it is available */
69 #ifdef HAVE_SIGACTION
70 #undef HAVE_SIGHOLD
71 #else
72 /* use sighold/sigrelse, otherwise use old fashioned BSD signals */
73 #if !defined(HAVE_SIGHOLD) || !defined(HAVE_SIGRELSE)
74 #define BSD_SIGNALS
75 #endif
76 #endif
77
78 /* if FD_SET and friends aren't present, then fake something up */
79 #ifndef FD_SET
80 typedef int fd_set;
81 #define FD_ZERO(x)     (*(x) = 0)
82 #define FD_SET(f, x)   (*(x) = 1<<f)
83 #endif
84
85 /* includes specific to top */
86
87 #include "top.h"
88 #include "machine.h"
89 #include "globalstate.h"
90 #include "commands.h"
91 #include "display.h"
92 #include "screen.h"
93 #include "boolean.h"
94 #include "username.h"
95 #include "utils.h"
96 #include "version.h"
97 #ifdef ENABLE_COLOR
98 #include "color.h"
99 #endif
100
101 /* definitions */
102 #define BUFFERSIZE 4096
103 #define JMP_RESUME 1
104 #define JMP_RESIZE 2
105
106 /* externs for getopt: */
107 extern int  optind;
108 extern char *optarg;
109
110 /* statics */
111 static char stdoutbuf[BUFFERSIZE];
112 static jmp_buf jmp_int;
113
114 /* globals */
115 char *myname = "top";
116
117 void
118 quit(int status)
119
120 {
121     screen_end();
122     chdir("/tmp");
123     exit(status);
124     /* NOTREACHED */
125 }
126
127 /*
128  *  signal handlers
129  */
130
131 void
132 set_signal(int sig, RETSIGTYPE (*handler)(int))
133
134 {
135 #ifdef HAVE_SIGACTION
136     struct sigaction action;
137
138     action.sa_handler = handler;
139     action.sa_flags = 0;
140     (void) sigaction(sig, &action, NULL);
141 #else
142     (void) signal(sig, handler);
143 #endif
144 }
145
146 void
147 release_signal(int sig)
148
149 {
150 #ifdef HAVE_SIGACTION
151     sigset_t set;
152     sigemptyset(&set);
153     sigaddset(&set, sig);
154     sigprocmask(SIG_UNBLOCK, &set, NULL);
155 #endif
156
157 #ifdef HAVE_SIGHOLD
158     sigrelse(sig);
159 #endif
160
161 #ifdef BSD_SIGNALS
162     (void) sigsetmask(sigblock(0) & ~(sigmask(sig)));
163 #endif
164 }
165
166 RETSIGTYPE
167 sig_leave(int i)        /* exit under normal conditions -- INT handler */
168
169 {
170     screen_end();
171     exit(EX_OK);
172 }
173
174 RETSIGTYPE
175 sig_tstop(int i)        /* SIGTSTP handler */
176
177 {
178     /* move to the lower left */
179     screen_end();
180     fflush(stdout);
181
182     /* default the signal handler action */
183     set_signal(SIGTSTP, SIG_DFL);
184
185     /* unblock the TSTP signal */
186     release_signal(SIGTSTP);
187
188     /* send ourselves a TSTP to stop the process */
189     (void) kill(0, SIGTSTP);
190
191     /* reset the signal handler */
192     set_signal(SIGTSTP, sig_tstop);
193
194     /* reinit screen */
195     screen_reinit();
196
197     /* jump back to a known place in the main loop */
198     longjmp(jmp_int, JMP_RESUME);
199
200     /* NOTREACHED */
201 }
202
203 #ifdef SIGWINCH
204 RETSIGTYPE
205 sig_winch(int i)                /* SIGWINCH handler */
206
207 {
208     /* reascertain the screen dimensions */
209     screen_getsize();
210
211     /* jump back to a known place in the main loop */
212     longjmp(jmp_int, JMP_RESIZE);
213 }
214 #endif
215
216 #ifdef HAVE_SIGACTION
217 static sigset_t signalset;
218 #endif
219
220 void *
221 hold_signals()
222
223 {
224 #ifdef HAVE_SIGACTION
225     sigemptyset(&signalset);
226     sigaddset(&signalset, SIGINT);
227     sigaddset(&signalset, SIGQUIT);
228     sigaddset(&signalset, SIGTSTP);
229 #ifdef SIGWINCH
230     sigaddset(&signalset, SIGWINCH);
231 #endif
232     sigprocmask(SIG_BLOCK, &signalset, NULL);
233     return (void *)(&signalset);
234 #endif
235
236 #ifdef HAVE_SIGHOLD
237     sighold(SIGINT);
238     sighold(SIGQUIT);
239     sighold(SIGTSTP);
240 #ifdef SIGWINCH
241     sighold(SIGWINCH);
242     return NULL;
243 #endif
244 #endif
245
246 #ifdef BSD_SIGNALS
247     int mask;
248 #ifdef SIGWINCH
249     mask = sigblock(sigmask(SIGINT) | sigmask(SIGQUIT) |
250                     sigmask(SIGTSTP) | sigmask(SIGWINCH));
251 #else
252     mask = sigblock(sigmask(SIGINT) | sigmask(SIGQUIT) | sigmask(SIGTSTP));
253     return (void *)mask;
254 #endif
255 #endif
256
257 }
258
259 void
260 set_signals()
261
262 {
263     (void) set_signal(SIGINT, sig_leave);
264     (void) set_signal(SIGQUIT, sig_leave);
265     (void) set_signal(SIGTSTP, sig_tstop);
266 #ifdef SIGWINCH
267     (void) set_signal(SIGWINCH, sig_winch);
268 #endif
269 }
270
271 void
272 release_signals(void *parm)
273
274 {
275 #ifdef HAVE_SIGACTION
276     sigprocmask(SIG_UNBLOCK, (sigset_t *)parm, NULL);
277 #endif
278
279 #ifdef HAVE_SIGHOLD
280     sigrelse(SIGINT);
281     sigrelse(SIGQUIT);
282     sigrelse(SIGTSTP);
283 #ifdef SIGWINCH
284     sigrelse(SIGWINCH);
285 #endif
286 #endif
287
288 #ifdef BSD_SIGNALS
289     (void) sigsetmask((int)parm);
290 #endif
291 }
292
293 /*
294  * void do_arguments(globalstate *gstate, int ac, char **av)
295  *
296  * Arguments processing.  gstate points to the global state,
297  * ac and av are the arguments to process.  This can be called
298  * multiple times with different sets of arguments.
299  */
300
301 #ifdef HAVE_GETOPT_LONG
302 static struct option longopts[] = {
303     { "color", no_argument, NULL, 'C' },
304     { "debug", no_argument, NULL, 'D' },
305     { "system-procs", no_argument, NULL, 'S' },
306     { "idle-procs", no_argument, NULL, 'I' },
307     { "tag-names", no_argument, NULL, 'T' },
308     { "all", no_argument, NULL, 'a' },
309     { "batch", no_argument, NULL, 'b' },
310     { "full-commands", no_argument, NULL, 'c' },
311     { "interactive", no_argument, NULL, 'i' },
312     { "quick", no_argument, NULL, 'q' },
313     { "threads", no_argument, NULL, 't' },
314     { "uids", no_argument, NULL, 'u' },
315     { "version", no_argument, NULL, 'v' },
316     { "delay", required_argument, NULL, 's' },
317     { "displays", required_argument, NULL, 'd' },
318     { "user", required_argument, NULL, 'U' },
319     { "sort-order", required_argument, NULL, 'o' },
320     { "display-mode", required_argument, NULL, 'm' },
321     { NULL, 0, NULL, 0 },
322 };
323 #endif
324
325
326 void
327 do_arguments(globalstate *gstate, int ac, char **av)
328
329 {
330     int i;
331
332     /* this appears to keep getopt happy */
333     optind = 1;
334
335 #ifdef HAVE_GETOPT_LONG
336     while ((i = getopt_long(ac, av, "CDSITabcinqtuvs:d:U:o:m:", longopts, NULL)) != -1)
337 #else
338     while ((i = getopt(ac, av, "CDSITabcinqtuvs:d:U:o:m:")) != EOF)
339 #endif
340     {
341         switch(i)
342         {
343 #ifdef ENABLE_COLOR
344         case 'C':
345             gstate->use_color = !gstate->use_color;
346             break;
347 #endif
348
349         case 'D':
350             debug_set(1);
351             break;
352
353         case 'v':
354             fprintf(stderr, "%s: version %s\n", myname, version_string());
355             exit(EX_OK);
356             break;
357
358         case 'b':
359         case 'n':
360             gstate->interactive = No;
361             break;
362
363         case 'a':
364             gstate->displays = Infinity;
365             gstate->topn = Infinity;
366             break;
367
368         case 'i':
369             gstate->interactive = Yes;
370             break;
371
372         case 'o':
373             gstate->order_name = optarg;
374             break;
375
376         case 'd':
377             i = atoiwi(optarg);
378             if (i == Invalid || i == 0)
379             {
380                 message_error(" Bad display count");
381             }
382             else
383             {
384                 gstate->displays = i;
385             }
386             break;
387
388         case 's':
389             i = atoi(optarg);
390             if (i < 0 || (i == 0 && getuid() != 0))
391             {
392                 message_error(" Bad seconds delay");
393             }
394             else
395             {
396                 gstate->delay = i;
397             }
398             break;
399
400         case 'u':
401             gstate->show_usernames = !gstate->show_usernames;
402             break;
403
404         case 'U':
405             i = userid(optarg);
406             if (i == -1)
407             {
408                 message_error(" Unknown user '%s'", optarg);
409             }
410             else
411             {
412                 gstate->pselect.uid = i;
413             }
414             break;
415
416         case 'm':
417             i = atoi(optarg);
418             gstate->pselect.mode = i;
419             break;
420
421         case 'S':
422             gstate->pselect.system = !gstate->pselect.system;
423             break;
424
425         case 'I':
426             gstate->pselect.idle = !gstate->pselect.idle;
427             break;
428
429 #ifdef ENABLE_COLOR
430         case 'T':
431             gstate->show_tags = 1;
432             break;
433 #endif
434
435         case 'c':
436             gstate->pselect.fullcmd = !gstate->pselect.fullcmd;
437             break;
438
439         case 't':
440             gstate->pselect.threads = !gstate->pselect.threads;
441             break;
442
443         case 'q':               /* be quick about it */
444             /* only allow this if user is really root */
445             if (getuid() == 0)
446             {
447                 /* be very un-nice! */
448                 (void) nice(-20);
449             }
450             else
451             {
452                 message_error(" Option -q can only be used by root");
453             }
454             break;
455
456         default:
457             fprintf(stderr, "\
458 Top version %s\n\
459 Usage: %s [-ISTabcinqu] [-d x] [-s x] [-o field] [-U username] [number]\n",
460                     version_string(), myname);
461             exit(EX_USAGE);
462         }
463     }
464
465     /* get count of top processes to display */
466     if (optind < ac && *av[optind])
467     {
468         if ((i = atoiwi(av[optind])) == Invalid)
469         {
470             message_error(" Process count not a number");
471         }
472         else
473         {
474             gstate->topn = i;
475         }
476     }
477 }
478
479 void
480 do_display(globalstate *gstate)
481
482 {
483     int active_procs;
484     int i;
485     time_t curr_time;
486     caddr_t processes;
487     struct system_info system_info;
488     char *hdr;
489
490     /* get the time */
491     time_mark(&(gstate->now));
492     curr_time = (time_t)(gstate->now.tv_sec);
493
494     /* get the current stats */
495     get_system_info(&system_info);
496
497     /* get the current processes */
498     processes = get_process_info(&system_info, &(gstate->pselect), gstate->order_index);
499
500     /* determine number of processes to actually display */
501     if (gstate->topn > 0)
502     {
503         /* this number will be the smallest of:  active processes,
504            number user requested, number current screen accomodates */
505         active_procs = system_info.P_ACTIVE;
506         if (active_procs > gstate->topn)
507         {
508             active_procs = gstate->topn;
509         }
510         if (active_procs > gstate->max_topn)
511         {
512             active_procs = gstate->max_topn;
513         }
514     }
515     else
516     {
517         /* dont show any */
518         active_procs = 0;
519     }
520
521 #ifdef HAVE_FORMAT_PROCESS_HEADER
522     /* get the process header to use */
523     hdr = format_process_header(&(gstate->pselect), processes, active_procs);
524 #else
525     hdr = gstate->header_text;
526 #endif
527
528     /* full screen or update? */
529     if (gstate->fulldraw)
530     {
531         display_clear();
532         i_loadave(system_info.last_pid, system_info.load_avg);
533         i_uptime(&(gstate->statics->boottime), &curr_time);
534         i_timeofday(&curr_time);
535         i_procstates(system_info.p_total, system_info.procstates, gstate->pselect.threads);
536         if (gstate->show_cpustates)
537         {
538             i_cpustates(system_info.cpustates);
539         }
540         else
541         {
542             if (smart_terminal)
543             {
544                 z_cpustates();
545             }
546             gstate->show_cpustates = Yes;
547         }
548         i_kernel(system_info.kernel);
549         i_memory(system_info.memory);
550         i_swap(system_info.swap);
551         i_message(&(gstate->now));
552         i_header(hdr);
553         for (i = 0; i < active_procs; i++)
554         {
555             i_process(i, format_next_process(processes, gstate->get_userid));
556         }
557         i_endscreen();
558         if (gstate->smart_terminal)
559         {
560             gstate->fulldraw = No;
561         }
562     }
563     else
564     {
565         u_loadave(system_info.last_pid, system_info.load_avg);
566         u_uptime(&(gstate->statics->boottime), &curr_time);
567         i_timeofday(&curr_time);
568         u_procstates(system_info.p_total, system_info.procstates, gstate->pselect.threads);
569         u_cpustates(system_info.cpustates);
570         u_kernel(system_info.kernel);
571         u_memory(system_info.memory);
572         u_swap(system_info.swap);
573         u_message(&(gstate->now));
574         u_header(hdr);
575         for (i = 0; i < active_procs; i++)
576         {
577             u_process(i, format_next_process(processes, gstate->get_userid));
578         }
579         u_endscreen();
580     }
581 }
582
583 #ifdef DEBUG
584 void
585 timeval_xdprint(char *s, struct timeval tv)
586
587 {
588     xdprintf("%s %d.%06d\n", s, tv.tv_sec, tv.tv_usec);
589 }
590 #endif
591
592 void
593 do_wait(globalstate *gstate)
594
595 {
596     struct timeval wait;
597
598     wait.tv_sec = gstate->delay;
599     wait.tv_usec = 0;
600     select(0, NULL, NULL, NULL, &wait);
601 }
602
603 void
604 do_command(globalstate *gstate)
605
606 {
607     int status;
608     struct timeval wait = {0, 0};
609     struct timeval now;
610     fd_set readfds;
611     unsigned char ch;
612
613     /* calculate new refresh time */
614     gstate->refresh = gstate->now;
615     gstate->refresh.tv_sec += gstate->delay;
616     time_get(&now);
617
618     /* loop waiting for time to expire */
619     do {
620         /* calculate time to wait */
621         if (gstate->delay > 0)
622         {
623             wait = gstate->refresh;
624             wait.tv_usec -= now.tv_usec;
625             if (wait.tv_usec < 0)
626             {
627                 wait.tv_usec += 1000000;
628                 wait.tv_sec--;
629             }
630             wait.tv_sec -= now.tv_sec;
631         }
632
633         /* set up arguments for select on stdin (0) */
634         FD_ZERO(&readfds);
635         FD_SET(STDIN_FILENO, &readfds);
636
637         /* wait for something to read or time out */
638         if (select(32, &readfds, NULL, NULL, &wait) > 0)
639         {
640             /* read it */
641             if (read(STDIN_FILENO, &ch, 1) != 1)
642             {
643                 /* read error */
644                 message_error(" Read error on stdin");
645                 quit(EX_DATAERR);
646                 /*NOTREACHED*/
647             }
648
649             /* mark pending messages as old */
650             message_mark();
651
652             /* dispatch */
653             status = command_process(gstate, (int)ch);
654             switch(status)
655             {
656             case CMD_ERROR:
657                 quit(EX_SOFTWARE);
658                 /*NOTREACHED*/
659                 
660             case CMD_REFRESH:
661                 return;
662
663             case CMD_UNKNOWN:
664                 message_error(" Unknown command");
665                 break;
666
667             case CMD_NA:
668                 message_error(" Command not available");
669             }
670         }
671
672         /* get new time */
673         time_get(&now);
674     } while (timercmp(&now, &(gstate->refresh), < ));
675 }
676
677 void
678 do_minidisplay(globalstate *gstate)
679
680 {
681     int real_delay;
682     struct system_info si;
683
684     /* save the real delay and substitute 1 second */
685     real_delay = gstate->delay;
686     gstate->delay = 1;
687
688     /* wait 1 second for a command */
689     time_mark(&(gstate->now));
690     do_command(gstate);
691
692     /* do a mini update that only updates the cpustates */
693     get_system_info(&si);
694     u_cpustates(si.cpustates);
695
696     /* restore the delay time */
697     gstate->delay = real_delay;
698
699     /* done */
700     i_endscreen();
701 }
702
703 int
704 main(int argc, char *argv[])
705
706 {
707     char *env_top;
708     char **preset_argv;
709     int preset_argc = 0;
710     void *mask;
711     int need_mini = 1;
712
713     struct statics statics;
714     globalstate *gstate;
715
716     /* get our name */
717     if (argc > 0)
718     {
719         if ((myname = strrchr(argv[0], '/')) == 0)
720         {
721             myname = argv[0];
722         }
723         else
724         {
725             myname++;
726         }
727     }
728
729     /* binary compatibility check */
730 #ifdef HAVE_UNAME
731     {
732         struct utsname uts;
733
734         if (uname(&uts) == 0)
735         {
736             if (strcmp(uts.machine, UNAME_HARDWARE) != 0)
737             {
738                 fprintf(stderr, "%s: incompatible hardware platform\n",
739                         myname);
740                 exit(EX_UNAVAILABLE);
741             }
742         }
743     }
744 #endif
745
746     /* initialization */
747     gstate = (globalstate *)calloc(1, sizeof(globalstate));
748     gstate->statics = &statics;
749     time_mark(NULL);
750
751     /* preset defaults for various options */
752     gstate->show_usernames = Yes;
753     gstate->topn = DEFAULT_TOPN;
754     gstate->delay = DEFAULT_DELAY;
755     gstate->fulldraw = Yes;
756     gstate->use_color = Yes;
757     gstate->interactive = Maybe;
758
759     /* preset defaults for process selection */
760     gstate->pselect.idle = Yes;
761     gstate->pselect.system = No;
762     gstate->pselect.fullcmd = No;
763     gstate->pselect.command = NULL;
764     gstate->pselect.uid = -1;
765     gstate->pselect.mode = 0;
766
767     /* use a large buffer for stdout */
768 #ifdef HAVE_SETVBUF
769     setvbuf(stdout, stdoutbuf, _IOFBF, BUFFERSIZE);
770 #else
771 #ifdef HAVE_SETBUFFER
772     setbuffer(stdout, stdoutbuf, BUFFERSIZE);
773 #endif
774 #endif
775
776     /* get preset options from the environment */
777     if ((env_top = getenv("TOP")) != NULL)
778     {
779         preset_argv = argparse(env_top, &preset_argc);
780         preset_argv[0] = myname;
781         do_arguments(gstate, preset_argc, preset_argv);
782     }
783
784     /* process arguments */
785     do_arguments(gstate, argc, argv);
786
787 #ifdef ENABLE_COLOR
788     /* If colour has been turned on read in the settings. */
789     env_top = getenv("TOPCOLOURS");
790     if (!env_top)
791     {
792         env_top = getenv("TOPCOLORS");
793     }
794     /* must do something about error messages */
795     color_env_parse(env_top);
796     color_activate(gstate->use_color);
797 #endif
798
799     /* in order to support forward compatability, we have to ensure that
800        the entire statics structure is set to a known value before we call
801        machine_init.  This way fields that a module does not know about
802        will retain their default values */
803     memzero((void *)&statics, sizeof(statics));
804     statics.boottime = -1;
805
806     /* call the platform-specific init */
807     if (machine_init(&statics) == -1)
808     {
809         exit(EX_SOFTWARE);
810     }
811
812     /* create a helper list of sort order names */
813     gstate->order_namelist = string_list(statics.order_names);
814
815     /* look up chosen sorting order */
816     if (gstate->order_name != NULL)
817     {
818         int i;
819
820         if (statics.order_names == NULL)
821         {
822             message_error(" This platform does not support arbitrary ordering");
823         }
824         else if ((i = string_index(gstate->order_name,
825                                    statics.order_names)) == -1)
826         {
827             message_error(" Sort order `%s' not recognized", gstate->order_name);
828             message_error(" Recognized sort orders: %s", gstate->order_namelist);
829         }
830         else
831         {
832             gstate->order_index = i;
833         }
834     }
835
836     /* initialize extensions */
837     init_username();
838
839     /* initialize termcap */
840     gstate->smart_terminal = screen_readtermcap(gstate->interactive);
841
842     /* determine interactive state */
843     if (gstate->interactive == Maybe)
844     {
845         gstate->interactive = smart_terminal;
846     }
847
848     /* if displays were not specified, choose an appropriate default */
849     if (gstate->displays == 0)
850     {
851         gstate->displays = gstate->smart_terminal ? Infinity: 1;
852     }
853
854     /* we don't need a mini display when delay is less than 2
855        seconds or when we are not on a smart terminal */
856     if (gstate->delay <= 1 || !smart_terminal)
857     {
858         need_mini = 0;
859     }
860
861 #ifndef HAVE_FORMAT_PROCESS_HEADER
862     /* set constants for username/uid display */
863     if (gstate->show_usernames)
864     {
865         gstate->header_text = format_header("USERNAME");
866         gstate->get_userid = username;
867     }
868     else
869     {
870         gstate->header_text = format_header("   UID  ");
871         gstate->get_userid = itoa7;
872     }
873 #endif
874     gstate->pselect.usernames = gstate->show_usernames;
875
876     /* initialize display */
877     if ((gstate->max_topn = display_init(&statics)) == -1)
878     {
879         fprintf(stderr, "%s: can't allocate sufficient memory\n", myname);
880         exit(EX_OSERR);
881     }
882
883     /* check for infinity and for overflowed screen */
884     if (gstate->topn == Infinity)
885     {
886         gstate->topn = INT_MAX;
887     }
888     else if (gstate->topn > gstate->max_topn)
889     {
890         message_error(" This terminal can only display %d processes",
891                       gstate->max_topn);
892     }
893
894 #ifdef ENABLE_COLOR
895     /* producing a list of color tags is easy */
896     if (gstate->show_tags)
897     {
898         color_dump(stdout);
899         exit(EX_OK);
900     }
901 #endif
902
903     /* hold all signals while we initialize the screen */
904     mask = hold_signals();
905     screen_init();
906
907     /* set the signal handlers */
908     set_signals();
909
910     /* longjmp re-entry point */
911     /* set the jump buffer for long jumps out of signal handlers */
912     if (setjmp(jmp_int) != 0)
913     {
914         /* this is where we end up after processing sigwinch or sigtstp */
915
916         /* tell display to resize its buffers, and get the new length */
917         if ((gstate->max_topn = display_resize()) == -1)
918         {
919             /* thats bad */
920             quit(EX_OSERR);
921             /*NOTREACHED*/
922         }
923
924         /* set up for a full redraw, and get the current line count */
925         gstate->fulldraw = Yes;
926
927         /* safe to release the signals now */
928         release_signals(mask);
929     }
930     else
931     {
932         /* release the signals */
933         release_signals(mask);
934
935         /* some systems require a warmup */
936         /* always do a warmup for batch mode */
937         if (gstate->interactive == 0 || statics.flags.warmup)
938         {
939             struct system_info system_info;
940             struct timeval timeout;
941
942             time_mark(&(gstate->now));
943             get_system_info(&system_info);
944             (void)get_process_info(&system_info, &gstate->pselect, 0);
945             timeout.tv_sec = 1;
946             timeout.tv_usec = 0;
947             select(0, NULL, NULL, NULL, &timeout);
948
949             /* if we've warmed up, then we can show good states too */
950             gstate->show_cpustates = Yes;
951             need_mini = 0;
952         }
953     }
954
955     /* main loop */
956     while ((gstate->displays == -1) || (--gstate->displays > 0))
957     {
958         do_display(gstate);
959         if (gstate->interactive)
960         {
961             if (need_mini)
962             {
963                 do_minidisplay(gstate);
964                 need_mini = 0;
965             }
966             do_command(gstate);
967         }
968         else
969         {
970             do_wait(gstate);
971         }
972     }
973
974     /* do one last display */
975     do_display(gstate);
976
977     quit(EX_OK);
978     /* NOTREACHED */
979     return 1; /* Keep compiler quiet. */
980 }