Sowohl in der Win32, als auch in der Linux-Version ist eine Makrosprache implementiert. In der DOS-Version ist diese Makrosprache nicht vorgesehen (zu wenig Speicher).
Die Makrosprache ist bis jetzt noch eine Baustelle. !!! Vorsicht. Diese Beschreibung ist unvollständig, in teilen falsch !!! und auch die Software ist noch unvollständig und ungetestet. !!!
Dieses Kapitel dient lediglich der Sammlung von Ideen.
Es wird oft gewünscht, in eine vorhandene Mailbox zusätzliche Funktionen einzubinden. Eine gewisse Möglichkeit ist durch die Einbindung von RUN-Utilities gegeben. Diese sind allerdings sehr unflexibel und können nur stark eingeschränkt auf die Datenbestände der Mailbox zugreifen.
Um diese Lücke zu schließen wurde eine Skriptsprache entwickelt, mit der kleine Zusatzfunktionen realisiert werden können, für die das Mailboxprogramm nicht verändert werden muß. Was dabei herausgekommen ist, wird jedem Informatiker, der sich bereits mit formalen Sprachen beschäftigt hat, die Haare zu Berge stehen lassen. Beruhigend ist jedoch die Tatsache, daß es relativ viele Skriptspachen gibt, die ebenso wirr strukturiert sind und dabei einen weit größeren Verbreitungsgrat haben als BCSL.
Ein Skript kann zu Testzwecken vom Sysop per Befehl gestartet werden. Dies geht mit macro <scriptname> Die Skripte stehen in einem Verzeichnis namens "macro". Jedes Skript wird in einem eigenen File abgelegt. Der Name des Scriptes ist der Filename (ohne Endung, bzw. mit Endung ist auch diese Bestandteil des Scriptnamens) Im praktischen Betrieb erfolgt die Abarbeitung von Skripten ereignisgesteuert. Immer wenn ein Ereignis unter bestimmten Randbedingungen eintritt, kann ein bestimmtes Skript ausgeführt werden. Die Verbindung zwischen Ereignissen und Skriptnamen wird in einem file namens MACRO.BCM festgelegt.
Dieses File hat Einträge im folgenden Format:
<event> [<call> | <command> | <wildcard>]: <macro>
Folgendes Beispiel zeigt grob die Möglichkeiten: login *: loginout login dg3rbu: rbu accept db0aab-3: aab3 cmd a*ktuell: akt cmd pu*rge: disabled scmd fwdadd: fwdadd beforecmd v*ersion: b beforecmd *: aftercmd *: mailfm dh3vy: deletemail mailto *: unknown db0kfb*: nobox
In der ersten Spalte wird das Ereignis benannt, das zur Ausführung des Skriptes führen wird. Hinter dieser Angabe erfolgt ein Parameter, der das Ereignis entweder konkretisiert oder einschränkt. Abkürzungen mit * sind meist möglich, die alleinige Angabe von * ist grundsätzlich zutreffend und ist gleichwertig mit dem Weglassen dieses Parameters, d.h. "login *: script" und "login: script" verhalten sich gleich. Hier gibt es folgende Möglichkeiten: login [<call>]: Nach jedem Login wird dieses Makro ausgeführt. Der Zeitpunkt der Ausführung ist, nachdem alle Loginformalitäten erledigt sind aber noch bevor das erste Kommando (siehe "alter command") ausgeführt wurde. Durch einen Parameter kann das Rufzeichen des Logins eingeschränkt werden. Mittels * ist das Rufzeichen abzukürzen. Denkbar wäre z.B. ein Konstrukt: "login oe*: oegruss", wenn im Skript "oegruss" alle Freunde aus unserem südöstlichen Nachbarland besonders herzlich begrüßt werden. accept <call>: Hier wird ein Connect von außen unter dem angegebenen Rufzeichen entgegengenommen. Der Connect wird entgegengenommen, die Kommunikation mit dem neu ankommenden Benutzer wird direkt an das angegebene Skript übergeben.
Wird das Skript beendet, so erfolgt ein Disconnect. Bei Logins über Telnet oder am Bildschirm kann das Rufzeichen als zweiter Parameter beim Login angegeben werden. An dieser Stelle ist eine Abkürzung mit * nicht zulässig. cmd <command>: Fügt ein neues Kommando der Boxoberfläche hinzu. Das Kommando kann mittels * in einen verbindlichen und einen optionalen Teil zerfallen. Beispiel: new*command bedeutet, daß ein neuer Befehl "newcommand" eingefügt wird, wobei "new" in jedem Fall eingegeben werden muß, der Rest kann entfallen. So eingeführte Befehle können eingebaute Befehle der Mailbox überschreiben, diese sind dann mit der original-Syntax nicht mehr ausführbar.
Durch Voranstellen eines "-"Zeichen wird jedoch die Ausführung des eingebauten Befehles erzwungen, so daß ein tatsächliches Abdecken eines Befehles nicht möglich ist (dies ist kein Bug sondern Absicht). scmd <command>: wie "cmd", jedoch sind so erzeugte Kommandos nur im Sysop-Modus ausführbar. beforecmd <cmd>: aftercmd <cmd>: Hier referenzierte Skripte werden vor bzw. nach der Ausführung des mit <cmd> referenzierten Kommandos ausgeführt.
Die Abkürzung des Kommandos muß genauso aufgeschrieben werden, wie dieses von der Box interpretiert wird. mailfrom <call>: mailto <call>: Dieses Skript wird aufgerufen, wenn eine neue Nachricht in der Box eintrifft. Über den Parameter kann ggf. eingeschränkt werden, zu wem (bzw. in welche Rubrik) oder von wem eine Nachricht eingespeichert wurde. Beim Forwarding ist dabei das tatsächliche Absenderrufzeichen der Mail relevant, nicht das Loginrufzeichen der Forwardbox. Wird kein Parameter oder * angegeben, sind die Ereignisse "mailto" und "mailfrom" identisch. unknown <adr>: Das referenzierte Skript wird ausgeführt, wenn eine Nachricht mit einer unbekannten Forwardadresse eintrifft. Bei der Adresse ist eine Abkürzung mit * möglich.
Die BCM-Makroskriptsprache ist eine frei erfundene Programmiersprache, die zwar sehr viele Gemeinsamkeiten mit anderen Sprachen hat, zu keiner jedoch identisch ist. Der Kritiker mag dies schimpfend bemängeln, wird es aber auch nicht mehr ändern können. Als Begründung, warum die Sprache so aussieht und nicht anders kann man lediglich anführen, daß sie eben so geworden ist. Eine belastbare technische Begründung gibt es nicht. Wesentlich ist die Tatsache, daß es nicht möglich ist, komplexe Programme in BCSL zu schreiben.
Um keine falschen Hoffnungen zu wecken, seien hier zunächst einmal die Schwächen von BCSL aufgezählt: - mit BCSL sind nur überschaubare, oberflächliche Problemstellungen lösbar. - Es existieren keine strukturierten oder indizierten Datentypen. - Unterprogamme sind nur mit gewissen Einschränkungen möglich. - durch textuelle Interpretation ist die Ausführung der Skripten ausgesprochen langsam. -
Es ist nur Integerarithmetik mit den Grundrechenarten vorhanden. - Durch relativ hohen Speicherbedarf ist die Funktion in der DOS-Version normalerweise garnicht, in Sonderfällen nur eingeschränkt nutzbar.
Jedes Skript wird durch die Schlüsselwörter "begin" und "end" eingeklammert. Auch Kontrollstrukturen ("if" und "loop") werden gezielt durch "endif" und "endloop" beendet. Jedes Statement wird mit ";" abgeschlossen. Es gibt einen Unterschied zwischen Prozeduren und Funktionen. Funktionen haben einen Rückgabewert, dieser muß auch weiterverarbeitet werden (z.B. in einem Klammerausdruck oder einer Zuweisung). Prozeduren haben keinen Rückgabewert und können nicht zugewiesen werden. Das Verhalten in dieser Beziehung ist ähnlich Pascal. Innerhalb einer Blockstruktur (zwischen "begin" und "end" oder sinngemäß innerhalb einer Kontrollstruktur "if" oder "loop") wird eine Serie von Statements ausgeführt, die jeweils durch ";" abgeschlossen werden.
begin <script-Block>
if( Bedingung )
<script-Block>
else
<script-Block>
endif;
loop
<script-Block>
exit_if( Bedingung );
<script-Block>
endloop;
<script-Block>
end.
Beispiel:
// Skript zur Ausgabe des Aktuelltextes
begin
ftime=filetime("msg/aktuell.dl");
if(ftime!=0) put("Aktuell vom " & datestr(ftime) & " " & timestr(ftime)
& "\n");
cmd("rt msg/aktuell.dl");
else put("Kein aktueller Text geladen.\n");
endif;
end.
Im Skript dürfen an beliebigen Stellen Kommentare stehen. Die Konventionen entsprechen hier der Sprache C++: - Die Zeichen /* und */ schliessen einen Kommentar innerhalb einer Zeile ein. Hierbei ist keine Schachtelung zulässig. - Das Zeichen // leitet einen Kommentar bis zum Ende der Zeile ein. - Kommentare dürfen an beliebigen Stellen im Quelltext stehen, jedoch nicht innerhalb eines Bezeichners (Konstante, Variable, Schlüsselwort etc).
Variablen werden implizit deklariert. Das heißt, daß eine Variable als deklariert und definiert gilt, sobald sie die linke Seite einer Zuweisung passiert hat. Eine Variable kann ohne Typdeklaration sowohl numerischen als auch alphanumerischen Inhalt annehmen, auch eine Zuweisung von einem Typ zum anderen ist zulässig. Eine leere (aber definierte) Variable kann durch x=""; erzeugt werden. Der Inhalt einer Variablen kann beliebig lang werden (Begrenzung durch die Resourcen des Rechners, bei DOS 64kByte), numerische Werte reichen von -2^31 bis 2^31 (32 Bit integer).
Im Programmtext werden Stringkonstanten in "Anführungszeichen" angegeben. Bei numerischen Konstanten ist sowohl eine Angabe mit als auch ohne Anführungszeichen zulässig. Innerhalb von Stringkonstanten können die C-üblichen Steuerzeichen verwendet werden, das sind: \n Newline, also Sprung an den Anfang der nächsten Zeile \a Klingelzeichen, also CTRL-G \" Anführungszeichen " \xhh Hexadezimale Angabe des Zeichencodes, der eingesetzt werden soll. Die Angabe wird stets 2stellig erwartet, z.B. \x00 oder \xff Beispiele: x=10; y="das ist ein String mit Zeilenende\n"; xx=27+y; ist gleichwertig zu xx="27"+y;
Die Schlüsselwörter von BCSL teilen sich auf in - Operatoren - Strukturanweisungen - interne Prozeduren - interne Funktionen Operatoren (Auflistung in der Reihenfolge der Priorität)
Bei allen arithmetischen Operationen werden leere Variablen, oder Variablen die keine Zahl beinhalten, zu 0 angenommen (eine solche Operation führt nicht zum Fehler). Bei logischen Operationen gilt der Wert 0 (oder keine Zahl) als "FALSE", jede Zahl ungleich 0 gilt als "TRUE". Zurückgegeben wird "0" für "FALSE" und "1" für "TRUE". Das Verhalten in dieser Beziehung ist gleich der Sprache C.
Für Variablen, die keine Zahlen enthalten (also Strings) sind nur die Operatoren "==", "!=" und "&" sinnvoll einsetzbar. Punktoperatoren: * multipliziert numerische Ausdrücke / dividiert numerische Ausdrücke mod Modulo-Operation (Rest der Division) Strichoperatoren: + addiert numerische Ausdrücke - subtrahiert numerische Ausdrücke Vergleichsoperatoren: > größer als < kleiner als >= größer oder gleich <= kleiner oder gleich == gleich (bei Strings: identisch. Groß-Kleinschreibung irrelevant) != ungleich (auch für Strings anwendbar) logische Operatoren 0 oder : and logisches UND or logisches ODER xor exklusives ODER Stringverkettung: & fügt Strings aneinander.
* * begin, end: Klammern ein ganzes Skript.
* Jedes Skript wird durch die Folge begin <block> end; eingeschlossen.
* * if, else, endif Machen eine Verzweigung nach einer Bedingung
möglich. Das Verhalten ist mit jeder anderen strukturierten
Programmiersprache vergleichbar.
* Eine Schachtelung solcher Strukturen ist möglich und hat nicht das von
C bekannte Problem der Uneindeutigkeit von "else", da jede Struktur mit
"endif" eingeklammert wird. if( <Bedingung> ) <block> endif; oder if(
<Bedingung> ) <block> else <block> endif; Der Ausdruck <Bedingung> wird
wie in C numerisch ausgewertet. Jeder i numerische Wert ungleich 0 ist
"true", 0 ist "false". Ein nichtnumerischer Wert wird ebenfalls als
"false" interpretiert.
* * loop, exit, exit_if, endloop Dies sind die Elemente des
Schleifenkonstruktes. Ein solches Konstrukt ist z.B. aus der Sprache
ADA bekannt und ersetzt die verschiedenen Konstrukte wie "do ... while"
oder "repeat ... until". Auch eine FOR-Schleife existiert hier nicht.
loop <block> exit_if( <Bedingung> ); <block> endloop; Der Block vor
oder nach dem "exit_if"-Ausdruck kann jeweils auch entfallen, dadurch
erfolgt die Überprüfung der Ausstiegsbedingung vor oder nach dem
Schleifendurchlauf. Die Bedingung wird wie bei "if" ausgewertet. Das
Schlüsselwort "exit" bewirkt einen harten Ausstieg aus der Schleife
ohne Bedingung, dies wird jedoch wohl eher selten benötigt.
* abort(<reason>); bricht ein Makro hart ab.
* call(<macroname>[,<var1>,...]); ruft ein Unterprogramm auf. Dabei
können Variablen für das Unterprogramm sichtbar gemacht werden.
* disconnect(<handle>); geht noch nicht.
* filewrite(<filename>,<data>,<mode>); schreibt eine Variable in ein
File.
* oshell(<command>); führt einen shell-Befehl aus.
* put(<string>); gibt einen String zum Benutzer aus.
* sleep(<milliseconds>); suspendiert das Skript.
* trace(<level>,<string>); gibt einen Trace ins Syslog aus.
* cmd("e dl8mbt 1-"); führt ein Boxkommando aus. Ausgaben erfolgen zum
Benutzer.
* x=chr(65); * one ASCII-character erzeugt ein Zeichen gemäß
angegebenem Code.
* x=cmd("v"); führt ein Kommando aus und weist die Ausgabe des Kommandos
einer Variable zu.
* handle=connect("dl8mbt","db0aab"); geht noch nicht.
* x=datestr(gettime); wandelt ANSI-Zeit in lesbaren String um.
* line=fileline("filename",5); geht noch nicht.
* string=fileread("filename"); liest ein File ein und weist es einer
Variable zu.
* size=filesize("filename"); gibt die Größe eines Files zurück.
* t=filetime("filename"); gibt den Zeitstempel eines Files als ANSI-Zeit
zurück.
* s=getfname("dl8mbt",25); geht noch nicht.
* s=getline; geht noch nicht. s=getlist("dl8mbt"); geht noch nicht.
* s=getmacro("%o"); geht noch nicht.
* t=gettime; gibt die momentane ANSI-Zeit zurück.
* s=getuser("dl8mbt","forward"); gibt eine ALTER-Einstellung eines Users
zurück. Folgende Einstellungen können abgefragt werden: check command
echo fdelay fhold forward grep helplevel idir ilist iread lf lines
mybbs name nopurge prompt pw pwline quota readlock reject rlimit speech
status ttypw udir ulist uread geht noch nicht.
* boolean=getvalid; geht noch nicht.
* call=getvar("logincall"); gibt den Inhalt einer Systemvariable zurück.
Folgende Bezeichner können übergeben werden: logincall uplink txbytes
rxbytes sysop logintime board dest src frombox at bid boxlist usermail
lifetime lines bytes subject replyboard replynum taskid taskname
created lastinput lastcmd geht noch nicht.
* i=len(string); gibt die Länge eines Strings zurück.
* a=lines(x); gibt die Anzahl der Zeilen in einem String zurück. geht
noch nicht.
* x=oshell("ls -l"); or oshell("ls -l"); shell-Ausstieg. Zuweisung geht
noch nicht.
* s=strline(x,5); gibt die 5-te Zeile eines Strings zurück. geht noch
nicht.
* i=strpos(s,"test"); gibt die Position eines Substrings in einem String
zurück.
* t=timestr(time); wandelt ANSI-Zeit in Zeitstring um
* s1=token(command,1); zerlegt einen String in einzelne Teile geht noch
nicht.
* x=val("A"); gibt den Zeichencode des ersten Zeichens im String zurück
Wichtig: Dieses Kapitel diente lediglich der Sammlung von Ideen!