/*
 *  WIDENTIFY   A companion utility to wIconSetter and wIconify that allows
 *              you to determine the commands necessary to properly identify
 *              a window or screen in the wIconSetter initialization file.
 *
 *  wIdentify.c     main code for wIdentify.
 *
 *  Copyright 1990 by Davide P. Cervone, all rights reserved.
 *  You may use this code, provided this copyright notice is kept intact.
 */

#include "wIdentify.h"
#include <devices/input.h>

static char *program   = PROGRAM;
static char *copyright = COPYRIGHT;


struct MsgPort *IdPort;         /* Port for messages in when NEW is specified */
static char *WindowName;        /* Name of window to identify */
static char *ScreenName;        /* Name of screen holding window */

/*  Storage for SetFunction return values */

long OldOpenWindow;
long OldSetWindowTitles;
long OldOpenScreen;

#define TOUPPER(c)          (((c)>='a'&&(c)<='z')?(c)-'a'+'A':c)
#define MATCH(n1,n2)        (PrefixMatch(n1,n2) == 0)


#define ARGMATCH(s)     (stricmp(argv[0],s) == 0)
#define FUNCTION(s)     (ARGMATCH(s) && function == JUST_EXIT)
#define NAME(s)\
   (ARGMATCH(s) && (function == JUST_EXIT || function == IDENTIFY_ONE))

#define PARAMETER(f,a,s)\
{\
   if (argc >= 2)\
   {\
      if (function == JUST_EXIT) function = f;\
      a = argv[1];\
      argc--; argv++;\
   } else {\
      printf(s);\
      function = SHOW_USAGE;\
   }\
}


/*
 *  DoExit()
 *
 *  General purpose error-exit routine.  Print an error message if one was
 *  supplied (it can have up to three parameters), and then clean up any
 *  memory, libraries, etc. that need to be handled before exiting.
 */

void DoExit(s,x1,x2,x3)
char *s, *x1, *x2, *x3;
{
   long status = EXIT_OK;
   
   if (s != NULL)
   {
      printf(s,x1,x2,x3);
      printf("\n");
      status = EXIT_ERROR;
   }
   if (IdPort)          DeletePort(IdPort);
   if (IntuitionBase)   CloseLibrary(IntuitionBase);
   exit(status);
}


/*
 *  CheckLibOpen()
 *
 *  Call OpenLibrary() for the specified library, and check that the 
 *  open succeeded.
 */

static void CheckLibOpen(lib,name,rev)
APTR *lib;
char *name;
int rev;
{
   extern APTR OpenLibrary();

   if ((*lib = OpenLibrary(name,(LONG)rev)) == NULL)
      DoExit("Can't open %s",name);
}


/*
 *  SetVectors()
 *
 *  Set the Intuition library vectors for the routines we are trapping
 *  to the routines specified by the handler.  Save the old routine pointers
 *  for later replacement.
 */

static void SetVectors()
{
   OldOpenWindow = SetFunction(IntuitionBase,&LVOOpenWindow,&aOpenWindow);
   OldSetWindowTitles =
      SetFunction(IntuitionBase,&LVOSetWindowTitles,&aSetWindowTitles);
   OldOpenScreen = SetFunction(IntuitionBase,&LVOOpenScreen,&aOpenScreen);
}


/*
 *  UnSetVectors()
 *
 *  Replace the old Intuition library vectors, but make sure that no one
 *  else has changed them behind our back.  If they are not the same as
 *  what we set them to originally, then put back the ones that we found,
 *  and return an error status.
 */

static int UnSetVectors()
{
   long NewOpenWindow;
   long NewSetWindowTitles;
   long NewOpenScreen;
   int status = FALSE;
 
   NewOpenWindow = SetFunction(IntuitionBase,&LVOOpenWindow,OldOpenWindow);
   NewSetWindowTitles =
      SetFunction(IntuitionBase,&LVOSetWindowTitles,OldSetWindowTitles);
   NewOpenScreen = SetFunction(IntuitionBase,&LVOOpenScreen,OldOpenScreen);
   if (NewOpenWindow      != (long) &aOpenWindow ||
       NewSetWindowTitles != (long) &aSetWindowTitles ||
       NewOpenScreen      != (long) &aOpenScreen)
   {
      SetFunction(IntuitionBase,&LVOOpenWindow,NewOpenWindow);
      SetFunction(IntuitionBase,&LVOSetWindowTitles,NewSetWindowTitles);
      SetFunction(IntuitionBase,&LVOOpenScreen,NewOpenScreen);
      status = TRUE;
   }
   return(status);
}


/*
 *  ParseArguments()
 *
 *  Initialize the wIdentify function to just exiting.
 *  If we were started from the WB, just exit.
 *  If we did not have any parameters, identify active window.
 *  While there are more arguments to consider,
 *    Get the next argument
 *    If it is NEW or ALL, set the function appropriately.
 *    If it is WINDOW, get the window name or show an error.
 *    If it is SCREEN, get the screen name or show an error.
 *    Otherwise show un-recognized parameter error and show usage.
 *  Return the function to perform.
 */

static int ParseArguments(argc,argv)
int argc;
char **argv;
{
   int function = JUST_EXIT;
   
   if (argc == 0) return(JUST_EXIT);
   if (argc == 1) return(IDENTIFY_ONE);
   while (--argc > 0)
   {
      argv++;
      if (FUNCTION("NEW"))      function = IDENTIFY_NEW;
      else if (FUNCTION("ALL")) function = IDENTIFY_ALL;
      else if (NAME("WINDOW"))
         PARAMETER(IDENTIFY_ONE,WindowName,"Window name required\n")
      else if (NAME("SCREEN"))
         PARAMETER(IDENTIFY_ONE,ScreenName,"Screen name required\n")
      else
      {
         printf("Unrecognized parameter '%s'\n",*argv);
         function = SHOW_USAGE;
      }
   }
   return(function);
}

/*
 *  PrefixMatch()
 *
 *  Case-insensitive prefix match.  NULL pointers and empty strings
 *  only match themselves, and are not considered prefixes to any string.
 *
 *  Return <0 if the first is smaller than the second,
 *         =0 if the first is a prefix of the second,
 *         >1 if the first is larger than the second.
 */

static int PrefixMatch(s1,s2)
char *s1,*s2;
{
   int match = -1;

   if (s1)
   {
      if (s2)
      {
         while (TOUPPER(*s1) == TOUPPER(*s2) && *s2 != 0) s1++,s2++;
         if (*s1 == 0) match = 0; else match = *s1 - *s2;
      } else match = 1;
   } else if (s2 == NULL) match = 0;
   return(match);
}


/*
 *  *FindScreen()
 *
 *  Lock IntuitionBase so nothing happens while we look.
 *  If there is a name to look for,
 *    Start at the first screen, and look for a screen whose title
 *      matches the given name.  If none, return NULL.
 *  Otherwise, use the active screen.
 *  Unlock Intuition.
 *  Return the screen pointer found, if any.
 */

static struct Screen *FindScreen(ScreenName)
char *ScreenName;
{
   struct Screen *theScreen;
   long ILock, LockIBase();

   ILock = LockIBase(0L);
   if (ScreenName)
   {
      theScreen = IntuitionBase->FirstScreen;
      while (theScreen && PrefixMatch(ScreenName,theScreen->Title))
         theScreen = theScreen->NextScreen;
   } else {
      theScreen = IntuitionBase->ActiveScreen;
   }
   UnlockIBase(ILock);
   return(theScreen);
}


/*
 *  *FindWindow()
 *
 *  Lock Intuition so nothing happens while we look.
 *  If there is a window name given,
 *    Start with the first window on the screen and look through
 *      the window list for one that matches the given name.
 *      Return NULL if none found.
 *  Otherwise, use the active window.
 *  Unlock Intuition.
 *  Return the window pointer, if any.
 */

static struct Window *FindWindow(WindowName,theScreen)
char *WindowName;
struct Screen *theScreen;
{
   struct Window *theWindow;
   long ILock, LockIBase();

   ILock = LockIBase(0L);
   if (WindowName)
   {
      theWindow = theScreen->FirstWindow;
      while (theWindow && PrefixMatch(WindowName,theWindow->Title))
         theWindow = theWindow->NextWindow;
   } else {
      theWindow = IntuitionBase->ActiveWindow;
   }
   UnlockIBase(ILock);
   return(theWindow);
}


/*
 *  DoMessage()
 *
 *  Get the message's screen and window pointers.
 *  If the Message has a program, print its name, otherwise print '[Unknown]'
 *  If the screen has a title, print it, otherwise print '[NULL]'
 *  If this is not a screen-open message
 *    if the window has a title, print it, otherwise print '[NULL]'
 */

static void DoMessage(theMessage)
struct IdMessage *theMessage;
{
   struct Window *theWindow = theMessage->Window;
   struct Screen *theScreen = theMessage->Screen;

   if (theMessage->Program)
      printf("\nProgram: '%s'\n",theMessage->Program);
     else
      printf("\nProgram: [Unknown]\n");

   if (theScreen->DefaultTitle)
      printf("  Screen: '%s'\n",theScreen->DefaultTitle);
     else
      printf("  Screen: [NULL]\n");

   if (theWindow != SCREENICON)
   {
      if (theWindow->Title)
         printf("    Window: '%s'\n",theWindow->Title);
        else
         printf("    Window: [NULL]\n");
   }
}


/*
 *  IdentifyWindow()
 *
 *  Get ther port of the window
 *  If there is a port, and it has a task to signal,
 *    Get the task's name and print it, otherwise print '[ANY]'
 *  If the window's screen has a title, print it, otherwise print '[NULL]'
 *  If the window itself has a title, print it, otherwise print '[NULL]'
 */

static void IdentifyWindow(theWindow)
struct Window *theWindow;
{
   struct MsgPort *thePort;
   char Program[MAXNAME];
   
   thePort = theWindow->UserPort;
   if (thePort && thePort->mp_SigTask != NULL &&
      (thePort->mp_Flags & PF_ACTION) == PA_SIGNAL)
          GetProgramName(Program,thePort->mp_SigTask),
          printf("Program: '%s'\n",Program);
         else
          printf("Program: [ANY]\n");

   if (theWindow->WScreen->DefaultTitle)
      printf("  Screen: '%s'\n",theWindow->WScreen->DefaultTitle);
     else
      printf("  Screen: [NULL]\n");
 
   if (theWindow->Title)
      printf("    Window: '%s'\n",theWindow->Title);
     else
      printf("    Window: [NULL]\n");
}


/*
 *  IdentifyScreen()
 *
 *  Get the screen's first window
 *  While there are still windows to consider
 *    Print a blank line then the window's name, etc.
 *    Go on to the next window
 */

static void IdentifyScreen(theScreen)
struct Screen *theScreen;
{
   struct Window *theWindow;
   
   Forbid();
   theWindow = theScreen->FirstWindow;
   while (theWindow)
   {
      printf("\n");
      IdentifyWindow(theWindow);
      theWindow = theWindow->NextWindow;
   }
   Permit();
}


/*
 *  IdentifyNew()
 *
 *  Create a port for messages from the handler
 *  Trap the intuition routines, and give a message that we are running
 *  Get the wait signals
 *  As long as we haven't bee told otherwise,
 *    Wait for a message or CTRL-C
 *    While ther are messages in the port,
 *      Do each message and reply to it
 *    If we were signalled to stop,
 *      Try to remove the trap routines
 *      If unsuccessful, print a message, otherwise, end the loop
 *  Indicate that wIdentify is finished.
 */

static void IdentifyNew()
{
   ULONG Signals;
   ULONG WaitSignals;
   int NotDone = TRUE;
   struct IdMessage *theMessage;

   IdPort = CreatePort(0,0);
   if (IdPort == NULL) DoExit("Can't Create IdPort");

   SetVectors();
   printf("wIdentify Running...\n");
   WaitSignals = (ONE << IdPort->mp_SigBit) | SIGBREAKF_CTRL_C;

   while (NotDone)
   {
      Signals = Wait(WaitSignals);
      while (theMessage = GetMsg(IdPort))
      {
         DoMessage(theMessage);
         ReplyMsg(theMessage);
      }
      if (Signals & SIGBREAKF_CTRL_C)
      {
         NotDone = UnSetVectors();
         if (NotDone)
         {
            printf("[vectors have been changed - can't cancel]\n");
            printf("wIdentify Running...\n");
         }
      }
   }
   printf("\nwIdentify Done\n");
}


/*
 *  IdentifyOne()
 *
 *  Find the specified screen.
 *  If can't find it, error
 *  Otherwise,
 *    If a window was not specified, but a screen was, do the entire screen,
 *    Otherwise
 *      Find the specified window on the screen
 *      If found, identify it, otherwise error
 */

static void IdentifyOne()
{
   struct Window *theWindow;
   struct Screen *theScreen;
   
   Forbid();
   theScreen = FindScreen(ScreenName);
   if (theScreen == NULL)
   {
      printf("Can't find screen '%s'\n",ScreenName);
   } else {
      if (WindowName == NULL && ScreenName)
      {
         IdentifyScreen(theScreen);
         printf("\n");
      } else {
         theWindow = FindWindow(WindowName,theScreen);
         if (theWindow) IdentifyWindow(theWindow);
            else printf("Can't find window '%s' on screen '%s'\n",
                            WindowName,theScreen->DefaultTitle);
      }
   }
   Permit();
}


/*
 *  IdentifyAll()
 *
 *  Get the first Intuition screen
 *  While there are more screens
 *    Identify all windows on the screen and go to the next screen
 */

static void IdentifyAll()
{
   struct Screen *theScreen;
   
   Forbid();
   theScreen = IntuitionBase->FirstScreen;
   while (theScreen)
   {
      IdentifyScreen(theScreen);
      theScreen = theScreen->NextScreen;
   }
   Permit();
   printf("\n");
}


/*
 *  main()
 *
 *  Open the Intuition library
 *  Parse the argument list, and do the proper thing:
 *    Show usage, identify new windows, one window, or all windows
 *  Exit and clean up
 */

void main(argc,argv)
int argc;
char **argv;
{
   CheckLibOpen(&IntuitionBase,"intuition.library",INTUITION_REV);
   
   switch(ParseArguments(argc,argv))
   {
      case SHOW_USAGE:
         printf("Usage:  %s\n",USAGE);
         break;

      case IDENTIFY_NEW:
         IdentifyNew();
         break;

      case IDENTIFY_ONE:
         IdentifyOne();
         break;

      case IDENTIFY_ALL:
         IdentifyAll();
         break;
   }

   DoExit(NULL);
}
