-- File: WebServerTool.mesa - last edit:
-- AOF 25-Apr-96 10:43:16
-- DLion 2-Mar-96 23:10:57
-- WebServerTool.mesa
-- Create by FormSWLayoutTool on 22-Oct-95 22:20
-- Copyright (C) 1995, 1996 by Freier's Garage. All rights reserved.
DIRECTORY
Exec USING [
AddCommand, EndOfCommandLine, ExecProc, FreeTokenString, GetToken,
Handle, Outcome, OutputProc, RemoveCommand],
Format USING[LongDecimal, StringProc],
FormSW USING[
AllocateItemDescriptor, ClientItemsProcType, CommandItem, Enumerated,
EnumeratedItem, EnumeratedNotifyProcType, line0, line1, line3, line4,
line6, line7, LongNumberItem, ProcType, StringItem, TagOnlyItem],
Heap USING[Create, GetAttributes, Delete],
Process USING[Detach, priorityNormal, SetPriority],
Put USING[Text],
String USING [AppendChar, LowerCase],
Tool USING[
Create, Destroy, MakeFileSW, MakeFormSW, MakeMsgSW, MakeSWsProc,
UnusedLogName],
ToolWindow USING[TransitionProcType],
WebCache USING[serverCache],
WebLog USING[MailLogFile],
WebOps USING[StartServer, StopServer],
WebTool USING[Data, DataHandle, FormItems, LevelTypes, ServerisTypes],
Window USING[Handle];
WebServerTool: MONITOR
IMPORTS
Exec, Format, FormSW, Heap, Process, Put, String, Tool,
WebCache, WebLog, WebOps
EXPORTS WebTool =
BEGIN
wh: Window.Handle ¬ NIL;
data: PUBLIC --WebTool-- WebTool.DataHandle ¬ NIL;
zone: PUBLIC --WebTool-- UNCOUNTED ZONE ¬ Heap.Create[initial: 4];
busyBit: BOOLEAN ¬ FALSE;
Busy: ENTRY PROCEDURE RETURNS [isBusy: BOOLEAN] =
BEGIN
ENABLE UNWIND => NULL;
isBusy ¬ busyBit;
busyBit ¬ TRUE;
END;
Done: ENTRY PROCEDURE =
BEGIN
ENABLE UNWIND => NULL;
busyBit ¬ FALSE;
END;
Msg: PUBLIC --WebTool-- Format.StringProc =
{Put.Text[data.msgSW, s]};
Write: PUBLIC --WebTool-- Format.StringProc =
{Put.Text[data.fileSW, s]};
PruneServerCache: FormSW.ProcType =
BEGIN
ENABLE ABORTED => {Done[]; CONTINUE};
IF Busy[] THEN {Msg["Tool is busy.\n"L]; RETURN};
Process.Detach[FORK PruneServerCacheInternal[]];
END;
PruneServerCacheInternal: PROCEDURE =
BEGIN
ENABLE ABORTED => {Done[]; CONTINUE};
Process.SetPriority[Process.priorityNormal];
Msg["\nPruning server cache ... "L];
WebCache.serverCache.prune[WebCache.serverCache];
Msg["done\n"L];
Done[];
END;
EnumerateServerCache: FormSW.ProcType =
BEGIN
ENABLE ABORTED => {Done[]; CONTINUE};
IF Busy[] THEN {Msg["Tool is busy.\n"L]; RETURN};
Process.Detach[FORK EnumerateServerCacheInternal[]];
END;
EnumerateServerCacheInternal: PROCEDURE =
BEGIN
ENABLE ABORTED => {Done[]; CONTINUE};
Process.SetPriority[Process.priorityNormal];
Write["EnumerateServerCache called\n"L];
Done[];
END;
Sendlogto: FormSW.ProcType =
BEGIN
ENABLE ABORTED => {Done[]; CONTINUE};
IF Busy[] THEN {Msg["Tool is busy.\n"L]; RETURN};
Process.Detach[FORK SendlogtoInternal[]];
END;
SendlogtoInternal: PROCEDURE =
BEGIN
ENABLE ABORTED => {Done[]; CONTINUE};
Process.SetPriority[Process.priorityNormal];
WebLog.MailLogFile[];
Done[];
END;
ServerTransition: FormSW.EnumeratedNotifyProcType =
BEGIN
ENABLE ABORTED => CONTINUE;
SELECT oldValue FROM
WebTool.ServerisTypes[inactive].ORD => WebOps.StartServer[];
WebTool.ServerisTypes[active].ORD => WebOps.StopServer[];
ENDCASE;
END;
ClientTransition: ToolWindow.TransitionProcType = {
SELECT TRUE FROM
old = inactive =>
IF data = NIL THEN
BEGIN
data ¬ zone.NEW[WebTool.Data ¬ []];
WebOps.StartServer[];
END;
new = inactive =>
BEGIN
IF data # NIL THEN
BEGIN
IF (data.serveris = active) THEN
WebOps.StopServer[];
zone.FREE[@data];
END;
END;
ENDCASE;
};
Init: PROCEDURE =
BEGIN
Exec.AddCommand["WebServer.~"L, ExecCommand, Help, Unload];
wh ¬ Tool.Create[
makeSWsProc: MakeSWs, initialState: default,
clientTransition: ClientTransition, name: "WebServer"L,
tinyName1: "Web"L, tinyName2: "Server"L, cmSection: "WebServer"L];
END; --Init--
Help: Exec.ExecProc =
BEGIN
out: Format.StringProc ¬ Exec.OutputProc[h];
out["WebServer.~ [/switch[:value]]\n"L];
out[" ::= | | \n"L];
out[" ::= ""Server""\n"L];
out[" shows start time and connections served\n"L];
out[" ::= ""Cache"" ( | '/ )\n"L];
out[" with no switches shows current cache value\n"L];
out[" ::= ""prune"" | ""enum"" | \n"L];
out[" ""prune"" causes the cache to be purged of older entries\n"L];
out[" ""enum"" will list the names and sizes of the items in cache\n"L];
out[" ::= ""max"" ': \n"L];
out[" ::= {decimal number}\n"L];
out[" sets the maximum kilobytes of information to be cached\n"L];
END; --Help--
ExecCommand: Exec.ExecProc =
BEGIN
ENABLE ABORTED => {Done[]; CONTINUE};
cmd, switch: LONG STRING ¬ NIL;
out: Format.StringProc ¬ Exec.OutputProc[h];
IF Busy[] THEN
BEGIN
out["Busy - try later\n"L];
outcome ¬ abort;
END
ELSE
BEGIN
ENABLE UNWIND =>
BEGIN
IF (cmd # NIL) THEN cmd ¬ Exec.FreeTokenString[cmd];
IF (switch # NIL) THEN switch ¬ Exec.FreeTokenString[switch];
END;
WHILE (~Exec.EndOfCommandLine[h]) DO
[cmd, switch] ¬ Exec.GetToken[h];
SELECT TRUE FROM
(cmd = NIL) => NULL;
Match[cmd, "server"L] => outcome ¬ ServerCommand[switch, out];
Match[cmd, "cache"L] => outcome ¬ CacheCommand[switch, out];
Match[cmd, "logging"L] => outcome ¬ LoggingCommand[switch, out];
ENDCASE =>
BEGIN
out["Command '"L];
out[cmd];
out["' was not recognized\n"L];
outcome ¬ abort;
END;
IF (cmd # NIL) THEN cmd ¬ Exec.FreeTokenString[cmd];
IF (switch # NIL) THEN switch ¬ Exec.FreeTokenString[switch];
ENDLOOP;
END;
Done[];
END; --ExecCommand--
ServerCommand: PROC[switch: LONG STRING, out: Format.StringProc]
RETURNS[outcome: Exec.Outcome ¬ normal] =
BEGIN
out["Server started at "L];
out[data.serverstarted];
out["\n\tConnections served: "L];
Format.LongDecimal[out, data.connectionsserved];
out["\n\tCurrent heap size(pages): "L];
Format.LongDecimal[out, Heap.GetAttributes[zone].heapPages];
out["\n"G];
SELECT TRUE FROM
(switch = NIL) => NULL;
Match[switch, "recycle"L] =>
BEGIN
out["Stopping server ... '"L];
WebOps.StopServer[];
out["restarting ..."L];
WebOps.StartServer[];
out["done\n"L];
END;
ENDCASE =>
BEGIN
out["'"L];
out[switch];
out["' is undefined for 'Server' command\n"L];
outcome ¬ abort;
END;
END; --ServerCommand--
CacheCommand: PROC[switch: LONG STRING, out: Format.StringProc]
RETURNS[outcome: Exec.Outcome ¬ normal] =
BEGIN
out["Currently there are approximately "L];
Format.LongDecimal[out, data.cacheCurrent];
out[" kilobytes of cache\n"L];
SELECT TRUE FROM
(switch = NIL) => NULL;
Match[switch, "prune"L] =>
BEGIN
out["Pruning server cache ... "L];
WebCache.serverCache.prune[WebCache.serverCache];
out["done\n"L];
END;
Match[switch, "enum"L] =>
BEGIN
out["I can't do that!"L];
outcome ¬ abort;
END;
Match[switch, "max"L] =>
BEGIN
out["I can't do that!"L];
outcome ¬ abort;
END;
ENDCASE =>
BEGIN
out["Switch '"L];
out[switch];
out["' was not recognized\n"L];
outcome ¬ abort;
END;
END; --CacheCommand--
LoggingCommand: PROC[switch: LONG STRING, out: Format.StringProc]
RETURNS[outcome: Exec.Outcome ¬ normal] =
BEGIN
IF (switch = NIL) THEN
BEGIN
out["Expected a switch with 'Logging' command\n"L];
outcome ¬ abort;
END
ELSE
BEGIN
value: LONG STRING ¬ Value[switch];
IF (value = NIL) THEN
BEGIN
out["Expected a 'value' to follow '"L];
out[switch];
out["' switch\n"L];
outcome ¬ abort;
END
ELSE
BEGIN
ENABLE UNWIND => zone.FREE[@value];
SELECT TRUE FROM
Match[switch, "level"L] =>
BEGIN
SELECT TRUE FROM
Match[value, "silent"L] => data.level ¬ silent;
Match[value, "basic"L] => data.level ¬ basic;
Match[value, "http"L] => data.level ¬ http;
Match[value, "verbose"L] => data.level ¬ verbose;
ENDCASE =>
BEGIN
out["'"]; out[value];
out["' is not a valid logging level\n"L];
outcome ¬ abort;
END;
END;
Match[switch, "mailto"L] =>
BEGIN
saved: LONG STRING ¬ data.emailaddress;
data.emailaddress ¬ value;
out["Mailing log file to '"L];
out[value]; out[" ... "L];
WebLog.MailLogFile[! UNWIND => data.emailaddress ¬ saved];
data.emailaddress ¬ saved;
out["done\n"L];
END;
ENDCASE =>
BEGIN
out["'"L];
out[switch];
out["' is undefined for 'Logging' command\n"L];
outcome ¬ abort;
END;
zone.FREE[@value];
END;
END;
END; --LoggingCommand--
Match: PROC[string, literal: LONG STRING] RETURNS[match: BOOLEAN ¬ FALSE] =
BEGIN
--has to match for at least as long as the literal
FOR index: NATURAL IN[0..literal.length) DO
IF (String.LowerCase[string[index]] # literal[index]) THEN EXIT;
REPEAT FINISHED => match ¬ TRUE;
ENDLOOP;
END; --Match--
Value: PROC[string: LONG STRING] RETURNS[value: LONG STRING ¬ NIL] =
BEGIN
--looking for the string past the first ':
FOR index: NATURAL IN[0..string.length) DO
IF (string[index] = ':) THEN
BEGIN
value ¬ zone.NEW[StringBody[string.length - index]];
FOR copy: NATURAL IN(index..string.length) DO
String.AppendChar[value, string[copy]];
ENDLOOP;
EXIT;
END;
ENDLOOP;
END; --Value--
Unload: Exec.ExecProc = {
IF Busy[] THEN {
Exec.OutputProc[h]["Tool is busy. Sorry.\n"L];
RETURN[error] };
Tool.Destroy[wh];
Exec.RemoveCommand[h, "WebServer.~"L];
Heap.Delete[zone];
Done[];
};
MakeSWs: Tool.MakeSWsProc =
BEGIN
logName: LONG STRING ¬ [20];
Tool.UnusedLogName[unused: logName, root: "WebServer.log"L];
data.msgSW ¬ Tool.MakeMsgSW[window: window, lines: 3];
data.formSW ¬ Tool.MakeFormSW[
window: window, formProc: MakeForm, zone: zone];
data.fileSW ¬ Tool.MakeFileSW[window: window, name: logName];
END; --MakeSWs--
MakeForm: FormSW.ClientItemsProcType = {
nItems: CARDINAL = WebTool.FormItems.LAST.ORD + 1;
serveris: ARRAY[0..2) OF FormSW.Enumerated ¬ [
["inactive"L, WebTool.ServerisTypes.inactive.ORD],
["active"L, WebTool.ServerisTypes.active.ORD]];
level: ARRAY[0..4) OF FormSW.Enumerated ¬ [
["silent"L, WebTool.LevelTypes.silent.ORD],
["basic"L, WebTool.LevelTypes.basic.ORD],
["http"L, WebTool.LevelTypes.http.ORD],
["verbose"L, WebTool.LevelTypes.verbose.ORD]];
items ¬ FormSW.AllocateItemDescriptor[nItems, zone];
items[WebTool.FormItems.serveris.ORD] ¬ FormSW.EnumeratedItem[
tag: "Server is"L, place: [6, FormSW.line0], feedback: all,
proc: ServerTransition, choices: DESCRIPTOR[serveris],
value: @data.serveris, z: zone];
items[WebTool.FormItems.serverstarted.ORD] ¬ FormSW.StringItem[
tag: "Server started"L, place: [24, FormSW.line1],
readOnly: TRUE, inHeap: TRUE, string: @data.serverstarted, z: zone];
items[WebTool.FormItems.connectionsserved.ORD] ¬ FormSW.LongNumberItem[
tag: "Connections served"L, place: [282, FormSW.line1],
readOnly: TRUE, signed: FALSE, default: 2147418112,
value: @data.connectionsserved, z: zone];
items[WebTool.FormItems.cache.ORD] ¬ FormSW.TagOnlyItem[
tag: "Cache"L, place: [6, FormSW.line3], z: zone];
items[WebTool.FormItems.cachePrune.ORD] ¬ FormSW.CommandItem[
tag: "Prune"L, place: [42, FormSW.line4], proc: PruneServerCache, z: zone];
items[WebTool.FormItems.cacheEnumerate.ORD] ¬ FormSW.CommandItem[
tag: "Enumerate"L, place: [96, FormSW.line4],
proc: EnumerateServerCache, z: zone];
items[WebTool.FormItems.cacheSize.ORD] ¬ FormSW.TagOnlyItem[
tag: "Size(kb)-"L, place: [174, FormSW.line4], z: zone];
items[WebTool.FormItems.cacheLimit.ORD] ¬ FormSW.LongNumberItem[
tag: "maximum"L, place: [234, FormSW.line4], signed: FALSE,
default: 0, value: @data.cacheLimit, z: zone];
items[WebTool.FormItems.cacheCurrent.ORD] ¬ FormSW.LongNumberItem[
tag: "current"L, place: [348, FormSW.line4], readOnly: TRUE,
signed: FALSE, default: 0, value: @data.cacheCurrent, z: zone];
items[WebTool.FormItems.logging.ORD] ¬ FormSW.TagOnlyItem[
tag: "Logging"L, place: [6, FormSW.line6], z: zone];
items[WebTool.FormItems.level.ORD] ¬ FormSW.EnumeratedItem[
tag: "Level"L, place: [36, FormSW.line7],
choices: DESCRIPTOR[level], value: @data.level, z: zone];
items[WebTool.FormItems.sendlogto.ORD] ¬ FormSW.CommandItem[
tag: "Send log to"L, place: [156, FormSW.line7],
proc: Sendlogto, z: zone];
items[WebTool.FormItems.emailaddress.ORD] ¬ FormSW.StringItem[
tag: "e-mail address"L, place: [234, FormSW.line7],
inHeap: TRUE,
string: @data.emailaddress, z: zone];
RETURN[items: items, freeDesc: TRUE];
};
-- Mainline code
Init[];
END...