/**
 * Scout - The Amiga System Monitor
 *
 *------------------------------------------------------------------
 *
 * This program 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 of the License, or
 * any later version.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * You must not use this source code to gain profit of any kind!
 *
 *------------------------------------------------------------------
 *
 * @author Andreas Gelhausen
 * @author Richard Körber <rkoerber@gmx.de>
 */



#include "scout_tasks.h"

extern struct ExecBase  *SysBase;

int  taskcnt;
APTR taskmoretext0,taskmoretext1a,taskmoretext1b,taskmoretext2,taskmoretext3;
APTR procmorelist;
APTR processmoretext0,processmoretext1a,processmoretext1b,processmoretext2,processmoretext3;
APTR processmoretext4,processmoretext5,processmoretext6a,processmoretext6b,processmoretext7;
struct Remember *TaskRememberKey;

struct SignalSemaphore *patchsem = NULL;
VOID __asm (*cleartaskdata) (VOID) = NULL;
LONG  __asm (*gettaskdata) (register __d0 struct Task *) = NULL;
struct Task * __asm (*getaddedtask) (VOID) = NULL;
char  *switchstate = NULL;

LONG totalmicros,cpuseconds = 1,cpumicros = 0;

#define CHEATSTACKSIZE  1024
#define CHEATTASKNAME   "« Scout's cheat task »"
struct Task *cheattask = NULL;

static void cheattask_func (void) {
   while (TRUE) {}
}

struct Task * AddCheatTask (void) {
   struct Task *result;

   if (result = AllocMem (CHEATSTACKSIZE + sizeof (struct Task), MEMF_ANY|MEMF_CLEAR)) {
      result->tc_SPLower = sizeof (struct Task) + (char *) result;
      result->tc_SPUpper = CHEATSTACKSIZE + sizeof (struct Task) + (char *) result;
      result->tc_SPReg = result->tc_SPUpper;

      result->tc_Node.ln_Name = CHEATTASKNAME;
      result->tc_Node.ln_Pri = -128;
      result->tc_Node.ln_Type = NT_TASK;

      AddTask (result, (APTR) &cheattask_func, NULL);
   }
   return (result);
}

void RemoveCheatTask (void) {
   RemTask (cheattask);
   FreeMem (cheattask, CHEATSTACKSIZE + sizeof (struct Task));
   cheattask = NULL;
}

struct Task * TaskExists (struct Task *tasktofind) {
   struct   Task  *task, *result = NULL;

   if ((tasktofind == (struct Task *) 42) || (tasktofind == myprocess))
      return (myprocess);

   Forbid();

   task = FIRSTTASKREADY;
   while (task->tc_Node.ln_Succ != 0) {
      if (task == tasktofind)
         result = task;
      task = (struct Task *) task->tc_Node.ln_Succ;
   }

   task = FIRSTTASKWAIT;
   while (task->tc_Node.ln_Succ != 0) {
      if (task == tasktofind)
         result = task;
      task = (struct Task *) task->tc_Node.ln_Succ;
   }

   Permit();
   return (result);
}

void HandleTimerRequest (BOOL waitstate) {
   struct Window *taskwin;

   if (! waitstate) {
      AbortIO ((struct IORequest *) &TimerIORequest);
   }

   if ((patchsem) && (*switchstate)) {
      if (((waitstate) && (TimerIORequest.tr_node.io_Message.mn_Node.ln_Type != NT_MESSAGE)) \
         || (! waitstate)) {
         (*cleartaskdata) ();

         if (WI_Tasks) {
            TimerIORequest.tr_time.tv_secs = cpuseconds;
            TimerIORequest.tr_time.tv_micro = cpumicros;
            TimerIORequest.tr_node.io_Command = TR_ADDREQUEST;
            SendIO ((struct IORequest *) &TimerIORequest);

            get (WI_Tasks, MUIA_Window_Window, &taskwin);
            if (taskwin)
               UpdateTasks();
         }
      }
   }
}

void CheckCPUUsage (void) {
   ULONG    size;

   get (CY_CpuUsage,MUIA_Cycle_Active,&updatetimestate);

   if (updatetimestate) {
      Forbid();
      if (patchsem = FindSemaphore ((UBYTE *) &SemaphoreName)) {
         CodeAddress = sizeof (struct SignalSemaphore) + 8 + (char *) patchsem;
         Permit();
      } else {
         Permit();
         size = (ULONG) ((char *) &CodeAddress - (char *) &MySwitch + 8);

         if (AsmTimerBase = (struct Library *) FindName (&SysBase->DeviceList, TIMER_DEVNAME)) {
            if (patchsem = AllocMem (sizeof (struct SignalSemaphore) + 8 + size, MEMF_ANY|MEMF_CLEAR)) {
               Forbid();
               FreeMem (patchsem, sizeof (struct SignalSemaphore) + 8 + size);
               patchsem = AllocMem (sizeof (struct SignalSemaphore) + 8 + size, MEMF_ANY|MEMF_CLEAR|MEMF_REVERSE);
               Permit();

               if (patchsem) {
                  CodeAddress = sizeof (struct SignalSemaphore) + 8 + (char *) patchsem;
                  OldSwitch = *((ULONG *) (-52 + (char *) SysBase));
                  OldAdd = *((ULONG *) (-280 + (char *) SysBase));

                  CopyMem (&MySwitch, (APTR) CodeAddress, size);

                  patchsem->ss_Link.ln_Name = (char *) &SemaphoreName - (char *) &MySwitch + CodeAddress;
                  patchsem->ss_Link.ln_Type = NT_SIGNALSEM;
                  AddSemaphore (patchsem);

//                  cleartaskdata = (VOID (* __asm) (VOID)) ((char *) &ClearTaskData - (char *) &MySwitch + CodeAddress);
//                  gettaskdata = (LONG (* __asm)(register __d0 struct Task *)) ((char *) &GetTaskData - (char *) &MySwitch + CodeAddress);
//                  getaddedtask = (struct Task *(* __asm)(VOID)) ((char *) &GetAddedTask - (char *) &MySwitch + CodeAddress);

                  SetPatches();
                  ShowTasks();
               }
            }
         } else {
            aprintf ("Couldn't find %ls!\n", TIMER_DEVNAME);
         }
      }
   }

        if (patchsem) {
      cleartaskdata = (VOID (* __asm) (VOID)) ((char *) &ClearTaskData - (char *) &MySwitch + CodeAddress);
      gettaskdata = (LONG (* __asm)(register __d0 struct Task *)) ((char *) &GetTaskData - (char *) &MySwitch + CodeAddress);
      getaddedtask = (struct Task *(* __asm)(VOID)) ((char *) &GetAddedTask - (char *) &MySwitch + CodeAddress);
      switchstate = (char *) &SwitchState - (char *) &MySwitch + CodeAddress;

           *switchstate = (char) updatetimestate;
           HandleTimerRequest (FALSE);

      if (updatetimestate == 2) {
         if (! cheattask)
            cheattask = AddCheatTask();
      } else {
         if (cheattask)
            RemoveCheatTask();
         set(cpucount,MUIA_Gauge_Current,0);
         set(cpucount,MUIA_Gauge_InfoText,"\0330-----");
//*         MySetContents (cpucount, ESC "c-----");
      }
        }
}

char * GetTaskState (UBYTE state) {
   char *TaskStateText[] = {
      "frozen","added","run","ready","wait","except","removed",
      "invalid","< ??? >"
   };

   UBYTE TaskState[] = {
      TS_FROZEN,TS_ADDED,TS_RUN,TS_READY,TS_WAIT,TS_EXCEPT,TS_REMOVED,
      TS_INVALID
   };

   int i = 0;

   while (TaskState[i]) {
      if (state == TaskState[i]) {
         return (TaskStateText[i]);
      }
      i++;
   }
   if (TaskState[i] == TS_INVALID) {
      return (TaskStateText[i]);
   } else {
      return (TaskStateText[i+1]);
   }
}

char * GetNodeType (UBYTE type) {
   char *NodeTypeText[] = {
      "task","interrupt","device","msgport",
      "message","freemsg","replymsg","resource","library",
      "memory","softint","font","process","semaphore",
      "signalsem","bootnode","kickmem","graphics",
      "deathmessage","user","extended",
      "unknown","< ??? >"
   };

   UBYTE NodeType[] = {
      NT_TASK,NT_INTERRUPT,NT_DEVICE,NT_MSGPORT,
      NT_MESSAGE,NT_FREEMSG,NT_REPLYMSG,NT_RESOURCE,
      NT_LIBRARY,NT_MEMORY,NT_SOFTINT,NT_FONT,NT_PROCESS,
      NT_SEMAPHORE,NT_SIGNALSEM,NT_BOOTNODE,NT_KICKMEM,
      NT_GRAPHICS,NT_DEATHMESSAGE,NT_USER,NT_EXTENDED,
      NT_UNKNOWN
   };

   int i = 0;

   while (NodeType[i]) {
      if (type == NodeType[i]) {
         return (NodeTypeText[i]);
      }
      i++;
   }
   if (NodeType[i] == NT_UNKNOWN)
      return (NodeTypeText[i]);
   else
      return (NodeTypeText[i+1]);
}

__asm LONG tasklist_dspfunc(register __a2 char **array, register __a1 struct TaskEntry *taskentry, register __a0 struct Hook *hook) {
   if (taskentry) {
      *array++ = taskentry->ts_address;
      *array++ = taskentry->ts_type;
      *array++ = taskentry->ts_pri;
      *array++ = taskentry->ts_num;
      *array++ = taskentry->ts_state;
      *array++ = taskentry->ts_sigwait;
      *array++ = taskentry->ts_cpu;
      *array++ = taskentry->ts_name;
      *array   = NULL;
   } else {
      *array++ = ESC "bAddress";
      *array++ = ESC "bln_Type";
      *array++ = ESC "bln_Pri";
      *array++ = ESC "bNUM";
      *array++ = ESC "bState";
      *array++ = ESC "bSigWait";
      *array++ = ESC "bCPU %";
      *array++ = ESC "bln_Name";
      *array++ = NULL;
   }
   return (0);
}

struct Hook tasklist_dsphook = {
 {NULL, NULL},
 (ULONG (* )())tasklist_dspfunc,
 NULL, NULL
};

void FreeTasks (void) {
   MyFreeStructs (&TaskRememberKey, tasktext, tasklist);
}

char * GetTaskName (struct Task *task) {
   struct CommandLineInterface   *cli;
   int   lauf = 0;
//   int   offset = 0;
   char  *pointer;
   char  *error = "<notask>";

   if (task) {
      if ((task->tc_Node.ln_Type == NT_PROCESS) && (((struct Process *) task)->pr_CLI != 0)) {
         cli = (struct CommandLineInterface *) (((struct Process *) task)->pr_CLI << 2);
         if ((pointer = (char *) cli->cli_CommandName) &&
             (pointer = (char *) (((LONG) pointer) << 2) + 1) &&
             (pointer[0])) {

            while (pointer[lauf]) {
               if (pointer[lauf++] != ' ')
                  return (pointer);
            }
/*
            pointer = (char *) (((LONG) pointer) << 2);

            while (*(pointer + lauf) != '\0') {
               if ((*(pointer + lauf) == '/') || ((*(pointer + lauf) == ':') && (*(pointer + lauf + 1) != '\0'))) {
                  offset = lauf;
               }
               lauf++;
            }
            if (lauf)
               return (pointer + 1 + offset);
*/
         }
      }
      if (((task->tc_Node.ln_Type == NT_PROCESS) || (task->tc_Node.ln_Type == NT_TASK)) \
          && (task->tc_Node.ln_Succ)) {
         return (task->tc_Node.ln_Name);
      } else {
         return (error);
      }
   }
   return (NULL);
}

void GetTaskNumber (struct Task *task, char string[]) {
   if ((task->tc_Node.ln_Type == NT_TASK) || (((struct Process *)task)->pr_TaskNum == 0)){
      strcpy (string, "---");
   } else {
      sprintf (string, "%ld", ((struct Process *) task)->pr_TaskNum);
   }
}

void GetTaskEntry (struct Task *task, struct TaskEntry *taskentry, BOOL cpuflag) {
   char  tasknum[4];
   ULONG  cpu,zehntel;

   if (! taskentry->ts_ptr) {
      taskentry->ts_ptr = task;

      sprintf (taskentry->ts_address, "$%08lx", task);
      strcpy (taskentry->ts_type, GetNodeType (task->tc_Node.ln_Type));
      GetTaskNumber (task, tasknum);
      strncpy (taskentry->ts_num, tasknum, 3);
   }

   sprintf (taskentry->ts_pri, "%4ld ", task->tc_Node.ln_Pri);
   strcpy (taskentry->ts_state, GetTaskState ((UBYTE) task->tc_State));
   sprintf (taskentry->ts_sigwait, "$%08lx", task->tc_SigWait);
   strncpy (taskentry->ts_name, nonetest (GetTaskName (task)), NODENAMELENGTH);
   healstring (taskentry->ts_name);

   if ((cpuflag) && (totalmicros = *((ULONG *) ((char *) &TotalMicros1 + 4 - (char *) &MySwitch + CodeAddress)))) {
      cpu = (*gettaskdata)(task);

      if (!(zehntel = UDivMod32 (totalmicros, 1000)))
         zehntel = 1;

      zehntel = UDivMod32 (cpu, zehntel);
      cpu = UDivMod32 (zehntel, 10);
      zehntel -= UMult32 (cpu, 10);
                if (cpu > 99) {
                        cpu = 100;
                        zehntel = 0;
                }
      sprintf (taskentry->ts_cpu, "%ld.%ld", cpu, zehntel);
   } else {
      strcpy (taskentry->ts_cpu, "0.0");
   }
}

void UpdateTasks (void) {
   struct   TaskEntry   *taskentry,*cmptaskentry;
   struct   Task        *task;
   int      i;
   ULONG    cheatcpu;

   while (task = (*getaddedtask) ()) {
      if (TaskExists (task) && (taskentry = AllocRemember (&TaskRememberKey, sizeof (struct TaskEntry), MEMF_ANY|MEMF_CLEAR))) {
         GetTaskEntry (task, taskentry, TRUE);
         InsertSortedEntry (tasklist, (APTR *) &taskentry);
         SetCountText (taskcount, ++taskcnt);
      }
   }

   if (cmptaskentry = AllocMem (sizeof (struct TaskEntry), MEMF_ANY|MEMF_CLEAR)) {
      for (i=0;;i++) {
         DoMethod (tasklist,MUIM_List_GetEntry,i,&taskentry);

         if (taskentry) {
            if (task = TaskExists ((struct Task *) taskentry->ts_ptr)) {
               CopyMem (taskentry, cmptaskentry, sizeof (struct TaskEntry));
               GetTaskEntry (task, taskentry, TRUE);
               if (memcmp (taskentry,cmptaskentry, sizeof (struct TaskEntry))) {
                  DoMethod (tasklist,MUIM_List_Redraw,i);
               }
            } else {
               if (! (task = TaskExists ((struct Task *) taskentry->ts_ptr))) {
                  DoMethod (tasklist, MUIM_List_Remove, i--);
                  SetCountText (taskcount, --taskcnt);
               }
            }
         } else {
            break;
         }
      }
      FreeMem (cmptaskentry, sizeof (struct TaskEntry));
   }

   if (updatetimestate == 2) {
      cheatcpu = (*gettaskdata)(cheattask);
      if (totalmicros) {
         set(cpucount,MUIA_Gauge_InfoText,"%ld %%");
         set(cpucount,MUIA_Gauge_Current,UDivMod32 (UMult32 (totalmicros - cheatcpu, 100), totalmicros));
//*         MySetContents (cpucount, ESC "r%ld \%", UDivMod32 (UMult32 (totalmicros - cheatcpu, 100), totalmicros));
      }
   }
}

int GetTasks (struct TaskEntry **first) {
   #define maxtasknumber 512

   struct   Task        *task;
   struct   TaskEntry   *taskentry,*previous = NULL;
   unsigned long        *buffer,maxcnt,loopcnt;

   int taskcnt = 0;
   *first = 0;

   if (clientstate) {
      if (SendDaemon ("GetTaskList")) {
         while ((taskentry = AllocRemember (&TaskRememberKey, sizeof (struct TaskEntry), MEMF_ANY|MEMF_CLEAR)) \
           && (ReceiveDecodedEntry ((UBYTE *) taskentry, sizeof (struct TaskEntry)))) {
            IsHex (taskentry->ts_address, (long *) &taskentry->ts_ptr);

            if (! *first)
               *first = taskentry;
            if (previous)
               previous->ts_next = taskentry;

            taskcnt++;
            previous = taskentry;
         }
      }
   } else {
      loopcnt = 0;
      maxcnt = maxtasknumber;
   
      if (buffer = AllocMem ((maxtasknumber+1)<<2, MEMF_ANY|MEMF_CLEAR)) {
         Forbid();
   
         task = FIRSTTASKREADY;
         while ((task->tc_Node.ln_Succ != 0) && (maxcnt)) {
            buffer[taskcnt++] = (ULONG) task;
            maxcnt--;
            task = (struct Task *)task->tc_Node.ln_Succ;
         }
   
         task = FIRSTTASKWAIT;
         while ((task->tc_Node.ln_Succ != 0) && (maxcnt)) {
            buffer[taskcnt++] = (ULONG) task;
            maxcnt--;
            task = (struct Task *)task->tc_Node.ln_Succ;
         }
   
         Permit();
         while ((buffer[loopcnt]) && (taskentry = AllocRemember (&TaskRememberKey, sizeof (struct TaskEntry), MEMF_ANY|MEMF_CLEAR))) {
            if (! *first)
               *first = taskentry;
            if (previous)
               previous->ts_next = taskentry;
   
            GetTaskEntry ((struct Task *) buffer[loopcnt++], taskentry, FALSE);
            previous = taskentry;
         }
         FreeMem (buffer, (maxtasknumber+1)<<2);
   
         if (taskentry = AllocRemember (&TaskRememberKey, sizeof (struct TaskEntry), MEMF_ANY|MEMF_CLEAR)) {
            if (previous)
               previous->ts_next = taskentry;
   
            GetTaskEntry (myprocess, taskentry, FALSE);
            taskentry->ts_ptr = (struct Task *) 42;  // 42, damit Scouts Prozess oben in der Liste steht!
            taskcnt++;
            previous = taskentry;
         }
      }
   }
   return (taskcnt);
}

void PrintTasks (char *filename) {
   int   i=1;
   BPTR  handle;
   struct TaskEntry *entryp;

   handle = HandlePrintStart (filename);
   if ((handle) && (PrintOneLine (handle, "\n  Address  Type     Pri NUM State    SigWait  Name\n\n"))) {
      if (! WI_Tasks) {
         i = GetTasks (&entryp);
      }
      if (i) {
         for (i=0;;i++) {
            if (WI_Tasks)
               DoMethod (tasklist,MUIM_List_GetEntry,i,&entryp);
            if (!entryp) break;

            sprintf (tmpstr2, " %ls %-7.7ls %4ls%3ls %-7.7ls %ls %ls\n", entryp->ts_address, entryp->ts_type, entryp->ts_pri, entryp->ts_num, entryp->ts_state, entryp->ts_sigwait, entryp->ts_name);
            if (! (PrintOneLine (handle, tmpstr2)))
               break;

            if (! WI_Tasks)
               entryp = entryp->ts_next;
         }
      }
   }
   HandlePrintStop();
}

void ShowTasks (void) {
   #define maxtasknumber 512

   struct   TaskEntry   *task;

   ApplicationSleep();
   set(tasklist,MUIA_List_Quiet,TRUE);
   set(tasklist,MUIA_List_CompareHook,tasklist_cmphook_ptr);
   set(BT_TaskRemove, MUIA_Disabled, TRUE);
   set(BT_TaskFreeze, MUIA_Disabled, TRUE);
   set(BT_TaskActivate, MUIA_Disabled, TRUE);
   set(BT_TaskPriority, MUIA_Disabled, TRUE);
   set(BT_TaskSignal, MUIA_Disabled, TRUE);
   set(BT_TaskBreak, MUIA_Disabled, TRUE);
   set(BT_TaskMore, MUIA_Disabled, TRUE);

   if (clientstate) {
      set(CY_CpuUsage, MUIA_Disabled, TRUE);
   }
   FreeTasks();

   taskcnt = GetTasks (&task);

   while (task) {
      InsertSortedEntry (tasklist, (APTR *) &task);
      task = task->ts_next;
   }

   SetCountText (taskcount, taskcnt);
   AwakeApplication();
   set(tasklist,MUIA_List_Quiet,FALSE);
}

void SendTaskList (void) {
   struct   TaskEntry   *task;

   FreeTasks();
   GetTasks (&task);

   while (task) {
      SendEncodedEntry ((UBYTE *) task, sizeof (struct TaskEntry));
      task = task->ts_next;
   }
   FreeTasks();
}

void GetTaskMore (struct Task *task) {
   unsigned char     *title = "TASK: ";
   struct   WinFree  *ptr;

   if (ptr = AllocWinFree()) {
      ptr->wf_Window = (APTR) WindowObject,

//      MUIA_Window_SizeGadget, FALSE,
      MUIA_HelpNode, TasksText,
      MUIA_Window_ID, MakeDetailID('.','T','A','S'),
      WindowContents, HGroup,
         Child, VGroup, MUIA_Group_SameWidth, TRUE,
            Child, MyLabel2 ("Name:"),
            Child, MyLabel2 ("Address:\nIDNestCnt:\nTrapAlloc:\nTrapAble:\nTrapData:\nTrapCode:\nUserData:"),
            Child, MyLabel2 ("Flags:"),
         End,
         Child, VGroup, MUIA_Group_SameWidth, TRUE,
            Child, taskmoretext0 = MyTextObject(),
            Child, HGroup,
               Child, VGroup,
                  Child, taskmoretext1a = MyTextObject2(),
                  Child, taskmoretext1b = KeyButtonF ('b', task->tc_Flags),
               End,
               Child, MyLabel ("State:\nTDNestCnt:\nExceptData:\nExceptCode:\nStackSize:\nSPLower:\nSPUpper:\nSPReg:"),
               Child, taskmoretext2 = MyTextObject2(),
               Child, MyLabel ("Pri:\nSigAlloc:\nSigWait:\nSigRecvd:\nSigExcept:\nSwitch():\nLaunch():\nMemEntry:"),
               Child, taskmoretext3 = MyTextObject2(),
            End,
         End,
      End, End;

      if (ptr->wf_Window) {
         MySetContents (taskmoretext1a, ESC "r $%08x\n%d\n$%04x\n$%04x\n $%08x\n $%08x\n $%08x", task, task->tc_IDNestCnt, task->tc_TrapAlloc, task->tc_TrapAble, task->tc_TrapData, task->tc_TrapCode, task->tc_UserData);
         MySetContents (taskmoretext2, ESC "c%s\n" ESC "r%d\n$%08x\n$%08x\n%d\n$%08x\n$%08x\n$%08x", GetTaskState ((BYTE) task->tc_State), task->tc_TDNestCnt, task->tc_ExceptData, task->tc_ExceptCode, (char *) task->tc_SPUpper - (char *) task->tc_SPLower, task->tc_SPLower, task->tc_SPUpper, task->tc_SPReg);
         MySetContents (taskmoretext3, ESC "r%d\n$%08x\n$%08x\n$%08x\n$%08x\n$%08x\n$%08x\n$%08x", task->tc_Node.ln_Pri, task->tc_SigAlloc, task->tc_SigWait, task->tc_SigRecvd, task->tc_SigExcept, task->tc_Switch, task->tc_Launch, task->tc_MemEntry);

         HandleFlagsButtonPressed (taskmoretext1b, ptr, "(TASK)", "tc_Flags", task->tc_Flags, (struct LongFlag *) &tc_flags, NULL, 'b');
         HandleWindowOpen (ptr, title, GetTaskName (task));
         MySetContents (taskmoretext0, "%s", GetTaskName (task));
         HandleWindowClose (ptr);
      }
   }
}

__asm LONG procmore_dspfunc(register __a2 char **array, register __a1 struct HunkEntry *hunkentry, register __a0 struct Hook *hook) {
   if (hunkentry) {
      *array++ = hunkentry->he_lower;
      *array++ = hunkentry->he_upper;
      *array++ = hunkentry->he_size;
      *array   = NULL;
   } else {
      *array++ = ESC "bLower";
      *array++ = ESC "bUpper";
      *array++ = ESC "bSize";
      *array++ = NULL;
   }
   return (0);
}

struct Hook procmore_dsphook = {
 {NULL, NULL},
 (ULONG (* )())procmore_dspfunc,
 NULL, NULL
};

void GetSegList (struct Process *proc, struct Remember **remkey) {
   struct HunkEntry  *hunkentry;
   long  *seg;
   long  size;

   set(procmorelist,MUIA_List_Quiet,TRUE);

   if (((seg = (long *) (((long) *((long *) (((long) proc->pr_SegList<<2) + 12)))<<2)) && points2ram((APTR) seg)) || \
      ((proc->pr_CLI) && (seg = (long *) (((struct CommandLineInterface *) (proc->pr_CLI<<2))->cli_Module<<2)))) {

      while ((seg) && (hunkentry = AllocRemember (remkey, sizeof (struct HunkEntry), MEMF_ANY|MEMF_CLEAR))) {
         sprintf (hunkentry->he_lower, "$%08lx", ((char *) seg) + 4);
         size = *(seg - 1);
         sprintf (hunkentry->he_size, "%ld", size);
         sprintf (hunkentry->he_upper, "$%08lx", ((char *) seg) - 4 + size);

         InsertBottomEntry (procmorelist, (APTR *) &hunkentry);
         seg = (long *) ((*seg)<<2);
      }
   }

   set(procmorelist,MUIA_List_Quiet,FALSE);
}

void GetProcessMore (struct Process *proc) {
   unsigned char     *title = "PROCESS: ";
   unsigned char     tasknum[4];
   struct   WinFree  *ptr;

   if (ptr = AllocWinFree()) {
      ptr->wf_Window = (APTR) WindowObject,

//      MUIA_Window_SizeGadget, FALSE,
      MUIA_HelpNode, TasksText,
      MUIA_Window_ID, MakeDetailID('.','T','A','S'),
      WindowContents, HGroup,
         Child, VGroup, MUIA_Group_SameWidth, TRUE,
#ifndef WINDOW_PROBLEMS
            Child, MyLabel2 ("Name:"),
#endif
            Child, MyLabel2 ("Address:\nIDNestCnt:\nTrapAlloc:\nTrapAble:\nTrapData:\nTrapCode:\nUserData:"),
            Child, MyLabel2 ("Flags:"),
            Child, MyLabel2 ("Pad:\nGlobVec:\nResult2:\nCOS:\nCLI:\nWindowPtr:"),
            Child, MyLabel2 ("\nHunks:\n\n\n\n"),
            Child, MyVSpace(0),
         End,
         Child, VGroup, MUIA_Group_SameWidth, TRUE,
#ifndef WINDOW_PROBLEMS
            Child, processmoretext0 = MyTextObject(),
#endif
            Child, HGroup,
               Child, VGroup,
                  Child, HGroup,
                     Child, VGroup, MUIA_Group_SameWidth, TRUE, MUIA_Weight, 0,
                        Child, processmoretext1a = MyTextObject(),
                        Child, processmoretext1b = KeyButtonF ('b', ((struct Task *) proc)->tc_Flags),
                        Child, processmoretext4 = MyTextObject(),
                     End,
//                     Child, MyHSpace(0),
                     Child, VGroup, MUIA_Group_SameWidth, TRUE, MUIA_Weight, 60,
                        Child, MyLabel ("State:\nTDNestCnt:\nExceptData:\nExceptCode:\nStackSize:\nSPLower:\nSPUpper:\nSPReg:"),
                        Child, MyVSpace(0),
                        Child, MyLabel ("SegList:\nTaskNum:\nCurrentDir:\nConsoleTask:\nReturnAddr:\nHomeDir:"),
                     End,
                     Child, VGroup, MUIA_Group_SameWidth, TRUE,
                        Child, processmoretext2 = MyTextObject2(),
                        Child, MyVSpace(0),
                        Child, processmoretext5 = MyTextObject2(),
                     End,
                  End,
                  Child, MyVSpace(2),
                  Child, procmorelist = ListviewObject,
                     MUIA_Listview_DoubleClick, TRUE,
                     MUIA_Listview_Input, FALSE,
                     MUIA_Listview_List, ListObject,
                     MUIA_List_Format, "COL=0 DELTA=8,COL=1 DELTA=8,COL=2 P=\33r",
                     MUIA_List_Title, TRUE,
                     MUIA_List_DisplayHook, &procmore_dsphook,
                     ReadListFrame,
                  End, End,
               End,
//               Child, MyHSpace(0),
               Child, VGroup, MUIA_Group_SameWidth, TRUE, MUIA_Weight, 60,
                  Child, MyLabel ("Pri:\nSigAlloc:\nSigWait:\nSigRecvd:\nSigExcept:\nSwitch():\nLaunch():\nMemEntry:"),
                  Child, MyLabel ("Flags:"),
                  Child, MyLabel ("StackSize:\nStackBase:\nCIS:\nFileSystemTask:\nPktWait:"),
                  Child, MyLabel ("ExitCode:\nExitData:\nArguments:\nLocalVars:\nShellPrivate:\nCES:"),
               End,
               Child, VGroup, MUIA_Group_SameWidth, TRUE,
                  Child, processmoretext3 = MyTextObject2(),
                  Child, processmoretext6b = KeyButtonF ('l', proc->pr_Flags),
                  Child, processmoretext6a = MyTextObject2(),
                  Child, processmoretext7 = MyTextObject2(),
               End,
            End,
         End,
      End, End;

      if (ptr->wf_Window) {
         MySetContents (processmoretext1a, ESC "r$%08x\n%d\n$%04x\n$%04x\n$%08x\n$%08x\n$%08x", proc, proc->pr_Task.tc_IDNestCnt, proc->pr_Task.tc_TrapAlloc, proc->pr_Task.tc_TrapAble, proc->pr_Task.tc_TrapData, proc->pr_Task.tc_TrapCode, proc->pr_Task.tc_UserData);
         MySetContents (processmoretext2, ESC "c%s\n" ESC "r%d\n$%08x\n$%08x\n%d\n$%08x\n$%08x\n$%08x", GetTaskState ((BYTE) proc->pr_Task.tc_State), proc->pr_Task.tc_TDNestCnt, proc->pr_Task.tc_ExceptData, proc->pr_Task.tc_ExceptCode, (char *) proc->pr_Task.tc_SPUpper - (char *) proc->pr_Task.tc_SPLower, proc->pr_Task.tc_SPLower, proc->pr_Task.tc_SPUpper, proc->pr_Task.tc_SPReg);
         MySetContents (processmoretext3, ESC "r%d\n$%08x\n$%08x\n$%08x\n$%08x\n$%08x\n$%08x\n$%08x", proc->pr_Task.tc_Node.ln_Pri, proc->pr_Task.tc_SigAlloc, proc->pr_Task.tc_SigWait, proc->pr_Task.tc_SigRecvd, proc->pr_Task.tc_SigExcept, proc->pr_Task.tc_Switch, proc->pr_Task.tc_Launch, proc->pr_Task.tc_MemEntry);
         MySetContents (processmoretext4, ESC "r$%04x\n$%08x\n$%08x\n$%08x\n$%08x\n$%08x", proc->pr_Pad, proc->pr_GlobVec, proc->pr_Result2, ((ULONG) proc->pr_COS)<<2, ((ULONG) proc->pr_CLI)<<2, proc->pr_WindowPtr);

         GetTaskNumber ((struct Task *) proc, tasknum);

         MySetContents (processmoretext5, ESC "r$%08x\n%s\n$%08x\n$%08x\n$%08x\n$%08x", ((ULONG) proc->pr_SegList)<<2, tasknum, ((ULONG) proc->pr_CurrentDir)<<2, proc->pr_ConsoleTask, proc->pr_ReturnAddr, ((ULONG) proc->pr_HomeDir)<<2);
         MySetContents (processmoretext6a, ESC "r%d\n$%08x\n$%08x\n$%08x\n$%08x", proc->pr_StackSize, ((ULONG) proc->pr_StackBase)<<2, ((ULONG) proc->pr_CIS)<<2, proc->pr_FileSystemTask, proc->pr_PktWait);

         MySetContents (processmoretext7, ESC "r$%08x\n$%08x\n$%08x\n$%08x\n$%08x\n$%08x", proc->pr_ExitCode, proc->pr_ExitData, proc->pr_Arguments, proc->pr_LocalVars, proc->pr_ShellPrivate, ((ULONG) proc->pr_CES)<<2);

         GetSegList (proc, &ptr->wf_RememberKey);

         HandleFlagsButtonPressed (processmoretext1b, ptr, "(PROCESS)", "tc_Flags", ((struct Task *) proc)->tc_Flags, (struct LongFlag *) &tc_flags, NULL, 'b');
         HandleFlagsButtonPressed (processmoretext6b, ptr, "(PROCESS)", "pr_Flags", proc->pr_Flags, (struct LongFlag *) &pr_flags, NULL, 'l');
         HandleWindowOpen (ptr, title, GetTaskName ((struct Task *) proc));
         set (ptr->wf_Window,MUIA_Window_ActiveObject,procmorelist);
#ifndef WINDOW_PROBLEMS
         MySetContents (processmoretext0, "%s", GetTaskName ((struct Task *) proc));
#endif
         HandleWindowClose (ptr);
      }
   }
}

