AmiTimeKeeper/arexx.c

543 lines
16 KiB
C
Raw Normal View History

2021-01-12 22:16:18 +00:00
/*-
* Copyright (c) 2020-2021 Carsten Sonne Larsen <cs@innolan.net>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
*
*/
#include "config.h"
#include "global.h"
#include "string.h"
#include "message.h"
#include "setting.h"
#include "text.h"
#include "sync.h"
#include "conv.h"
#include "mem.h"
#include "win.h"
#include "val.h"
#include "tz.h"
#include <proto/rexxsyslib.h>
#include <rexx/storage.h>
#include <rexx/rxslib.h>
#include "logmod.h"
#define MODULENAME "ARexx"
static char *arexxHelpText = NULL;
typedef void (*ArexxCommand)(struct RexxMsg *, char **);
struct ArexxCommandDef
{
const int Identifier;
const char *Name;
const char *Description;
ArexxCommand Command;
int Parameters;
};
struct ArexxParameterDef
{
const int Identifier;
const char *Keyword;
};
static void ArexxIdentifierCommand(struct RexxMsg *, char **);
static void ArexxVersionCommand(struct RexxMsg *, char **);
static void ArexxHelpCommand(struct RexxMsg *, char **);
static void ArexxStatusCommand(struct RexxMsg *, char **);
static void ArexxTimezoneCommand(struct RexxMsg *msg, char **);
static void ArexxLastSyncCommand(struct RexxMsg *msg, char **);
static void ArexxLastAdjustCommand(struct RexxMsg *msg, char **);
static void ArexxGetValueCommand(struct RexxMsg *, char **);
static void ArexxSetValueCommand(struct RexxMsg *, char **);
static void ArexxShowCommand(struct RexxMsg *, char **);
static void ArexxHideCommand(struct RexxMsg *, char **);
static void ArexxStartCommand(struct RexxMsg *, char **);
static void ArexxStopCommand(struct RexxMsg *, char **);
static void ArexxShutdownCommand(struct RexxMsg *, char **);
#define AREXX_COMMAND_OK 0
#define AREXX_COMMAND_ERROR 1
#define AREXX_COMMAND_UNKNOWN 10
#define AREXX_COMMAND_PARM_UNKNOWN 11
#define AREXX_COMMAND_PARM_INVALID 12
#define AREXX_COMMAND_SHUTDOWN 99
static struct ArexxCommandDef arexxCommands[] = {
{1, "ID", "Get application identifier", ArexxIdentifierCommand, 0},
{2, "VERSION", "Get application version number", ArexxVersionCommand},
{3, "HELP", "Get a list of ARexx commands and a short description", ArexxHelpCommand, 0},
{4, "STATUS", "Get current synchronization status", ArexxStatusCommand, 0},
{5, "TIMEZONE", "Get current time zone", ArexxTimezoneCommand, 0},
{6, "LASTSYNC", "Get time of last synchronization operation", ArexxLastSyncCommand, 0},
{7, "LASTADJ", "Get time of last clock adjustment", ArexxLastAdjustCommand, 0},
{8, "GET", "Get a runtime configuration value", ArexxGetValueCommand, 1},
{9, "SET", "Set a runtime configuration value", ArexxSetValueCommand, 2},
{10, "SHOW", "Show settings window", ArexxShowCommand, 0},
{11, "HIDE", "Hide settings window", ArexxHideCommand, 0},
{12, "START", "Start the time synchronization process", ArexxStartCommand, 0},
{13, "STOP", "Stop the time synchronization process", ArexxStopCommand, 0},
{14, "SHUTDOWN", "Shutdown " APP_SHORT_NAME, ArexxShutdownCommand, 0}};
#define AREXX_COMMAND_COUNT 14
#define AREXX_PARAMETER_UNKNOWN 0
#define AREXX_PARAMETER_SERVER 1
#define AREXX_PARAMETER_PORT 2
#define AREXX_PARAMETER_THRESHOLD 3
#define AREXX_PARAMETER_INTERVAL 4
#define AREXX_PARAMETER_PRIORITY 5
#define AREXX_PARAMETER_POPKEY 6
#define AREXX_PARAMETER_POPUP 7
#define AREXX_PARAMETER_READONLY 8
#define AREXX_PARAMETER_EXPERT 9
#define AREXX_PARAMETER_TIMEOUT 10
static struct ArexxParameterDef arexxParameters[] = {
{AREXX_PARAMETER_SERVER, KEYWORD_SERVER},
{AREXX_PARAMETER_PORT, KEYWORD_PORT},
{AREXX_PARAMETER_THRESHOLD, KEYWORD_THRESHOLD},
{AREXX_PARAMETER_INTERVAL, KEYWORD_INTERVAL},
{AREXX_PARAMETER_PRIORITY, KEYWORD_PRIORITY},
{AREXX_PARAMETER_POPKEY, KEYWORD_POPKEY},
{AREXX_PARAMETER_POPUP, KEYWORD_POPUP},
{AREXX_PARAMETER_READONLY, KEYWORD_READONLY},
{AREXX_PARAMETER_EXPERT, KEYWORD_EXPERT},
{AREXX_PARAMETER_TIMEOUT, KEYWORD_TIMEOUT}};
#define AREXX_PARAMETER_COUNT 10
#define AREXX_MAX_ARGS_COUNT 8
static void SplitAndTerminate(char *string, char **res)
{
char *p = string;
char **q = res;
int count = 1;
while (*p != '\0')
{
if (*p == ' ')
{
// Terminate
*p = '\0';
p++;
// Skip trailing spaces
while (*p == ' ')
p++;
*q = p;
q++;
count++;
// Bail out
if (count >= AREXX_MAX_ARGS_COUNT)
return;
}
else
{
p++;
}
}
}
static void BuildArexxHelpText(void)
{
char *p;
int i, line, len = 0;
if (arexxHelpText != NULL)
return;
len += 26; // Header
len += 71 * 2; // Spacer
for (i = 0; i < AREXX_COMMAND_COUNT; i++)
{
len += StrLen(arexxCommands[i].Description) + 14;
}
arexxHelpText = (char *)AllocStringSafe(len);
if (arexxHelpText == NULL)
return;
p = AppendText(arexxHelpText, " Command | Description\n");
p = AppendChar(p, '-', 70);
p = AppendChar(p, '\n', 1);
for (i = 0; i < AREXX_COMMAND_COUNT; i++)
{
line = 10 - StrLen(arexxCommands[i].Name);
p = AppendChar(p, ' ', 1);
p = AppendText(p, arexxCommands[i].Name);
p = AppendChar(p, ' ', line);
p = AppendChar(p, '|', 1);
p = AppendChar(p, ' ', 1);
p = AppendText(p, arexxCommands[i].Description);
p = AppendChar(p, '\n', 1);
}
p = AppendChar(p, '-', 70);
}
void CleanupArexx(void)
{
if (arexxHelpText != NULL)
{
FreeMemSafe(arexxHelpText);
arexxHelpText = NULL;
}
}
static void ArexxIdentifierCommand(struct RexxMsg *msg, char **parms)
{
msg->rm_Result2 = (LONG)CreateArgstring((STRPTR)APP_TITLE, (LONG)StrLen(APP_TITLE));
}
static void ArexxVersionCommand(struct RexxMsg *msg, char **parms)
{
msg->rm_Result2 = (LONG)CreateArgstring((STRPTR)APP_VERSION, (LONG)StrLen(APP_VERSION));
}
static void ArexxHelpCommand(struct RexxMsg *msg, char **parms)
{
BuildArexxHelpText();
msg->rm_Result2 = (LONG)CreateArgstring((STRPTR)arexxHelpText, (LONG)StrLen(arexxHelpText));
}
static void ArexxStatusCommand(struct RexxMsg *msg, char **parms)
{
if (SynchronizerRunning)
msg->rm_Result2 = (LONG)CreateArgstring((STRPTR)TextSyncOn, (LONG)StrLen(TextSyncOn));
else
msg->rm_Result2 = (LONG)CreateArgstring((STRPTR)TextSyncOff, (LONG)StrLen(TextSyncOff));
}
static void ArexxTimezoneCommand(struct RexxMsg *msg, char **parms)
{
char Timezone[TIMEZONE_TEXT_LEN];
GetTimezoneText(AppLocale, Timezone, UTC_NO_PARENS);
msg->rm_Result2 = (LONG)CreateArgstring((STRPTR)Timezone, (LONG)StrLen(Timezone));
}
static void ArexxLastSyncCommand(struct RexxMsg *msg, char **parms)
{
struct timeval tv = LastSync;
if (tv.tv_sec == 0 && tv.tv_micro == 0)
{
msg->rm_Result2 = (LONG)CreateArgstring((STRPTR)TextNoResponses, (LONG)StrLen(TextNoResponses));
}
else
{
char result[64];
Utc2Str(&tv, result);
msg->rm_Result2 = (LONG)CreateArgstring((STRPTR)result, (LONG)StrLen(result));
}
}
static void ArexxLastAdjustCommand(struct RexxMsg *msg, char **parms)
{
struct timeval tv = LastAdjust;
if (tv.tv_sec == 0 && tv.tv_micro == 0)
{
msg->rm_Result2 = (LONG)CreateArgstring((STRPTR)TextNoClockAdjust, (LONG)StrLen(TextNoClockAdjust));
}
else
{
char result[64];
Utc2Str(&tv, result);
msg->rm_Result2 = (LONG)CreateArgstring((STRPTR)result, (LONG)StrLen(result));
}
}
static int ArexxFindParameter(char **parms)
{
int i;
char *parm = parms[0];
if (parm == NULL || *parm == '\0')
{
return AREXX_PARAMETER_UNKNOWN;
}
for (i = 0; i < AREXX_PARAMETER_COUNT; i++)
{
if (Stricmp((STRPTR)parm, (STRPTR)arexxParameters[i].Keyword) == 0)
{
return arexxParameters[i].Identifier;
}
}
return AREXX_PARAMETER_UNKNOWN;
}
static void ArexxGetValueCommand(struct RexxMsg *msg, char **parms)
{
char buf[MAXLONGLONGCHARSIZE];
char *res;
int param = ArexxFindParameter(parms);
switch (param)
{
case AREXX_PARAMETER_UNKNOWN:
msg->rm_Result1 = AREXX_COMMAND_PARM_UNKNOWN;
break;
case AREXX_PARAMETER_SERVER:
res = Settings->DestinationAddress;
break;
case AREXX_PARAMETER_PORT:
res = Settings->DestinationPort;
break;
case AREXX_PARAMETER_THRESHOLD:
LongLongToStr(Settings->Threshold, buf);
res = buf;
break;
case AREXX_PARAMETER_INTERVAL:
LongToStr(Settings->Interval, buf);
res = buf;
break;
case AREXX_PARAMETER_PRIORITY:
LongToStr(Settings->Priority, buf);
res = buf;
break;
case AREXX_PARAMETER_POPKEY:
res = Settings->PopKey;
break;
case AREXX_PARAMETER_POPUP:
BoolToStr(Settings->Popup, buf);
res = buf;
break;
case AREXX_PARAMETER_READONLY:
BoolToStr(Settings->Readonly, buf);
res = buf;
break;
case AREXX_PARAMETER_EXPERT:
BoolToStr(Settings->Expert, buf);
res = buf;
break;
case AREXX_PARAMETER_TIMEOUT:
LongToStr(Settings->Timeout, buf);
res = buf;
break;
default:
msg->rm_Result1 = AREXX_COMMAND_PARM_UNKNOWN;
break;
}
if (msg->rm_Result1 != AREXX_COMMAND_PARM_UNKNOWN)
{
msg->rm_Result2 = (LONG)CreateArgstring((STRPTR)res, (LONG)StrLen(res));
}
}
static void ArexxSetValueCommand(struct RexxMsg *msg, char **parms)
{
int param = ArexxFindParameter(parms++);
char *valueString = *parms;
long long longlongValue;
long longValue;
bool boolValue;
switch (param)
{
case AREXX_PARAMETER_UNKNOWN:
msg->rm_Result1 = AREXX_COMMAND_PARM_UNKNOWN;
break;
case AREXX_PARAMETER_SERVER:
SetServer(valueString, APPLY_VALUE);
break;
case AREXX_PARAMETER_PORT:
SetPort(valueString, APPLY_VALUE);
break;
case AREXX_PARAMETER_THRESHOLD:
if (TryParseLongLong(valueString, &longlongValue))
SetThreshold(valueString, APPLY_VALUE);
else
msg->rm_Result1 = AREXX_COMMAND_PARM_INVALID;
break;
case AREXX_PARAMETER_INTERVAL:
if (TryParseLong(valueString, &longValue))
SetInterval(longValue, APPLY_VALUE);
else
msg->rm_Result1 = AREXX_COMMAND_PARM_INVALID;
break;
case AREXX_PARAMETER_PRIORITY:
if (TryParseLong(valueString, &longValue))
SetPriority(valueString, APPLY_VALUE);
else
msg->rm_Result1 = AREXX_COMMAND_PARM_INVALID;
break;
case AREXX_PARAMETER_POPKEY:
// TODO: Remove CX filter object and add new filter (Settings->PopKey)
// http://amigadev.elowar.com/read/ADCD_2.1/Libraries_Manual_guide/node0587.html
break;
case AREXX_PARAMETER_POPUP:
if (TryParseBoolean(valueString, &boolValue))
Settings->Popup = boolValue;
else
msg->rm_Result1 = AREXX_COMMAND_PARM_INVALID;
break;
case AREXX_PARAMETER_READONLY:
if (TryParseBoolean(valueString, &boolValue))
Settings->Readonly = boolValue;
else
msg->rm_Result1 = AREXX_COMMAND_PARM_INVALID;
break;
case AREXX_PARAMETER_EXPERT:
if (TryParseBoolean(valueString, &boolValue))
{
Settings->Expert = boolValue;
SendWindowMessage(ATK_SHUTDOWN);
ShowSettingWindow();
}
else
msg->rm_Result1 = AREXX_COMMAND_PARM_INVALID;
break;
case AREXX_PARAMETER_TIMEOUT:
if (TryParseLong(valueString, &longValue))
SetTimeout(longValue, APPLY_VALUE);
else
msg->rm_Result1 = AREXX_COMMAND_PARM_INVALID;
break;
default:
msg->rm_Result1 = AREXX_COMMAND_PARM_UNKNOWN;
break;
}
}
static void ArexxShowCommand(struct RexxMsg *msg, char **parms)
{
ShowSettingWindow();
}
static void ArexxHideCommand(struct RexxMsg *msg, char **parms)
{
SendWindowMessage(ATK_SHUTDOWN);
}
static void ArexxStartCommand(struct RexxMsg *msg, char **parms)
{
if (!SynchronizerRunning)
{
Activate();
}
else
{
// TODO: Set error text
msg->rm_Result1 = AREXX_COMMAND_ERROR;
}
}
static void ArexxStopCommand(struct RexxMsg *msg, char **parms)
{
if (SynchronizerRunning)
{
Deactivate();
}
else
{
// TODO: Set error text
msg->rm_Result1 = AREXX_COMMAND_ERROR;
}
}
static void ArexxShutdownCommand(struct RexxMsg *msg, char **parms)
{
LogWarn("Requesting shutdown");
msg->rm_Result1 = AREXX_COMMAND_SHUTDOWN;
}
static bool HandleArexxMessage(struct RexxMsg *msg)
{
int i;
bool found = false;
bool cont = true;
msg->rm_Result1 = AREXX_COMMAND_UNKNOWN;
msg->rm_Result2 = 0;
for (i = 0; i < AREXX_COMMAND_COUNT; i++)
{
if (arexxCommands[i].Parameters == 0 && Stricmp((STRPTR)msg->rm_Args[0], (STRPTR)arexxCommands[i].Name) == 0)
{
LogNotice("Received %s", arexxCommands[i].Name);
msg->rm_Result1 = AREXX_COMMAND_OK;
arexxCommands[i].Command(msg, NULL);
found = true;
break;
}
}
if (!found)
{
char *args[AREXX_MAX_ARGS_COUNT];
char *buf = AllocStringSafe(StrLen(msg->rm_Args[0]) + 1);
StrCopy(buf, msg->rm_Args[0]);
for (i = 1; i < AREXX_MAX_ARGS_COUNT; i++)
{
args[i] = NULL;
}
SplitAndTerminate(buf, args);
for (i = 0; i < AREXX_COMMAND_COUNT; i++)
{
if (arexxCommands[i].Parameters > 0 && Stricmp((STRPTR)buf, (STRPTR)arexxCommands[i].Name) == 0)
{
LogNotice("Received %s", arexxCommands[i].Name);
msg->rm_Result1 = AREXX_COMMAND_OK;
arexxCommands[i].Command(msg, args);
found = true;
break;
}
}
FreeMemSafe(buf);
}
if (msg->rm_Result1 == AREXX_COMMAND_SHUTDOWN)
{
msg->rm_Result1 = AREXX_COMMAND_OK;
cont = false;
}
return cont;
}
bool HandleARexxMessages(void)
{
bool cont = true;
struct RexxMsg *msg;
while ((msg = (struct RexxMsg *)GetNewMessage(MSGPORT_AREXX)))
{
cont &= HandleArexxMessage(msg);
ReplyMsg((struct Message *)msg);
}
return cont;
}