/*
 * 	Copyright (C) 1994  Loganville Technical Resources, Incorporated.
 *
 * Xzet is free software; you can redistribute it and/or modify it under the 
 * terms of the GNU General Public License as published by the Free Software 
 * Foundation; either version 2, or (at your option) any later version.  
 *
 * Xzet is distributed in the hope that it will be useful, but 
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
 * or FITNESS FOR A PARTICULAR PURPOSE. 
 *
 *  See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along with 
 * xzet; see the file  COPYING. If  not, write to the Free Software Foundation, 
 * 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *
 * xzet - Zmans version of xetat and top.  Graphical system activity and 
 *	  process monitoring utility.  Featuring the unknown ztach widget.
 *
 *
 *  		This is bi-lingual source code.  Deal with it.
 *
 *
 * Its bad enough this is my first X program, but this was definitly not a good 
 * program to pick for it.  Making a morphodite of top and etat has resulted
 * in a seriously ugly piece of code.  Etats way of tracking the state of all  
 * POP'D up programs, which may look elegant at first, is A) Questionably
 * lacking in space efficiency, B) Prone to cause crashes when the list of
 * processes is updated and the address stored in the graph callback procedure  
 * for the POP'D up process(es) may now point to a process entry whose address  
 * may be different, or whose internal address pointers (of which there are many) 
 * may be different (the latter has caused me considerable grief) and C) would
 * be better off either melted into the ps_proc data structure now used as the
 * source of all knowledge, or at the very least chopped down to its essential
 * elements and then allocated as the ps_proc structure are created and pointed
 * to by them.     (By the way "B" occurs due to mise_a_jour_liste_processes)
 *
 * The entire idea of tracking processes, but not using their process id to
 * do it seems like utter folly on the surface, doesn't it?  Using the pointer
 * to a structure (in an array of these structures) created off an arbitrary 
 * index into a widgets list to track processes might work if A) the array was
 * static (as were all address referenced within its structures), B) the list
 * was static or C) If memory pointers that were freed and reallocated always
 * pointed back to the same place (Oy! who would count on such a thing?)
 *
 * Alright, alright, enough all ready. You've been warned, now heres the beast:
 *
 *   Not enough already! I got so sick of it, even after I got it to work
 *   that I changed it to track the widget pointers via the pid. As a side
 *   benefit, only one copy of the process info is kept, reducing the core
 *   process size and cpu overhead (by cutting out a hundred+ lines of code).  
 *
 *
 * The following copyright information is from xetat:
 * 
 * xetat - graphical system activity reporter
 *
 * Copyright 1991 Lectra Systemes
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted, provided
 * that the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of Lectra Systemes not be used in advertising or
 * publicity pertaining to distribution of the software without specific,
 * written prior permission.  Lectra Systemes makes no representations about the
 * suitability of this software for any purpose.  It is provided "as is"
 * without express or implied warranty.
 *
 * Auteur : Pierre FICHEUX 	Lectra Systemes R & D
 *
 */
static char sccsid[] = "@(#)process.c	2.0 02/04/94";

/*
 * Fonctions de visualisation des processus		
 */

#include <stdio.h>
#include <string.h>
#include <time.h>

/*
 * Fichiers d'inclusion X11
 */
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
/* #include <X11/XawMisc.h>            */
#include <X11/Xaw/Form.h>
#include <X11/Xaw/Label.h>
#include <X11/Shell.h>
#include <X11/Xaw/List.h>
#include <X11/Xaw/Viewport.h>
#include "Ztach/Ztach.h"

#ifdef X11R3
#include <X11/Command.h>
#include <X11/Box.h>
#else /* R4 or later */
#include <X11/Xaw/Command.h>
#include <X11/Xaw/Box.h>
#endif /* X11R3 */

#include <sys/types.h>
#include <sys/param.h>
#include <sys/signal.h>
#include <sys/dir.h>		  
#include <sys/user.h>              
#include <nlist.h>
#include <fcntl.h>

/* z added stuff */
#include "psdata.h"
#include "ps.h"
#include "Xzet.h"
#include <linux/tasks.h>


extern 	struct ps_proc_head *ph;         /* where we keep all task data */
extern 	struct z_system_info z_info;      /* global time and mem information */
extern 	char	active_running;
extern	char    there_are_popups;
char    process_running = FALSE;
extern  XtIntervalId	the_big_cahuna_meister;
extern 	struct  save_hist new_save_hist[NR_TASKS];
extern 	Widget	zman_short_1, zman_short_2, zman_long;
extern	Widget  bouton_quit_processus, bouton_mise_a_jour_liste;
extern	Widget	forme_base, forme_processus, viewport_processus;
int	list_columns = 0;
static 	char	short_mode = TRUE;           /* start in short mode */
int	short_list; 			/* Set in ap default (460), if fubar  */
int	long_list;        		/* ..things can get ugly, long=1.525x */
char	auto_update_mode = FALSE;
static char *auto_up = "Auto Update";
static char *freeze = "Freeze List";
char *proc_state(struct ps_proc *);     
void mesure_charge_courante_systeme();
void *xcalloc(void *, int);

/* Structure de definition d'un shell processus  definition_processus */
/* This struct was changed to the following: it is defined in Xzet.h */
/*
 	 struct process_widgets {
				char flag_shell_actif;	
				Widget popup_shell; 	
				Widget forme;         
				Widget label_titre;	
				Widget label_car;
				Widget cpu_hog;		
				Widget mem_hog;		
				Widget task_mem;
				Widget free_mem;       
				Widget quit;           
						}; */

/* Maxi 400 processus, (actually NR_TASKS is set to 128 by default) */

#define MAXPROC	NR_TASKS

extern Pixmap pixmap_processus, pixmap_plist;

extern Widget prototype_bouton_quit, prototype_label_titre;
extern Widget prototype_free_mem, prototype_task_mem, prototype_cpu_hog; 
extern Widget prototype_mem_hog, shell_processus, liste_processus;

static int nb_processus;		/* nombre courant de processus */
String table_processus[MAXPROC];	/* liste des noms des rpocessus */
static char buf[100], buf1[100],buf2[100];	
static long valeur_freemem;		/* valeur courante freemem */
char flag_mise_a_jour_autorisee;	/* TRUE si la mise a jour est autorisee */

extern char host[];

void mise_a_jour_liste_processus ();
void Mise_a_jour_liste (Widget, caddr_t, caddr_t);
void Selection();
void Quit_processus(Widget, caddr_t, caddr_t);



/****************************************************************************
*
*			CALLBACK FOR LIST COLUMNS/FORMAT CHANGE
*		woofa goofa momma toofa
* There really must be an easier way to do this (there certainly is with
* ("Windows" (tm) programming), but as this is my first x-windows program and
* I beat my brains, and got my lunch and dinner and next breakfast eaten 
* trying to find it, I will settle for this.  If you know of another way to
* accomplish this with significantly less code/cpu overhead, LET ME KNOW!	
******************************************************************************/
void Zcolform(w, closure, call_data)
Widget  w;
caddr_t closure;        /* unused */
caddr_t call_data;      /* unused */
{
	Arg 	args[6];   /* maybe add Bool forceColumns XtNforceColumns? */
	int	n;
	char	old_mode=short_mode;
	int	old_columns=list_columns;
	Dimension Zed=0;
	Dimension Wh, Ww;
	Position  Shellx, Shelly, Viewx, Viewy, OldViewx, OldViewy;
	Dimension Shellbw, OldShellh, NewShellh, OldViewh, SVd, Viewbw;
	char	buf[20];
	
	n = 0;
        short_mode = ((w == zman_short_1 || w == zman_short_2) ? TRUE : FALSE);
        list_columns = ((w == zman_short_1 || w == zman_long) ? 1 : 2); 
             
        if (list_columns != old_columns || short_mode != old_mode)
              {	
              /* get some information about this freaking mess ! */
                n = 0;
                XtSetArg(args[n], XtNheight, &OldShellh); n++;
                XtGetValues(shell_processus, args, n);
                n = 0;
                XtSetArg(args[n], XtNheight, &OldViewh); n++;
                XtSetArg(args[n], XtNx, &OldViewx); n++;                
                XtSetArg(args[n], XtNy, &OldViewy); n++;                                
                XtGetValues(viewport_processus, args, n);
                SVd = OldShellh - OldViewh;        /* button space is diff */          
                
              	XtPopdown (shell_processus); /* let's pull the rug out */
              	XtRemoveCallback(liste_processus, XtNcallback, Selection, 0);
              	      /* Destroy all pop-ups, as their parent is about to go */
		for(n=0; n < nb_processus; n++)
			if (new_save_hist[n].widgets_ptr->flag_shell_actif == TRUE)
			       {
			        XtPopdown(new_save_hist[n].widgets_ptr->popup_shell);
			       	XtDestroyWidget(new_save_hist[n].widgets_ptr->popup_shell);
			       	free(new_save_hist[n].widgets_ptr);
			       	new_save_hist[n].widgets_ptr = 
			       			(struct process_widgets *)NULL;
              		       }	
        	XtDestroyWidget(shell_processus);  /* and all children */        	
        	n = 0;
        	XtSetArg(args[n], XtNiconPixmap, pixmap_plist); n++;
       		shell_processus = XtCreatePopupShell (strcat (strcpy (buf, host), "proc"),
	       	             applicationShellWidgetClass, forme_base, args, n); 
		forme_processus = XtCreateManagedWidget ("forme_processus",
				formWidgetClass,  shell_processus, NULL, 0);
		viewport_processus = XtCreateManagedWidget ("viewport_processus",
                                viewportWidgetClass, forme_processus, NULL, 0);
		liste_processus = XtCreateManagedWidget("liste_processus", listWidgetClass,
						viewport_processus, args, n);
		n=0;
		XtSetArg(args[n], XtNdefaultColumns, list_columns); n++;
		XtSetArg(args[n], XtNlongest, 
        		( short_mode ? short_list : long_list )); 
        	n++;
        	XtSetArg(args[n], XtNwidth, Zed); n++;      /* force new */
        	XtSetArg(args[n], XtNheight, Zed); n++;     /* ...calc   */
        	XtSetArg (args[n], XtNlist, (String *)table_processus); n++; 
		XtSetValues(liste_processus, args, n);
		XtAddCallback(liste_processus, XtNcallback, Selection, 0);
		XtRealizeWidget(shell_processus);       /* does children too */
		n = 0;
                XtSetArg(args[n], XtNwidth, &Ww); n++;
                XtSetArg(args[n], XtNheight, &Wh); n++;
                XtSetArg(args[n], XtNx, &Shellx); n++;
                XtSetArg(args[n], XtNy, &Shelly); n++; 
                XtSetArg(args[n], XtNborderWidth , &Shellbw); n++;                                                
                XtGetValues(shell_processus, args, n);
                XtConfigureWidget(shell_processus, Shellx, Shelly, Ww,
               		((old_columns > list_columns) ? (OldViewh * 2) + SVd: 
                	((old_columns < list_columns) ? (OldViewh / 2) + SVd: 
                	  OldShellh)), Shellbw );     /* this also gets form */
                n = 0;
                XtSetArg(args[n], XtNwidth, &Ww); n++;
                XtSetArg(args[n], XtNheight, &Wh); n++;
                XtGetValues(shell_processus, args, n);
                NewShellh = Wh; 
                n = 0;
		XtSetArg(args[n], XtNwidth, &Ww); n++;
                XtSetArg(args[n], XtNheight, &Wh); n++;
                XtSetArg(args[n], XtNx, &Viewx); n++;
                XtSetArg(args[n], XtNy, &Viewy); n++; 
                XtSetArg(args[n], XtNborderWidth , &Viewbw); n++;                                                
                XtGetValues(viewport_processus, args, n);
                XtConfigureWidget(viewport_processus, OldViewx, OldViewy, Ww,
                	  NewShellh - SVd, Viewbw );   /* this also gets list?*/
		
		/* the list widget does automatically adjust its size also,
                   but it starts out (after realize for shell) at 15 less 
                   than the viewport, and now still is, so adjust it  anyway */
                XtConfigureWidget(liste_processus, 0, 0, Ww, Wh, 0); 
                				/*empiracly known correct zeds*/
                
                /* now the buttons, done last to prevent having to move */	
	
		bouton_quit_processus = XtCreateManagedWidget ("bouton_quit_processus", 
	                        commandWidgetClass, forme_processus, NULL, 0);
	        bouton_mise_a_jour_liste = XtCreateManagedWidget ("bouton_mise_a_jour_liste",
	        		commandWidgetClass, forme_processus, NULL, 0);
	       	n = 0;
		XtSetArg (args[n], XtNlabel, 
			(auto_update_mode == FALSE ? auto_up : freeze)); n++;
		XtSetValues (bouton_mise_a_jour_liste, args, n);
	        		
	 	zman_short_1 = XtCreateManagedWidget ("zman_short_1", 
	 			commandWidgetClass, forme_processus, NULL, 0);
		zman_short_2 = XtCreateManagedWidget("zman_short_2",
	           		commandWidgetClass, forme_processus, NULL, 0);
	     	zman_long = XtCreateManagedWidget("zman_long",
	           		commandWidgetClass, forme_processus, NULL, 0);
	        XtAddCallback(zman_short_1, XtNcallback, Zcolform, 0); 
	        XtAddCallback(zman_short_2, XtNcallback, Zcolform, 0);          
	        XtAddCallback(zman_long, XtNcallback, Zcolform, 0);                                                                                                                                                     
	 	XtAddCallback(bouton_quit_processus, XtNcallback, 
	 				(XtCallbackProc)Quit_processus, 0);
	     	XtAddCallback(bouton_mise_a_jour_liste, XtNcallback, 
	     				(XtCallbackProc)Mise_a_jour_liste, 0);
	
		XtUnrealizeWidget(shell_processus); 	/* this tomfoolery is */
		XtRealizeWidget(shell_processus);   	/* ...for buttons */
        	
        /* Yuk! Deal with wierd bug.  If we start out (via ap defaults) with
        	only 1 column, then the list widget will have a width which
        	is 15 pixels less than the viewport ONLY WHEN WE SWITCH TO 2
        	COLUMNS.  (This is discussed and corrected above)  This bit
        	of ugliness rears its ugly head again when the shell widget
        	is Unrealized and Re-Realized (which we do to get all the 
        	buttons to show up).  So now we must reconfigure the list.
	*/	
		if (list_columns == 2)
		       {	
			n = 0;
			XtSetArg(args[n], XtNwidth, &Ww); n++;
                	XtSetArg(args[n], XtNheight, &Wh); n++;
                	XtGetValues(viewport_processus, args, n);
			XtConfigureWidget(liste_processus, 0, 0, Ww, Wh, 0); 
		       }        	
        	
        	mise_a_jour_liste_processus ();
        	XtUnmapWidget (liste_processus);
    		XtMapWidget (liste_processus);
        	XtPopup (shell_processus, XtGrabNone);
               }	
}



/****************************************************************************
*
*			QUIT MAIN PROCESS LIST WINDOW
*			
******************************************************************************/
void Quit_processus(w, client_data, call_data)
Widget w;
caddr_t client_data, call_data;
{
	Arg	args[1];
	int	n = 0;
	
	if (auto_update_mode == TRUE)       /* conserve power, turn it off */
	       {	
		XtSetArg (args[n], XtNlabel, auto_up); n++;
		XtSetValues (bouton_mise_a_jour_liste, args, n);
		auto_update_mode = FALSE;
	       }	
#ifdef DEBUG
    printf ("Quitte processus...\n");
#endif
    XtPopdown (shell_processus);
    process_running = FALSE;
    if (active_running == FALSE && there_are_popups == FALSE)
       {
#ifdef DEBUG       
       	printf("\nNo One Left, Removing timeout >%lx", the_big_cahuna_meister);	
    	fflush(stdout);
#endif    	
    	XtRemoveTimeOut(the_big_cahuna_meister);
       } 	
}

 

/****************************************************************************
*
*			QUIT INDIVIDUAL PROCESS  WINDOW
*			
******************************************************************************/
/*
 * Ferme la fenetre apres click sur Ok (sans tuer le shell)
 *
 *       Changed to actually get rid of the widget so that we can
 *       idle the whole process if active and processus are no longer
 *       running and thereby conserve cpu. (Xzet cpu usage should go to zero)
 *
 */
void Quit_definition_processus(w, client_data, call_data)
Widget w;
caddr_t client_data, call_data;
{
	int nsh_index;
		
	nsh_index = 0;
	while(nsh_index < nb_processus)
	       {
	        if (new_save_hist[nsh_index].widgets_ptr->popup_shell == 
	        				(Widget)client_data)
			break;
	         nsh_index++;
	        }
	if (nsh_index < nb_processus)
	       {		        
    		XtPopdown(new_save_hist[nsh_index].widgets_ptr->popup_shell);
    		XtDestroyWidget(new_save_hist[nsh_index].widgets_ptr->popup_shell);
    		free(new_save_hist[nsh_index].widgets_ptr);
    		new_save_hist[nsh_index].widgets_ptr = (struct process_widgets *)NULL;
    	       }
#ifdef DEBUG    	       
   	else
   	       {					
   		printf("\nUnable to locate PopUP!");
   		fflush(stdout);
   	       }
#endif
   	       	 	 
}



/****************************************************************************
*
*			UPDATE PROCESS LIST WINDOW
*	This has been changed to toggle between auto update and frozen		
******************************************************************************/
/*
 * Mise a jour manuelle
 */
void Mise_a_jour_liste (w, client_data, call_data)
Widget w;
caddr_t client_data, call_data;
{
    register int n;
    Arg args[10];
    
    flag_mise_a_jour_autorisee = FALSE;

	/* Toggle state of process list updating, set button label apropos */
	
	n = 0;
	XtSetArg (args[n], XtNlabel, 
			(auto_update_mode == TRUE ? auto_up : freeze)); n++;
	XtSetValues (bouton_mise_a_jour_liste, args, n);
	auto_update_mode = (auto_update_mode == TRUE ? FALSE : TRUE);
	        	
/* This could be done here for immediate gratification, otherwise it will be
					done by mesure_charge_currente_systeme
    mise_a_jour_liste_processus ();
    
    XtUnmapWidget (liste_processus);
    XtMapWidget (liste_processus);
*/   
    /* la mise a jour est de nouveau autorisee */
    flag_mise_a_jour_autorisee = TRUE;
}



/****************************************************************************
*
*			UPDATE INFORMATION FOR SINGLE PROCESS
*			
******************************************************************************/
/*
 * Fonction de mise a jour d'un processus a partir de son indice dans
 * la liste definition_processsus
 */
			/*  ZMAN - major changes here */
			/* You can say that again!, Now that     */
			/* I've gone to a merged pointer system  */
			/* This function isn't even needed!      */
/* mise_a_jour_d_un_processus (indice_processus)
int indice_processus; */



/****************************************************************************
*
*		UPDATE POP-UP's DISPLAY INFORMATION FOR SINGLE PROCESS
*			        
* This was formerly mise_a_jour_graphe_process and was a callback for each
* popups stripchart, set in Selection.  Now it is called by do_stats as it
* resets the pointer in new_save_hist of each active popup process window.  
******************************************************************************/

void update_process_tachs_etc(nsh_index)
     int	nsh_index;	

/*  caddr_t	client_data; */	/* pointe le numero du processus dans la liste */
/* The source of all Bitching.........You are not pointing to the number of the
   process that we wish to update! Not the List entry, Not the pid! No you
   crazy frenchman, you are pointing at the address of the structure!  Ye gads
   you wildman, how can you do this and then let mise_a_jour_liste_processus
   re-allocate the structure!!!     	se non e vero e ben trovato? no!  
   			del'audace, encore del'audace, et toujours de l'audace!
*/
{
	struct process_widgets *p;
    	int n;
    	Arg args[10];
    	struct ps_proc *current_proc;
    	long	value;

#ifdef DEBUG	
	printf("\nUpdating POP-UP for Process with pointer: >%lx, Value: %d",
				(long)(client_data), (int)client_data);
	fflush(stdout);
#endif

	/* Ok, lets find our man  We have to not only find the widget
	  pointer to him in new_save_hist, we also must find the actual
	  data in the ps_proc linked list.  First we get the widget pointer: */
		/* CHANGED: Now we come in with the new_save_hist index */

	p = (struct process_widgets *)new_save_hist[nsh_index].widgets_ptr;
	       		 

	current_proc = ph->head;	/* NOW find the ps_proc entry */
	while (current_proc != NULL)
	       {
	       	if (current_proc->pid == new_save_hist[nsh_index].pid)
	       		break;
	       	current_proc = current_proc->next;
	       }				

    	sprintf(buf1, "%3d:%02d ", (current_proc->stime / CLK_TCK) / 60, 
    					(current_proc->stime / CLK_TCK) % 60);
    	sprintf(buf2, "%3d:%02d ", (current_proc->utime / CLK_TCK) / 60, 
    					(current_proc->utime / CLK_TCK) % 60);
    

	/* Label looks like this -> PPID  TTY   STIME  UTIME  USER<- */

    	sprintf (buf, "%-5d %-4s %-6s%-6s %s", current_proc->ppid, 
    			current_proc->ttyc, buf1, buf2, current_proc->user);


    	  					/* SET LABEL OF PROCESS INFO */
    	n = 0;
    	XtSetArg (args[n], XtNlabel, buf); n++;
    	XtSetValues (p->label_car, args, n);
    
    	valeur_freemem = z_info.free_mem;
    
    						/* SET TASK MEMORY LABEL  */
    	sprintf (buf, "T:%-4d", current_proc->vsize);
    	n = 0;
    	XtSetArg (args[n], XtNlabel, buf); n++;
    	XtSetValues (p->task_mem, args, n);
    
 						/* SET FREE MEMORY LABEL */
    	sprintf (buf, "F:%-4d", valeur_freemem);
    	n = 0;
    	XtSetArg (args[n], XtNlabel, buf); n++;
    	XtSetValues (p->free_mem, args, n);
    
 						/* SET TACHOMETERS */   
 	value = new_save_hist[nsh_index].pcpu;   	
        n = 0;
        XtSetArg(args[n], XtNvalue, value); n++;
        XtSetValues(p->cpu_hog, args, n);
                             
        value = 4096000. * (float)current_proc->rss / z_info.main_mem;                     
    	n = 0;
        XtSetArg(args[n], XtNvalue, (int)value); n++;
        XtSetValues(p->mem_hog, args, n);
    
#ifdef DEBUG
    	printf("...Done with Update\n");					
    	fflush(stdout);
#endif
    	    
}



/****************************************************************************
*
*	  CALLBACK FUNCTION TO UPDATE INFORMATION FOR ALL EXPOSED PROCESSES
*	This runs every 3 seconds to clear out exposed processes that are
*       no longer running and to get stats for those that are		
******************************************************************************/
/*
 * Fonction de mise a jour periodique des informations sur les processus
 */
		/* As with mise_a_jour_d_un_processus, now that */
		/* I've gone to a merged pointer system, this   */
		/* function is no longer needed!   The exposed  */
		/* processes that are no longer active will be  */
		/* cleared in activity.c by do_stats which is   */
		/* called by mesure_charge_courante, which is   */
		/* automatically invoked with Processus.        */
		/* The list will not be updated, but the user   */
		/* should get the point when the popup disappears*/
		/* I'm still debating whether or not to just set */
		/* the list to auto-update.  Now that I've made */
		/* so many changes, the CPU overhead may be low */
		/* enought to enable this. It was to high before*/
		 
/* XtTimerCallbackProc mise_a_jour_processus (closure, id)     No No NO!*/
/* void mise_a_jour_processus (closure, id)
caddr_t closure;
XtIntervalId *id; */




/****************************************************************************
*
*		CREATE POPUP WINDOW FOR SINGLE PROCESS SELECTED FROM LIST
*			
******************************************************************************/
/* Changed to be completely process id based */
/* Changed even more....
         I never really thought this window was that interesting,
         especially in light of having the process list now be 
         optionally auto-updating.  I have come to have some appreciation
         for the the process memory vs. free memory stripcharts ability
         to show this aspects of the  processes load regardless of the 
         temporal (ephemeral?) nature of its or freemems size, however,
         I think it my be more useful to show graphs of the instantaneous
	 'hog factor' of the processes (that is percent cpu utilization
	 and percent memory utilization), and just enumerate the other 
	 interesting information.  I have created a tachometer widget
	 especially for this purpose.
	 
	 Note that the memory tach indicates the percent of user physical 
	 memory used by the process, whereas the memory label ("T:xxxxxx") 
	 specfies the size of the CODE+DATA+STACK size of the task, which may 
	 be significantly larger or smaller than the physical memory.
*/	 
/*
 * Selection d'un processsus
 * Cette fonction est appelee lors de la selection d'un processus dans la liste
 * affichee. Elle ouvre un popup shell contenant les informations concernant
 * le processus.
 */
void Selection (w, client_data, call_data)
Widget w;
caddr_t client_data, call_data;
{
    int n;
    Arg args[10];
    int distance_horizontale;
    int distance_vertical;
    Dimension largeur, hauteur;
    String label;
    Pixel background;
    XtJustify justification;
    Boolean showgrip;
    Boolean localbool;    
    
    	unsigned int	ztime;
    	struct ps_proc  *current_proc;
    	int	selections_pid;
    	char	*cmd_line;
    	int	nsh_index;	

    	XawListReturnStruct *p = (XawListReturnStruct *) call_data;

#ifdef DEBUG
    printf (stderr, "Selection\n"); 
#endif

		/* The first thing to do is find the process id, which 
	   	is contained in the selection line as its first entry */
	
	sscanf(p->string, "%d", &selections_pid);	   

			/* now lets get the ps_proc data for this cat */	
	current_proc = ph->head;	
	while (current_proc != NULL)
	       {
	       	if (current_proc->pid == selections_pid)
	       		break;
	       	current_proc = current_proc->next;
	       }
	
		/* next lets get the index to the new_save_hist array so
	   			we can set the process_widgets pointer */	
	nsh_index = 0;
	while(nsh_index < nb_processus)
	       {
	        if (new_save_hist[nsh_index].pid == selections_pid)
			break;
	         nsh_index++;
	        }
	
			/* get the command line for the shell window title */
	if (strlen(current_proc->cmdline) > 0)
	      		cmd_line = current_proc->cmdline; 
	 	else 
	      		cmd_line = current_proc->cmd;  
	      	if (strlen(cmd_line) > 20 )
	      		cmd_line[20] = '\0';
	      		
	      	/* finally, allocate memory for the process_widgets struct */
	if (new_save_hist[nsh_index].widgets_ptr == NULL)	      		
      		new_save_hist[nsh_index].widgets_ptr = 
         (struct process_widgets *)xcalloc(new_save_hist[nsh_index].widgets_ptr,
			sizeof(struct process_widgets));	

	/* and now, back to our regularly scheduled program.......*/

    /* Si le processus n'est pas deja espionne */
    if (new_save_hist[nsh_index].widgets_ptr->flag_shell_actif == FALSE) 
    {

	/* On cree un shell application au nom du processus    CREATE SHELL */
	n = 0;
	/* put the host, process id and the command line in the pop-up title */
	sprintf(buf, "%s   %d - %s", host, selections_pid, cmd_line);
	 		
	XtSetArg(args[n], XtNiconPixmap, pixmap_processus); n++;
	new_save_hist[nsh_index].widgets_ptr->popup_shell =
		XtCreatePopupShell (buf, 		
			applicationShellWidgetClass, liste_processus, args, n);
	
	/* 
	 * Creation du widget forme                         CR/MANAGED WIDGET
	 */
	sprintf (buf, "forme%d", selections_pid);
	new_save_hist[nsh_index].widgets_ptr->forme = 
		XtCreateManagedWidget (buf, formWidgetClass, 
		   new_save_hist[nsh_index].widgets_ptr->popup_shell, NULL, 0);
	
	/* 
	 * Creation du widget titre                   SET and CR/MANAGED TITLE
	 */
	/* lecture des valeurs dans le prototype */
	n = 0;
	XtSetArg (args[n], XtNlabel, &label); n++;
	XtSetArg (args[n], XtNwidth, &largeur); n++;
	XtSetArg (args[n], XtNjustify, &justification); n++;		
	XtGetValues (prototype_label_titre, args, n);

	sprintf (buf, "label_titre%d", selections_pid);
	n = 0;
	XtSetArg (args[n], XtNlabel, label); n++;
	XtSetArg (args[n], XtNwidth, largeur); n++;
	XtSetArg (args[n], XtNjustify, justification); n++;		
	new_save_hist[nsh_index].widgets_ptr->label_titre = 
		XtCreateManagedWidget (buf, labelWidgetClass, 
			new_save_hist[nsh_index].widgets_ptr->forme, args, n);

	/*						SET THE PROCESS DATA 
	 * Creation du widget caracteristiques
	 */
	/* formatage des valeurs */

	ztime = current_proc->stime;
	sprintf(buf1, "%3d:%02d ", (ztime/CLK_TCK) / 60, (ztime/CLK_TCK) % 60); 
	ztime = current_proc->utime;
	sprintf(buf2, "%3d:%02d ", (ztime/CLK_TCK) / 60, (ztime/CLK_TCK) % 60);
	

	/* Label looks like this -> PPID  TTY   STIME  UTIME  USER<- */

    	sprintf (buf, "%-5d %-4s %-6s%-6s %s", current_proc->ppid, 
    			current_proc->ttyc, buf1, buf2, current_proc->user);
	
	/* creation du widget */		/* SET and CR/MANAGED LABEL */
	n = 0;
	XtSetArg (args[n], XtNlabel, buf); n++;
	XtSetArg (args[n], XtNborderWidth, 0); n++;
	XtSetArg (args[n], XtNfromVert, 
		new_save_hist[nsh_index].widgets_ptr->label_titre); n++;
	sprintf (buf1, "label_car%d", selections_pid);
	new_save_hist[nsh_index].widgets_ptr->label_car = 
		XtCreateManagedWidget (buf1, labelWidgetClass, 
		          new_save_hist[nsh_index].widgets_ptr->forme, args, n);

					     /* SETUP HOG FACTOR TACHOMETERS  */
	/* set up tachs from prototypes  */
	n = 0;
	XtSetArg (args[n], XtNwidth, &largeur); n++;
	XtSetArg (args[n], XtNheight, &hauteur); n++;
	XtSetArg (args[n], XtNulabel, &label); n++;	
	XtGetValues (prototype_cpu_hog, args, n);
	n = 0;
	XtSetArg (args[n], XtNwidth, largeur); n++;
	XtSetArg (args[n], XtNheight, hauteur); n++;
	XtSetArg (args[n], XtNulabel, label); n++;	
	XtSetArg (args[n], XtNfromVert, 
			new_save_hist[nsh_index].widgets_ptr->label_car); n++;	
	sprintf (buf, "cpu_hog%d", selections_pid);
	
						/* CR/MANAGED CPU TACHOMETER */
	new_save_hist[nsh_index].widgets_ptr->cpu_hog = 
			XtCreateManagedWidget(buf, ztachWidgetClass, 
			 new_save_hist[nsh_index].widgets_ptr->forme, args, n);
	/* no monkey around */
	XtUninstallTranslations( new_save_hist[nsh_index].widgets_ptr->cpu_hog);

	/* No callback for these two widgets.  Their values will be explicitly
	   set by do_stats, since that function knows about all active pop-ups
	   and also gathers all process related information. Actually do_stats
	   will call mise_a_jour_graphe_process.	  	   */	
	n = 0;						/* now do mem tach */
	XtSetArg (args[n], XtNwidth, &largeur); n++;
	XtSetArg (args[n], XtNheight, &hauteur); n++;
	XtSetArg (args[n], XtNulabel, &label); n++;
	XtSetArg (args[n], XtNrotateCW, &localbool); n++;		
	XtGetValues (prototype_mem_hog, args, n);
	n = 0;
	XtSetArg (args[n], XtNwidth, largeur); n++;
	XtSetArg (args[n], XtNheight, hauteur); n++;
	XtSetArg (args[n], XtNulabel, label); n++;
	XtSetArg (args[n], XtNrotateCW, localbool); n++;	
	XtSetArg (args[n], XtNfromHoriz, 
			new_save_hist[nsh_index].widgets_ptr->cpu_hog); n++;	
	XtSetArg (args[n], XtNfromVert, 
			new_save_hist[nsh_index].widgets_ptr->label_car); n++;	
	sprintf (buf, "mem_hog%d", selections_pid);
	
						/* CR/MANAGED MEM TACHOMETER */
	new_save_hist[nsh_index].widgets_ptr->mem_hog = 
			XtCreateManagedWidget(buf, ztachWidgetClass, 
			 new_save_hist[nsh_index].widgets_ptr->forme, args, n);
	/* no monkey around */
	XtUninstallTranslations( new_save_hist[nsh_index].widgets_ptr->mem_hog);


	/* setup task memory size */           	      /* SET UP MEMORY LABELS */ 
	n = 0;
	XtSetArg (args[n], XtNwidth, &largeur); n++;
	XtSetArg (args[n], XtNlabel, &label); n++;
	XtGetValues (prototype_task_mem, args, n);
	n = 0;
	XtSetArg (args[n], XtNwidth, largeur); n++;
	XtSetArg (args[n], XtNlabel, label); n++;
	XtSetArg (args[n], XtNfromVert, 
			new_save_hist[nsh_index].widgets_ptr->label_car); n++;
	XtSetArg (args[n], XtNfromHoriz, 
			new_save_hist[nsh_index].widgets_ptr->mem_hog); n++;	
	sprintf (buf, "task_mem%d", selections_pid);

						/* CR/MANAGED TASK MEM LABEL */
	new_save_hist[nsh_index].widgets_ptr->task_mem = 
		XtCreateManagedWidget (buf, labelWidgetClass, 
		          new_save_hist[nsh_index].widgets_ptr->forme, args, n);

	/* now do free mem */     
	n = 0;
	XtSetArg (args[n], XtNwidth, &largeur); n++;
	XtSetArg (args[n], XtNlabel, &label); n++;
	XtGetValues (prototype_free_mem, args, n);
	n = 0;
	XtSetArg (args[n], XtNwidth, largeur); n++;
	XtSetArg (args[n], XtNlabel, label); n++;
	XtSetArg (args[n], XtNfromVert, 
			new_save_hist[nsh_index].widgets_ptr->task_mem); n++;
	XtSetArg (args[n], XtNfromHoriz, 
			new_save_hist[nsh_index].widgets_ptr->mem_hog); n++;	
	sprintf (buf, "free_mem%d", selections_pid);

						/* CR/MANAGED FREE MEM LABEL */
	new_save_hist[nsh_index].widgets_ptr->free_mem = 
		XtCreateManagedWidget (buf, labelWidgetClass, 
		          new_save_hist[nsh_index].widgets_ptr->forme, args, n);


						/* SETUP QUIT BUTTON */
	/* Creation du widget quit */
	sprintf (buf, "quit%d", selections_pid);

	/* lecture des valeurs dans le prototype */
	n = 0;
	XtSetArg (args[n], XtNlabel, &label); n++;
	XtSetArg (args[n], XtNwidth, &largeur); n++;
	XtSetArg (args[n], XtNheight, &hauteur); n++;
	XtSetArg (args[n], XtNvertDistance, &distance_vertical); n++;		
	XtGetValues (prototype_bouton_quit, args, n);

	/* Fixe les valeurs */
	n = 0;
	XtSetArg (args[n], XtNlabel, label); n++;
	XtSetArg (args[n], XtNwidth, largeur); n++;
	XtSetArg (args[n], XtNheight, hauteur); n++;
	XtSetArg (args[n], XtNfromVert, 
			     new_save_hist[nsh_index].widgets_ptr->free_mem); n++;
	XtSetArg (args[n], XtNvertDistance, distance_vertical); n++;
	XtSetArg (args[n], XtNfromHoriz, 
			new_save_hist[nsh_index].widgets_ptr->mem_hog); n++;	
	
						/* CR/MANAGED QUIT BUTTON */
	new_save_hist[nsh_index].widgets_ptr->quit = 
		XtCreateManagedWidget (buf, commandWidgetClass, 
			new_save_hist[nsh_index].widgets_ptr->forme, args, n);
	
	/* callback du quit */				/* ADD QUIT CALLBACK */
	XtAddCallback(new_save_hist[nsh_index].widgets_ptr->quit, XtNcallback, 
		Quit_definition_processus, 
		    (caddr_t)new_save_hist[nsh_index].widgets_ptr->popup_shell);
	
	/* Affichage */					/* FLAG ITS UP THERE */
	new_save_hist[nsh_index].widgets_ptr->flag_shell_actif = TRUE;
    }

    /* affiche le popup shell */			/* PUT IT UP THERE */
    XtPopup (new_save_hist[nsh_index].widgets_ptr->popup_shell, XtGrabNone);
}



/****************************************************************************
*
*			CREATE INFORMATION FOR PROCESS LIST
*			
******************************************************************************/
/*
 * Mise a jour de la liste des processus
 * Cette fonction est appelee si un processus espionne est tue ou bien
 * si l'utilisateur demande une nouvelle liste des processus
 */
 
 /*            This one needed some major wHacking too */
 
void mise_a_jour_liste_processus ()
{
	struct ps_proc *current_proc;

    register int i, j, nb, n;
    Arg args[10];
    	long	in_mem, hog_factor;
    	char	*cmd_line;

    /* interdit la mise a jour ! */	
    flag_mise_a_jour_autorisee = FALSE;

    /* On determine tous les processus existants */
    nb = 0;
    current_proc = ph->head;
    while (current_proc != NULL)	           /* HERE IS REAL MAIN LOOP */
    {
	   /* We can go with either the entire command line here or just 
	      the cmd itself, I am not sure which was in u_comm, but it
	      had a size of DIRSIZE for sco, so let's try the whole 9 yards */
			       
	        if (strlen(current_proc->cmdline) > 0)
	      		cmd_line = current_proc->cmdline; 
	 	else 
	      		cmd_line = current_proc->cmd;       /* the short one */
	      	if (strlen(cmd_line) > 20 )
	      		cmd_line[20] = '\0';
	      	
	      		
	      	/* TRY THIS ON FOR SIZE */
	      	in_mem = 4096000. * (float)current_proc->rss / z_info.main_mem;
		     /* get the hog factor from the history */
		i = 0;
		while(i < MAXPROC)
		       {
		       	if (new_save_hist[i].pid == current_proc->pid)
		       	       {	
		       		hog_factor = new_save_hist[i].pcpu;
		       		break;
		       	       }		
		       	i++;
		       }	
	    if (short_mode)
	       {		       	       			
	      	sprintf(buf, "%5d %5d %s %3d:%02d %2d.%dM%% %2d.%dC%% %-8s %s",
	      		current_proc->pid,
	      		    current_proc->ppid,
	      		    	proc_state(current_proc),
                  ((current_proc->stime + current_proc->utime) / CLK_TCK) / 60, 
	      	  ((current_proc->stime + current_proc->utime) / CLK_TCK) % 60,
				    in_mem / 10,
				    in_mem % 10,	
				    	hog_factor / 10,
				    	hog_factor % 10,       	  
						current_proc->user,
	                         		    cmd_line);
	       }
	    else
	       {                     		    
		sprintf(buf, 
 "%5d %5d %s %3dP %3dN %4dM %4dR %4dS %3d:%02d %2d.%dM%% %2d.%dC%% %s %-8s %s",
			current_proc->pid,
		            current_proc->ppid,
				proc_state(current_proc),
                                    2 * PZERO - current_proc->counter,
  				    PZERO - current_proc->priority,
                  		    current_proc->vsize/1024,
                  		    current_proc->rss * 4,
                  		    current_proc->statm.share << 2,
                  ((current_proc->stime + current_proc->utime) / CLK_TCK) / 60, 
	      	  ((current_proc->stime + current_proc->utime) / CLK_TCK) % 60,
				    	in_mem / 10,
				    	in_mem % 10,	
				    	    hog_factor / 10,
				    	    hog_factor % 10,
				    	    	current_proc->ttyc,       	  
						    current_proc->user,
	                         		    	cmd_line);
	       }           
	      					
#ifdef DEBUG
printf ("processus %s\n", buf);
#endif
	    if (table_processus[nb] != NULL)
	      free (table_processus[nb]);
	    if ((table_processus[nb] = 
	    			(String) calloc (1, 1 + strlen (buf))) == NULL)
	       {
		perror ("mise_a_jour_liste_processus-calloc");
		exit (1);
	       }
	    strcpy (table_processus[nb], buf);

	    /*       
	        	See if there is a POP-UP active for this process
	        	If there is, copy the widget pointers for it
	    */
	    /*
	    		None of this code is necessary anymore, as I
	    		no longer use the aribitary index of the process
	    		in this list to either point to the widgets
	    		associated with the process or the statistics
	    		assiciated with the process.  They are now indexed
	    		off the process id (imagine that!).  
	    		
	    		In Short, the only thing this function needs to
	    		do is just generate the list!
	    */		
 	nb++;
     	current_proc = current_proc->next;	
    }
			/* End of main loop */
			
    /* Mise a jour du nombre de processus courant */
    nb_processus = nb;

    /* Modification du widget liste */
    n = 0;
    XtSetArg (args[n], XtNnumberStrings, nb_processus); n++;
    XtSetValues (liste_processus, args, n);

    /* la mise a jour est de nouveau autorisee */
    flag_mise_a_jour_autorisee = TRUE;
}



/****************************************************************************
*
* 	         THE MAIN PROCESS ROUTINE, CALLED FROM BUTTON
*			
******************************************************************************/

/*
 * Lecture et affichage de la liste des processus
 * Cette fonction cree et affiche la liste des processus, elle lance de plus
 * la mise a jour periodique des caracteristiques des processus espionnes
 */

void Processus(w, client_data, call_data)
Widget w;
caddr_t client_data, call_data;
{
    int i, n;
    Arg args[10];

#ifdef DEBUG
    printf ("Processus...\n");
#endif

    /* Creation de la liste des processus */
    nb_processus = 0;
    process_running = TRUE; 
    if (active_running == FALSE && there_are_popups == FALSE)
    	mesure_charge_courante_systeme();		/* prime the pump */
    
    n = 0;
    XtSetArg(args[n], XtNdefaultColumns, &list_columns); n++;
    XtGetValues(liste_processus, args, n); 
                                                            
    mise_a_jour_liste_processus ();                 /* MAKE THE LIST */


    /* Modification du widget liste */
    n = 0;
    XtSetArg (args[n], XtNnumberStrings, nb_processus); n++;
    XtSetValues (liste_processus, args, n);

    /* affichage */
    XtPopup (shell_processus, XtGrabNone);

    /* Initialise le comptage */
    flag_mise_a_jour_autorisee = TRUE;
    /* This is no longer necessary because do_stats does all the work */
    /* including killing pop-ups and freeing their associated memory  */
/* XtAddTimeOut ((unsigned long)3000, mise_a_jour_processus, (caddr_t)NULL); */
}


/************************************************************************
*
*			support routines
*
****************************************************************************/

char *proc_state(proc)
struct 	ps_proc	*proc;
{
	static char ss[4] = "   ";
    
	ss[0] = proc->state;
    	ss[1] = ( (proc->rss == 0 && proc->state != 'Z') ? 'W' : ' ');
        ss[2] = (proc->priority > PZERO ? '<' : 
        	(proc->priority < PZERO ? 'N' : ' '));	
        return(ss);
}