text: /*
   * Copyright (c) 1984 through 2008, William LeFebvre
   * All rights reserved.
   * 
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions are met:
   * 
   *     * Redistributions of source code must retain the above copyright
   * notice, this list of conditions and the following disclaimer.
   * 
   *     * Redistributions in binary form must reproduce the above
   * copyright notice, this list of conditions and the following disclaimer
   * in the documentation and/or other materials provided with the
   * distribution.
   * 
   *     * Neither the name of William LeFebvre nor the names of other
   * contributors may be used to endorse or promote products derived from
   * this software without specific prior written permission.
   * 
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
   * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
   * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   */
  
  char *copyright =
      "Copyright (c) 1984 through 2008, William LeFebvre";
  
  /*
   * Changes to other files that we can do at the same time:
   * screen.c:init_termcap: get rid of the "interactive" argument and have it
   *      pass back something meaningful (such as success/failure/error).
   */
  
  #include "os.h"
  #include <signal.h>
  #include <setjmp.h>
  #include <ctype.h>
  #include <sys/types.h>
  #include <sys/uio.h>
  #include <unistd.h>
  
  #ifdef HAVE_SYS_UTSNAME_H
  #include <sys/utsname.h>
  #endif
  
  #ifdef HAVE_GETOPT_H
  #include <getopt.h>
  #endif
  
  /* definitions */
  #ifndef STDIN_FILENO
  #define STDIN_FILENO 0
  #endif
  
  /* determine which type of signal functions to use */
  /* cant have sigaction without sigprocmask */
  #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGPROCMASK)
  #undef HAVE_SIGACTION
  #endif
  /* always use sigaction when it is available */
  #ifdef HAVE_SIGACTION
  #undef HAVE_SIGHOLD
  #else
  /* use sighold/sigrelse, otherwise use old fashioned BSD signals */
  #if !defined(HAVE_SIGHOLD) || !defined(HAVE_SIGRELSE)
  #define BSD_SIGNALS
  #endif
  #endif
  
  /* if FD_SET and friends aren't present, then fake something up */
  #ifndef FD_SET
  typedef int fd_set;
  #define FD_ZERO(x)     (*(x) = 0)
  #define FD_SET(f, x)   (*(x) = 1<<f)
  #endif
  
  /* includes specific to top */
  
  #include "top.h"
  #include "machine.h"
  #include "globalstate.h"
  #include "commands.h"
  #include "display.h"
  #include "screen.h"
  #include "boolean.h"
  #include "username.h"
  #include "utils.h"
  #include "version.h"
  #ifdef ENABLE_COLOR
  #include "color.h"
  #endif
  
  /* definitions */
  #define BUFFERSIZE 4096
  #define JMP_RESUME 1
  #define JMP_RESIZE 2
  
  /* externs for getopt: */
  extern int  optind;
  extern char *optarg;
  
  /* statics */
  static char stdoutbuf[BUFFERSIZE];
  static jmp_buf jmp_int;
  
  /* globals */
  char *myname = "top";
  
  void
  quit(int status)
  
  {
      screen_end();
      chdir("/tmp");
      exit(status);
      /* NOTREACHED */
  }
  
  /*
   *  signal handlers
   */
  
  void
  set_signal(int sig, RETSIGTYPE (*handler)(int))
  
  {
  #ifdef HAVE_SIGACTION
      struct sigaction action;
  
      action.sa_handler = handler;
      action.sa_flags = 0;
      (void) sigaction(sig, &action, NULL);
  #else
      (void) signal(sig, handler);
  #endif
  }
  
  void
  release_signal(int sig)
  
  {
  #ifdef HAVE_SIGACTION
      sigset_t set;
      sigemptyset(&set);
      sigaddset(&set, sig);
      sigprocmask(SIG_UNBLOCK, &set, NULL);
  #endif
  
  #ifdef HAVE_SIGHOLD
      sigrelse(sig);
  #endif
  
  #ifdef BSD_SIGNALS
      (void) sigsetmask(sigblock(0) & ~(sigmask(sig)));
  #endif
  }
  
  RETSIGTYPE
  sig_leave(int i)	/* exit under normal conditions -- INT handler */
  
  {
      screen_end();
      exit(EX_OK);
  }
  
  RETSIGTYPE
  sig_tstop(int i)	/* SIGTSTP handler */
  
  {
      /* move to the lower left */
      screen_end();
      fflush(stdout);
  
      /* default the signal handler action */
      set_signal(SIGTSTP, SIG_DFL);
  
      /* unblock the TSTP signal */
      release_signal(SIGTSTP);
  
      /* send ourselves a TSTP to stop the process */
      (void) kill(0, SIGTSTP);
  
      /* reset the signal handler */
      set_signal(SIGTSTP, sig_tstop);
  
      /* reinit screen */
      screen_reinit();
  
      /* jump back to a known place in the main loop */
      longjmp(jmp_int, JMP_RESUME);
  
      /* NOTREACHED */
  }
  
  #ifdef SIGWINCH
  RETSIGTYPE
  sig_winch(int i)		/* SIGWINCH handler */
  
  {
      /* reascertain the screen dimensions */
      screen_getsize();
  
      /* jump back to a known place in the main loop */
      longjmp(jmp_int, JMP_RESIZE);
  }
  #endif
  
  #ifdef HAVE_SIGACTION
  static sigset_t signalset;
  #endif
  
  void *
  hold_signals()
  
  {
  #ifdef HAVE_SIGACTION
      sigemptyset(&signalset);
      sigaddset(&signalset, SIGINT);
      sigaddset(&signalset, SIGQUIT);
      sigaddset(&signalset, SIGTSTP);
  #ifdef SIGWINCH
      sigaddset(&signalset, SIGWINCH);
  #endif
      sigprocmask(SIG_BLOCK, &signalset, NULL);
      return (void *)(&signalset);
  #endif
  
  #ifdef HAVE_SIGHOLD
      sighold(SIGINT);
      sighold(SIGQUIT);
      sighold(SIGTSTP);
  #ifdef SIGWINCH
      sighold(SIGWINCH);
      return NULL;
  #endif
  #endif
  
  #ifdef BSD_SIGNALS
      int mask;
  #ifdef SIGWINCH
      mask = sigblock(sigmask(SIGINT) | sigmask(SIGQUIT) |
  		    sigmask(SIGTSTP) | sigmask(SIGWINCH));
  #else
      mask = sigblock(sigmask(SIGINT) | sigmask(SIGQUIT) | sigmask(SIGTSTP));
      return (void *)mask;
  #endif
  #endif
  
  }
  
  void
  set_signals()
  
  {
      (void) set_signal(SIGINT, sig_leave);
      (void) set_signal(SIGQUIT, sig_leave);
      (void) set_signal(SIGTSTP, sig_tstop);
  #ifdef SIGWINCH
      (void) set_signal(SIGWINCH, sig_winch);
  #endif
  }
  
  void
  release_signals(void *parm)
  
  {
  #ifdef HAVE_SIGACTION
      sigprocmask(SIG_UNBLOCK, (sigset_t *)parm, NULL);
  #endif
  
  #ifdef HAVE_SIGHOLD
      sigrelse(SIGINT);
      sigrelse(SIGQUIT);
      sigrelse(SIGTSTP);
  #ifdef SIGWINCH
      sigrelse(SIGWINCH);
  #endif
  #endif
  
  #ifdef BSD_SIGNALS
      (void) sigsetmask((int)parm);
  #endif
  }
  
  /*
   * void do_arguments(globalstate *gstate, int ac, char **av)
   *
   * Arguments processing.  gstate points to the global state,
   * ac and av are the arguments to process.  This can be called
   * multiple times with different sets of arguments.
   */
  
  #ifdef HAVE_GETOPT_LONG
  static struct option longopts[] = {
      { "color", no_argument, NULL, 'C' },
      { "debug", no_argument, NULL, 'D' },
      { "system-procs", no_argument, NULL, 'S' },
      { "idle-procs", no_argument, NULL, 'I' },
      { "tag-names", no_argument, NULL, 'T' },
      { "all", no_argument, NULL, 'a' },
      { "batch", no_argument, NULL, 'b' },
      { "full-commands", no_argument, NULL, 'c' },
      { "interactive", no_argument, NULL, 'i' },
      { "quick", no_argument, NULL, 'q' },
      { "threads", no_argument, NULL, 't' },
      { "uids", no_argument, NULL, 'u' },
      { "version", no_argument, NULL, 'v' },
      { "delay", required_argument, NULL, 's' },
      { "displays", required_argument, NULL, 'd' },
      { "user", required_argument, NULL, 'U' },
      { "sort-order", required_argument, NULL, 'o' },
      { "display-mode", required_argument, NULL, 'm' },
      { NULL, 0, NULL, 0 },
  };
  #endif
  
  
  void
  do_arguments(globalstate *gstate, int ac, char **av)
  
  {
      int i;
  
      /* this appears to keep getopt happy */
      optind = 1;
  
  #ifdef HAVE_GETOPT_LONG
      while ((i = getopt_long(ac, av, "CDSIMTabcinqtuvs:d:U:o:m:", longopts, NULL)) != -1)
  #else
      while ((i = getopt(ac, av, "CDSIMTabcinqtuvs:d:U:o:m:")) != EOF)
  #endif
      {
  	switch(i)
  	{
  #ifdef ENABLE_COLOR
  	case 'C':
  	    gstate->use_color = !gstate->use_color;
  	    break;
  #endif
  
  	case 'D':
  	    debug_set(1);
  	    break;
  
  	case 'v':
  	    fprintf(stderr, "%s: version %s\n", myname, version_string());
  	    exit(EX_OK);
  	    break;
  
  	case 'b':
  	case 'n':
  	    gstate->interactive = No;
  	    break;
  
  	case 'a':
  	    gstate->displays = Infinity;
  	    gstate->topn = Infinity;
  	    break;
  
  	case 'i':
  	    gstate->interactive = Yes;
  	    break;
  
  	case 'o':
  	    gstate->order_name = optarg;
  	    break;
  
  	case 'd':
  	    i = atoiwi(optarg);
  	    if (i == Invalid || i == 0)
  	    {
  		message_error(" Bad display count");
  	    }
  	    else
  	    {
  		gstate->displays = i;
  	    }
  	    break;
  
  	case 's':
  	    i = atoi(optarg);
  	    if (i < 0 || (i == 0 && getuid() != 0))
  	    {
  		message_error(" Bad seconds delay");
  	    }
  	    else
  	    {
  		gstate->delay = i;
  	    }
  	    break;
  
  	case 'u':
  	    gstate->show_usernames = !gstate->show_usernames;
  	    break;
  
  	case 'U':
  	    i = userid(optarg);
  	    if (i == -1)
  	    {
  		message_error(" Unknown user '%s'", optarg);
  	    }
  	    else
  	    {
  		gstate->pselect.uid = i;
  	    }
  	    break;
  
  	case 'm':
  	    i = atoi(optarg);
  	    gstate->pselect.mode = i;
  	    break;
  
  	case 'S':
  	    gstate->pselect.system = !gstate->pselect.system;
  	    break;
  
  	case 'I':
  	    gstate->pselect.idle = !gstate->pselect.idle;
  	    break;
  
          case 'M':
  	    enable_ncpus = 1;
              break;
  
  
  #ifdef ENABLE_COLOR
  	case 'T':
  	    gstate->show_tags = 1;
  	    break;
  #endif
  
  	case 'c':
  	    gstate->pselect.fullcmd = !gstate->pselect.fullcmd;
  	    break;
  
  	case 't':
  	    gstate->pselect.threads = !gstate->pselect.threads;
  	    break;
  
  	case 'q':		/* be quick about it */
  	    /* only allow this if user is really root */
  	    if (getuid() == 0)
  	    {
  		/* be very un-nice! */
  		(void) nice(-20);
  	    }
  	    else
  	    {
  		message_error(" Option -q can only be used by root");
  	    }
  	    break;
  
  	default:
  	    fprintf(stderr, "\
  Top version %s\n\
  Usage: %s [-ISTabcinqu] [-d x] [-s x] [-o field] [-U username] [number]\n",
  		    version_string(), myname);
  	    exit(EX_USAGE);
  	}
      }
  
      /* get count of top processes to display */
      if (optind < ac && *av[optind])
      {
  	if ((i = atoiwi(av[optind])) == Invalid)
  	{
  	    message_error(" Process count not a number");
  	}
  	else
  	{
  	    gstate->topn = i;
  	}
      }
  }
  
  void
  do_display(globalstate *gstate)
  
  {
      int active_procs;
      int i;
      time_t curr_time;
      caddr_t processes;
      struct system_info system_info;
      char *hdr;
  
      /* get the time */
      time_mark(&(gstate->now));
      curr_time = (time_t)(gstate->now.tv_sec);
  
      /* get the current stats */
      get_system_info(&system_info);
  
      /* get the current processes */
      processes = get_process_info(&system_info, &(gstate->pselect), gstate->order_index);
  
      /* determine number of processes to actually display */
      if (gstate->topn > 0)
      {
  	/* this number will be the smallest of:  active processes,
  	   number user requested, number current screen accomodates */
  	active_procs = system_info.P_ACTIVE;
  	if (active_procs > gstate->topn)
  	{
  	    active_procs = gstate->topn;
  	}
  	if (active_procs > gstate->max_topn)
  	{
  	    active_procs = gstate->max_topn;
  	}
      }
      else
      {
  	/* dont show any */
  	active_procs = 0;
      }
  
  #ifdef HAVE_FORMAT_PROCESS_HEADER
      /* get the process header to use */
      hdr = format_process_header(&(gstate->pselect), processes, active_procs);
  #else
      hdr = gstate->header_text;
  #endif
  
      /* full screen or update? */
      if (gstate->fulldraw)
      {
  	display_clear();
  	i_loadave(system_info.last_pid, system_info.load_avg);
  	i_uptime(&(gstate->statics->boottime), &curr_time);
  	i_timeofday(&curr_time);
  	i_procstates(system_info.p_total, system_info.procstates, gstate->pselect.threads);
  	if (gstate->show_cpustates)
  	{
  	    i_cpustates(system_info.cpustates);
  	}
  	else
  	{
  	    if (smart_terminal)
  	    {
  		z_cpustates();
  	    }
  	    gstate->show_cpustates = Yes;
  	}
  	i_kernel(system_info.kernel);
  	i_memory(system_info.memory);
  	i_swap(system_info.swap);
  	i_message(&(gstate->now));
  	i_header(hdr);
  	for (i = 0; i < active_procs; i++)
  	{
  	    i_process(i, format_next_process(processes, gstate->get_userid));
  	}
  	i_endscreen();
  	if (gstate->smart_terminal)
  	{
  	    gstate->fulldraw = No;
  	}
      }
      else
      {
  	u_loadave(system_info.last_pid, system_info.load_avg);
  	u_uptime(&(gstate->statics->boottime), &curr_time);
  	i_timeofday(&curr_time);
  	u_procstates(system_info.p_total, system_info.procstates, gstate->pselect.threads);
  	u_cpustates(system_info.cpustates);
  	u_kernel(system_info.kernel);
  	u_memory(system_info.memory);
  	u_swap(system_info.swap);
  	u_message(&(gstate->now));
  	u_header(hdr);
  	for (i = 0; i < active_procs; i++)
  	{
  	    u_process(i, format_next_process(processes, gstate->get_userid));
  	}
  	u_endscreen();
      }
  }
  
  #ifdef DEBUG
  void
  timeval_xdprint(char *s, struct timeval tv)
  
  {
      xdprintf("%s %d.%06d\n", s, tv.tv_sec, tv.tv_usec);
  }
  #endif
  
  void
  do_wait(globalstate *gstate)
  
  {
      struct timeval wait;
  
      wait.tv_sec = gstate->delay;
      wait.tv_usec = 0;
      select(0, NULL, NULL, NULL, &wait);
  }
  
  void
  do_command(globalstate *gstate)
  
  {
      int status;
      struct timeval wait = {0, 0};
      struct timeval now;
      fd_set readfds;
      unsigned char ch;
  
      /* calculate new refresh time */
      gstate->refresh = gstate->now;
      gstate->refresh.tv_sec += gstate->delay;
      time_get(&now);
  
      /* loop waiting for time to expire */
      do {
  	/* calculate time to wait */
  	if (gstate->delay > 0)
  	{
  	    wait = gstate->refresh;
  	    wait.tv_usec -= now.tv_usec;
  	    if (wait.tv_usec < 0)
  	    {
  		wait.tv_usec += 1000000;
  		wait.tv_sec--;
  	    }
  	    wait.tv_sec -= now.tv_sec;
  	}
  
  	/* set up arguments for select on stdin (0) */
  	FD_ZERO(&readfds);
  	FD_SET(STDIN_FILENO, &readfds);
  
  	/* wait for something to read or time out */
  	if (select(32, &readfds, NULL, NULL, &wait) > 0)
  	{
  	    /* read it */
  	    if (read(STDIN_FILENO, &ch, 1) != 1)
  	    {
  		/* read error */
  		message_error(" Read error on stdin");
  		quit(EX_DATAERR);
  		/*NOTREACHED*/
  	    }
  
  	    /* mark pending messages as old */
  	    message_mark();
  
  	    /* dispatch */
  	    status = command_process(gstate, (int)ch);
  	    switch(status)
  	    {
  	    case CMD_ERROR:
  		quit(EX_SOFTWARE);
  		/*NOTREACHED*/
  		
  	    case CMD_REFRESH:
  		return;
  
  	    case CMD_UNKNOWN:
  		message_error(" Unknown command");
  		break;
  
  	    case CMD_NA:
  		message_error(" Command not available");
  	    }
  	}
  
  	/* get new time */
  	time_get(&now);
      } while (timercmp(&now, &(gstate->refresh), < ));
  }
  
  void
  do_minidisplay(globalstate *gstate)
  
  {
      int real_delay;
      struct system_info si;
  
      /* save the real delay and substitute 1 second */
      real_delay = gstate->delay;
      gstate->delay = 1;
  
      /* wait 1 second for a command */
      time_mark(&(gstate->now));
      do_command(gstate);
  
      /* do a mini update that only updates the cpustates */
      get_system_info(&si);
      u_cpustates(si.cpustates);
  
      /* restore the delay time */
      gstate->delay = real_delay;
  
      /* done */
      i_endscreen();
  }
  
  int
  main(int argc, char *argv[])
  
  {
      char *env_top;
      char **preset_argv;
      int preset_argc = 0;
      void *mask;
      int need_mini = 1;
  
      struct statics statics;
      globalstate *gstate;
  
      /* get our name */
      if (argc > 0)
      {
  	if ((myname = strrchr(argv[0], '/')) == 0)
  	{
  	    myname = argv[0];
  	}
  	else
  	{
  	    myname++;
  	}
      }
  
      /* binary compatibility check */
  #ifdef HAVE_UNAME
      {
  	struct utsname uts;
  
  	if (uname(&uts) == 0)
  	{
  	    if (strcmp(uts.machine, UNAME_HARDWARE) != 0)
  	    {
  		fprintf(stderr, "%s: incompatible hardware platform\n",
  			myname);
  		exit(EX_UNAVAILABLE);
  	    }
  	}
      }
  #endif
  
      /* initialization */
      gstate = (globalstate *)calloc(1, sizeof(globalstate));
      gstate->statics = &statics;
      time_mark(NULL);
  
      /* preset defaults for various options */
      gstate->show_usernames = Yes;
      gstate->topn = DEFAULT_TOPN;
      gstate->delay = DEFAULT_DELAY;
      gstate->fulldraw = Yes;
      gstate->use_color = Yes;
      gstate->interactive = Maybe;
  
      /* preset defaults for process selection */
      gstate->pselect.idle = Yes;
      gstate->pselect.system = No;
      gstate->pselect.fullcmd = No;
      gstate->pselect.command = NULL;
      gstate->pselect.uid = -1;
      gstate->pselect.mode = 0;
  
      /* use a large buffer for stdout */
  #ifdef HAVE_SETVBUF
      setvbuf(stdout, stdoutbuf, _IOFBF, BUFFERSIZE);
  #else
  #ifdef HAVE_SETBUFFER
      setbuffer(stdout, stdoutbuf, BUFFERSIZE);
  #endif
  #endif
  
      /* get preset options from the environment */
      if ((env_top = getenv("TOP")) != NULL)
      {
  	preset_argv = argparse(env_top, &preset_argc);
  	preset_argv[0] = myname;
  	do_arguments(gstate, preset_argc, preset_argv);
      }
  
      /* process arguments */
      do_arguments(gstate, argc, argv);
  
  #ifdef ENABLE_COLOR
      /* If colour has been turned on read in the settings. */
      env_top = getenv("TOPCOLOURS");
      if (!env_top)
      {
  	env_top = getenv("TOPCOLORS");
      }
      /* must do something about error messages */
      color_env_parse(env_top);
      color_activate(gstate->use_color);
  #endif
  
      /* in order to support forward compatability, we have to ensure that
         the entire statics structure is set to a known value before we call
         machine_init.  This way fields that a module does not know about
         will retain their default values */
      memzero((void *)&statics, sizeof(statics));
      statics.boottime = -1;
  
      /* call the platform-specific init */
      if (machine_init(&statics) == -1)
      {
  	exit(EX_SOFTWARE);
      }
  
      /* create a helper list of sort order names */
      gstate->order_namelist = string_list(statics.order_names);
  
      /* look up chosen sorting order */
      if (gstate->order_name != NULL)
      {
  	int i;
  
  	if (statics.order_names == NULL)
  	{
  	    message_error(" This platform does not support arbitrary ordering");
  	}
  	else if ((i = string_index(gstate->order_name,
  				   statics.order_names)) == -1)
  	{
  	    message_error(" Sort order `%s' not recognized", gstate->order_name);
  	    message_error(" Recognized sort orders: %s", gstate->order_namelist);
  	}
  	else
  	{
  	    gstate->order_index = i;
  	}
      }
  
      /* initialize extensions */
      init_username();
  
      /* initialize termcap */
      gstate->smart_terminal = screen_readtermcap(gstate->interactive);
  
      /* determine interactive state */
      if (gstate->interactive == Maybe)
      {
  	gstate->interactive = smart_terminal;
      }
  
      /* if displays were not specified, choose an appropriate default */
      if (gstate->displays == 0)
      {
  	gstate->displays = gstate->smart_terminal ? Infinity: 1;
      }
  
      /* we don't need a mini display when delay is less than 2
         seconds or when we are not on a smart terminal */
      if (gstate->delay <= 1 || !smart_terminal)
      {
  	need_mini = 0;
      }
  
  #ifndef HAVE_FORMAT_PROCESS_HEADER
      /* set constants for username/uid display */
      if (gstate->show_usernames)
      {
  	gstate->header_text = format_header("USERNAME");
  	gstate->get_userid = username;
      }
      else
      {
  	gstate->header_text = format_header("   UID  ");
  	gstate->get_userid = itoa7;
      }
  #endif
      gstate->pselect.usernames = gstate->show_usernames;
  
      /* initialize display */
      if ((gstate->max_topn = display_init(&statics)) == -1)
      {
  	fprintf(stderr, "%s: can't allocate sufficient memory\n", myname);
  	exit(EX_OSERR);
      }
  
      /* check for infinity and for overflowed screen */
      if (gstate->topn == Infinity)
      {
  	gstate->topn = INT_MAX;
      }
      else if (gstate->topn > gstate->max_topn)
      {
  #if 0
  	message_error(" This terminal can only display %d processes",
  		      gstate->max_topn);
  #endif
      }
  
  #ifdef ENABLE_COLOR
      /* producing a list of color tags is easy */
      if (gstate->show_tags)
      {
  	color_dump(stdout);
  	exit(EX_OK);
      }
  #endif
  
      /* hold all signals while we initialize the screen */
      mask = hold_signals();
      screen_init();
  
      /* set the signal handlers */
      set_signals();
  
      /* longjmp re-entry point */
      /* set the jump buffer for long jumps out of signal handlers */
      if (setjmp(jmp_int) != 0)
      {
  	/* this is where we end up after processing sigwinch or sigtstp */
  
  	/* tell display to resize its buffers, and get the new length */
  	if ((gstate->max_topn = display_resize()) == -1)
  	{
  	    /* thats bad */
  	    quit(EX_OSERR);
  	    /*NOTREACHED*/
  	}
  
  	/* set up for a full redraw, and get the current line count */
  	gstate->fulldraw = Yes;
  
  	/* safe to release the signals now */
  	release_signals(mask);
      }
      else
      {
  	/* release the signals */
  	release_signals(mask);
  
  	/* some systems require a warmup */
  	/* always do a warmup for batch mode */
  	if (gstate->interactive == 0 || statics.flags.warmup)
  	{
  	    struct system_info system_info;
  	    struct timeval timeout;
  
  	    time_mark(&(gstate->now));
  	    get_system_info(&system_info);
  	    (void)get_process_info(&system_info, &gstate->pselect, 0);
  	    timeout.tv_sec = 1;
  	    timeout.tv_usec = 0;
  	    select(0, NULL, NULL, NULL, &timeout);
  
  	    /* if we've warmed up, then we can show good states too */
  	    gstate->show_cpustates = Yes;
  	    need_mini = 0;
  	}
      }
  
      /* main loop */
      while ((gstate->displays == -1) || (--gstate->displays > 0))
      {
  	do_display(gstate);
  	if (gstate->interactive)
  	{
  	    if (need_mini)
  	    {
  		do_minidisplay(gstate);
  		need_mini = 0;
  	    }
  	    do_command(gstate);
  	}
  	else
  	{
  	    do_wait(gstate);
  	}
      }
  
      /* do one last display */
      do_display(gstate);
  
      quit(EX_OK);
      /* NOTREACHED */
      return 1; /* Keep compiler quiet. */
  }
  