/* 
 * OpenURL -- A Workbech/CLI frontend for "openurl.library".
 *
 * Written by Thomas Aglassinger <agi@sbox.tu-graz.ac.at>
 * Placed in the public domain.
 *
 * Based on material provided by Troels Walsted Hansen <troels@thule.no>
 */

#include <exec/memory.h>
#include <dos/dos.h>
#include <libraries/openurl.h>
#include <intuition/intuition.h>

#include <string.h>

#include "SmartReadArgs.h"

#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/intuition.h>
#include <proto/openurl.h>

static const char *version = "\0$VER: OpenURL 1.1 " __AMIGADATE__;

#define TEMPLATE "URL/A,NOSHOW/S,NOFRONT/S,NEWWIN/S,NOLAUNCH/S,PUBSCREEN/K,FILE/S"
enum
{
   A_URL, A_NOSHOW, A_NOFRONT, A_NEWWIN, A_NOLAUNCH, A_PUBSCREEN, A_FILE, A_MAX
};

/* Maximum length an URL can get according to w3c. Currently, CLI
 * imposes even a smaller limit of 512. */
#define MAXIMUM_URL_LENGTH 1024

struct Library *OpenURLBase;

#define OPENURL_VERSION      1  /* Minimum version of openurl.library */
#define OPENURL_VERSION_STR "1" /* Used by error message */

#define MAXIMUM_ERROR_LENGTH 120    /* Size of buffer for error messages */

/* Some shit necessary because C standard libraries suck */
ULONG ulong_max(ULONG a, ULONG b)
{
   if (a > b)
   {
      return a;
   }
   else
   {
      return b;
   }
}

/* Main function
 *
 * Note that the startup code of SAS/c sets argc=0 when started from
 * workbench. This fact is used to auto-enable FILE in such a case.
 */
int main(int argc, char *argv[])
{
   int return_code = RETURN_FAIL;

   /* Error code from dos.library's IoErr() */
   LONG error_code = 0;
   /* Description of the action failed. If error_code is 0, only this
    * text is displayed. Otherwise, also the DOS error message is
    * appended. */
   STRPTR error_cause = NULL;

   /* Workbench startup message or NULL if started from CLI */
   struct WBStartup *wb_startup = NULL;

   /* Buffer for error messages. This is not allocated dynamically because
    * then it might not exist in case of no memory. */
   UBYTE error_buffer[MAXIMUM_ERROR_LENGTH] =
   {NULL};

   if ((OpenURLBase = OpenLibrary("openurl.library", OPENURL_VERSION)))
   {
      struct SmartArgs smart_args =
      {NULL};
      LONG args[A_MAX] =
      {0};

      /* The URL passed to URL_Open() */
      STRPTR real_url = NULL;

      /* The fully qualified filename, if any */
      STRPTR filename = NULL;

      /* Prepare argument parsing */
      smart_args.sa_Template = TEMPLATE;
      smart_args.sa_Parameter = args;
      smart_args.sa_FileParameter = A_URL;
      smart_args.sa_Window = "CON:////OpenURL/AUTO/CLOSE/WAIT";

      return_code = RETURN_ERROR;

      /* Auto-enable FILE switch when started from WB */
      if (argc == 0)
      {
         wb_startup = (struct WBStartup *) argv;
         args[A_FILE] = 1;
      }

      /* Parse arguments, either from Workbench or CLI */
      error_code = SmartReadArgs(wb_startup, &smart_args);

      /* Allocate string buffers. This reduces stack usage and also
       * makes the string buffers being checked by Mungwall.
       *
       * Yes, this wastes some memory if FILE is not set, but makes the
       * error handling easier.
       *
       * Yes, we could use a 2K buffer with filename = real_url + 1024 to
       * use only one allocation. This however would reduce the possibilty
       * for out-of-bounds checks for the end of one/beginning of the
       * other string. */
      if (error_code == 0)
      {
         /* Allocate string buffers */
         real_url = AllocVec(MAXIMUM_URL_LENGTH, MEMF_ANY);
         filename = AllocVec(MAXIMUM_URL_LENGTH, MEMF_ANY);
         if ((real_url == NULL) || (filename == NULL))
         {
            /* Not enough memory */
            SetIoErr(ERROR_NO_FREE_STORE);
            error_code = IoErr();
         }
      }

      if (error_code == 0)
      {
         if (args[A_FILE])
         {
            /* Expand the filename to a fully qualified URL */
            BPTR lock = Lock((STRPTR) args[A_URL], ACCESS_READ);

            if (lock != NULL)
            {
               if (NameFromLock(lock, filename, MAXIMUM_URL_LENGTH))
               {
                  strcpy(real_url, "file://localhost/");
                  strncat(real_url, filename, MAXIMUM_URL_LENGTH);
               }
               else
               {
                  error_cause = "Error obtaining full filename";
                  error_code = IoErr();
               }
               UnLock(lock);
            }
            else
            {
               error_cause = "Error opening input file";
               error_code = IoErr();
            }
         }
         else
         {
            /* Simply use the URL passed in arguments, assuming it is
             * an already fully qualified URL of any protocol. Possible
             * errors are now treated by the browser. */
            strncpy(real_url, (STRPTR) args[A_URL], MAXIMUM_URL_LENGTH);
         }

         /* Make sure that the URL gets a trailing zero (just in case
          * someone passed a too long one). */
         real_url[MAXIMUM_URL_LENGTH - 1] = '\0';

         if (error_code == 0)
         {
            /* Really show it */
            if (URL_Open(real_url,
                         URL_Launch, !args[A_NOLAUNCH],
                         URL_Show, !args[A_NOSHOW],
                         URL_BringToFront, !args[A_NOFRONT],
                         URL_NewWindow, args[A_NEWWIN],
                         URL_PubScreenName, args[A_PUBSCREEN],
                         TAG_DONE))
            {
               return_code = RETURN_OK;
            }
            else
            {
               error_code = 0;
               if (args[A_NOLAUNCH])
               {
                  error_cause = "Could not find browser port";
               }
               else
               {
                  error_cause = "Could not launch browser";
               }
            }
         }
      }
      else
      {
         error_cause = "Error in arguments";
      }

      /* Free extended read args (even in case of error) */
      SmartFreeArgs(&smart_args);

      /* Release all other resources */
      if (filename != NULL)
      {
         FreeVec(filename);
      }
      if (real_url != NULL)
      {
         FreeVec(real_url);
      }

      CloseLibrary(OpenURLBase);
   }
   else
   {
      error_cause =
         "Could not find \"openurl.library\", "
         "version " OPENURL_VERSION_STR;
   }

   /* Create error message in error_buffer (if any) */
   if (error_code != 0)
   {
      Fault(error_code, error_cause, error_buffer, MAXIMUM_ERROR_LENGTH);
   }
   else if (error_cause != NULL)
   {
      strncpy(error_buffer, error_cause, MAXIMUM_ERROR_LENGTH - 1);
   }

   /* Display error message in CLI or Requester (if any) */
   if (error_buffer[0] != '\0')
   {
      if (wb_startup != NULL)
      {
         struct EasyStruct error_requester =
         {
            sizeof(struct EasyStruct),
            0,
            "OpenURL Error",
            "%s",
            "Cancel"
         };

         EasyRequest(NULL, &error_requester, NULL, error_buffer);
      }
      else
      {
         FPuts(Output(), error_buffer);
         FPuts(Output(), "\n");
      }
   }

   return (return_code);
}
