CopyIf ====== Autor: Thomas G. Liesner Stand: 04.12.2000, V1.1, allerdings durchaus Lücken in der Doku möglich... Voraussetzung: Installierter Hamster Aufbau der "CopyIf.ini" ======================= [Settings] ; Falls die Message-IDs bei umkopierten Nachrichten geändert werden sollen, ; hier eine entsprechende Ergänzung a la $$$$NotOriginal$$$$. definieren, ; untergräbt u.a. die Crosspost-Erkennung von Agent, sofern diese nach ; Message-ID sucht, kann ansonsten leer bleiben. Ansonsten muß bei Antworten ; auf diese veränderten Nachrichten Korrnews genutzt werden, um die References ; wieder in Ordnung zu bringen AddToMessageID= ; Erlaubt das Testen der Funktionen ohne echte Änderungen ; 1 = Testen, 0 = Ernstfall TestOnly=1 ; Einschränkung, wieviele Postings getestet werden sollen, 0 für alle: ; - bzgl. neuen Gruppen NewGroups_TestMax=100 ; - bzgl. schon mindestens einmal durchsuchten Gruppen TestMaxPostings=0 ; Welche Gruppen nicht durchsucht werden sollen - bitte als RegExp-Ausdruck ; angeben, bei mehreren Ausschlüssen durch Kommata trennen. IgnoreGroups=^local\.,^internal\. ; Folgende Gruppen trotzdem testen: DontIgnoreGroups= ; Meldung über noch unabgeholte Mails integrieren MailInfo=1 ; Nur Kopieren, wenn noch keine Instanz vorhanden ist - bedingt gesetztes ; AddToMessageID, ansonsten wird immer kopiert: CopyIfExist=0 ; Crosspostings nur einmal testen, 0 testet jede Instanz CheckXPostingsOnlyOnce=1 [Display] ; Immer Enddialog anzeigen Ja/nein = 1/0 ShowResultAlways=0 ; Enddialog anzeigen, wenn kopiert oder verändert wurde ShowResultIfAction=1 ; Enddialog anzeigen, wenn Counter > 0 existieren (gilt auch für ; Mailbenachrichtigungen): ShowResultIfCounter=1 ; Nur Counter > 0 im Enddialog anzeigen ShowOnlyCounterGreaterThanNull=1 ; Suchvorgang in Taskbar anzeigen ShowTaskBarEntry=1 ; Bei Testdurchlauf passende Postings anzeigen: ShowFoundedPostingsWhenSimulate=1 [Groups] ; Wird automatisch gefüllt... Aufbau der "CopyIf.def" ======================= 0. Aufbau ~~~~~~ 1. Allgemeines 2. Die konkreten Möglichkeiten 2.1 Einführung in die Syntax 2.1.1 "Wildcards" 2.1.2 Variablen 2.1.3 Stringfunktionen 2.1.4 If-Bedingungen 2.1.5 Ablaufsteuerung 2.1.6 Optionen umstellen 2.2 Text-Body bearbeiten 2.2.1 Introductions 2.2.2 Texte suchen/ersetzen 2.2.3 Quoted-Printable => 8-Bit 2.2.4 OE-Begin-Bug umgehen 2.3 Header bearbeiten 2.3.1 Definition zusätzlicher Header, Header ändern oder Header löschen 2.3.2 ISO-Header => 8-Bit 2.4 Änderungen speichern 2.5 Weiteres 3. Syntax 1. Allgemeines ~~~~~~~~~~~ Die CopyIf.def muß sich im selben Verzeichnis wie CopyIf.exe selber befinden. Sie wird für jedes neue Posting einmalig durchlaufen. Über die CopyIf.ini sind bestimmte Gruppen ausschliessbar, zudem werden Crosspostings auch nur einmal durchlaufen. Ein einmal verändert gespeichertes Posting bekommt einen speziellen Headereintrag, welcher ebenfalls einen Zweitdurchlauf ausschliesst. Sofern in der CopyIf.def nicht ausdrücklich gespeichert wird, werden die eventuell gemachten Änderungen ohne Rückmeldung verworfen. Zum ersten Testen sollte man alles bis auf eine Gruppe ausschliessen und den Testmodus aktivieren, bis das Skript den eigenen Wünschen entspricht. Als Begriffsklärung hier noch der Aufbau eines Postings aus Sicht von CopyIf: ------------ I. Header II. Body a) Introduction b) Text ------------ Beispiel: ------------ I. Message-ID: <3375981e.18152730@news.netway.at> Date: Sat, 10 May 1997 16:32:11 GMT Newsgroups: de.newusers.questions From: habol@netway.at (Hans Boldrino) ... II. a) Achtung, Newbie-Gruppe! ----------------------------------------- b) Hallo Leute! Werdegang eines Newbies ... ------------ 2. Die konkreten Möglichkeiten ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 2.1 Einführung in die Syntax ------------------------ Die "Sprache" von CopyIf entspricht weitgehend der von Korrnews. Sie ist zeilen- orientiert und auf die konkreten Anforderungen abgestimmt. 2.1.1 "Wildcards" - - - - - - Wildcards sind jeweils in Prozent-Zeichen eingeschlossen und erlauben z.B. die Nutzung eines Headers bei der Zuweisung an einen anderen Header oder beim Setzen von Introduction-Zeilen. Innerhalb von Wildcards kann man auch Stringfunktionen nutzen, die anschliessend noch erläutert werden. Verwendung finden Wildcards bei - der Zuweisung u.a. von Headern - Blockanweisungen (Introductions) In den meisten anderen Fällen werden "Stringausdrücke" verwendet, welche dieselben Möglichkeiten haben, aber etwas lesbarer sein dürften. Für die Zuweisung von Headern und Introduction-Blöcken ist die Wildcard- Syntax aber immer noch ganz passend. Weitere Details sind der Korrnews-Dokumentation entnehmbar. 2.1.2 Variablen - - - - - Variablen sind z.B. dazu nutzbar, - einen längeren/komplizierteren und mehrfach gebrauchten Ausdruck mit weniger Tippaufwand nutzen zu können - Zustände zu merken, um sie später noch mal in IF-Anweisungen verwenden zu können - Strings stückchenweise zusammensetzen zu können, um sie dann komplett z.B. einem Header zuzuweisen. Die Skriptsprache kennt zur Zeit ausschliesslich String-Variablen, um trotzdem Zahlen zu verarbeiten, können diese per Str() jederzeit in einen String gewandelt werden und mit Val() wieder zu einer Zahl gemacht werden. Die Definition einer neuen Variablen sieht so aus: Set %Gruppe% = Header(Newsgroups) Set %Programmierer% = "Thomas G. Liesner" Set %SeinVorname% = "Das ist doch der " + Extract("^[^ ]+", %Programmierer%) + "!" Set %ZitierteZeilen% = Str(MatchedLines('^>')) Die Nutzung in Wildcards sieht dann so aus: X-Der-Programmierer: heisst %Programmierer%! Set Introduction Hinweis: F'up2 %Header(FollowUp-To)% end Richtig interessant werden diese Funktionen im Zusammenhang mit Bedingungen. 2.1.3 Stringfunktionen - - - - - - - - Konvertierungen: ---------------- Lower(...) wandelt den String in Kleinschrift um Upper(...) wandelt den String in Grossschrift um DecodeISO(...) wandelt einen als ISO-8859-1/qp-kodierten Header in einen "normalen" String mit 8Bit-Zeichen um. 8BitTo7Bit(...) wandelt 8-Bit-Zeichen in Ersatzzeichen innerhalb des ASCII- Codes um. (ä => ae, ß => ss, ...) Str(...,...,...) Wandelt den als ersten Parameter übergebenen numerischen Ausdruck in einen String um. Der optionale zweite numerische Parameter legt die Anzahl der Vorkomma-, der optionale dritte die Anzahl der Nachkommastellen fest. EscRegExp(...) Wenn man Header u.ä. als RexExp nutzt, kann man mittels dieser Funktion versehentliche Seiteneffekte vermeiden, da alle speziellen Zeichen ausmaskiert werden ("." => "\." etc) String "zerlegen": ------------------ First(...) sucht das erste Element des Strings heraus. Bei einem String mit Kommata den Teil vor dem ersten Komma (z.B. Newsgroups-Header bei Crosspostings), falls der String mit "<" anfängt und mit ">" endet, holt er das erste "<...>"-Paar heraus (z.B. References-Header). Last(...) sucht das letzte Element des Strings heraus, äqui- valent zu "First" Adress(...) bezieht sich - wie auch die folgenden beiden Funktionen - auf Mailadressen i.S. des From- oder Reply-To-Headers. Adress versucht, nur die eigentliche Adresse zu extrahieren, aus "Hans Hirni " wird also "abc@def.gh" Name(...) Versucht, den Gesamtnamen zu extrahieren, aus "Hans Hirni " wird also "Hans Hirni" FirstName(...) Versucht, nur den Vornamen zu extrahieren, aus "Hans Hirni " wird also "Hans" MakeAdress(...,...) kombiniert den übergebenen Namen und die übergebene Mail- adresse zu einem From/Reply-To/To-kompatiblen Ausdruck iSv "Name " incl. Berücksichtigung von Sonderfällen im Namen (Punkte, Anführungsstriche) Extract(...,...) Sucht den als 1. Parameter angegebenen regulären Ausdruck (entspricht den regulären Ausdrücken im Hamster) im zweiten Parameter zu finden, bei Erfolg liefert er den entsprechenden Teil zurück, sonst einen Leerstring. Aus Extract("a.*e", "Hallenbau") würde also "alle" CutLeft(...,...) Entfernt die als zweiten Parameter angegebene Zahl von Zeichen aus dem als ersten Parameter übergebenen String, CutLeft("Re: Ups", 3) ergibt somit "Ups". CutRight(...,...)Entfernt die als zweiten Parameter angegebene Zahl von Zeichen rechts aus dem als ersten Parameter übergebenen String, CutRight("Re: Ups", 4) ergibt somit "Re:". Sonderfunktionen: ----------------- Header(...) liefert den Wert des angegebenen Headers Full [raw] Header [without Headername1, ...] Ohne Parameter und z.Z. nur in Bedingungen sinnvoll nutzbar, liefert den gesamten Header als Zeichenkette zurück. Um bestimmte Header auszuschliessen, kann der "without"-Zusatz genutzt werden. Full [raw] Body Ohne Parameter und z.Z. nur in Bedingungen sinnvoll nutzbar, liefert den gesamten Textbody abzgl. der Signatur zurück. Full [raw] Sig[nature] Ohne Parameter und z.Z. nur in Bedingungen sinnvoll nutzbar, liefert die komplette Signatur als String zurück Full Article/Posting/Mail liefert den gesamten Text im "raw"-Format als String zurück Konstanten: ----------- CR Entspricht einem Zeilenumbruch 2.1.4 If-Bedingungen - - - - - - - Um flexiblere Möglichkeiten zu bieten, gibt es die Möglichkeit, Zeilen von Bedingungen abhängig zu machen. Die Grundform lautet: If [then] ... [else if ...] [else ...] endif Der ElseIf- und der Else-Zweig sind optional, für die "..." lassen sich beliebige Anweisungen einsetzen, der Übersichtlichkeit halber sind Ein- rückungen empfehlenswert. If-Bedingungen lassen sich auch schachteln, bis zu 15 Ebenen sind erlaubt. Falls man nur einen Befehl ausführen möchte, geht auch die Kurzvariante: If then Die genaue Syntax ist unter 3. zu finden, hier einfach mal einige kon- krete Beispiele: ; MixGroups ersetzen ; ================== Set %MixGroup% = "" If %NG% begins with "uunet.de." and %NG%<>"uunet.de.test" then Set %MixGroup% = "uunet.de.all" If %NG% begins with "hamster." then Set %MixGroup% = "hamster.de.ALL" If %NG% begins with "muenster." If %NG% begins with "muenster.markt." Set %MixGroup% = "muenster.markt.ALL" else Set %MixGroup% = "muenster.ALL" endif endif ... If %MixGroup% > "" then Do Copy To Group %MixGroup% oder ; Ersatz für CopyFups ; =================== Set %CopyFups% = "0" Set %CopyThreads% = "0" If Header(References) contains "@"+%FQDN%+">" If Header(References) ends with "@"+%FQDN%+">" Set %CopyFups% = "1" Do Inc Counter "* neue direkte Antworten", "* in " + %XNG% Set %Typ% = "Direkte Antwort, " else Do Inc Counter "* Neuzugänge in eigenen Threads", "* in " + %XNG% Set %Typ% = "Eigener Thread, " endif Set %CopyThreads% = "1" endif ... If %CopyFups% = "1" then Do Copy To Group "tgl.fups" If %CopyThreads% = "1" then Do Copy To Group "tgl.threads" oder ; Hinweise auf spezielle Headereinträge ; ===================================== If %NG% contains "," If %Typ% = "" then Append Intro X-Post über %Str(Count(',', Header(Newsgroups)))% Gruppen (%NG%) endif Set %temp% = Lower(Header(FollowUp-To)) If %temp% > "" and Not (%NG% begins with "ml." or %NG%="de.rec.film.kritiken" _ or %NG% contains "digest") _ then If %temp%='de.alt.flame' or %temp%='de.alt.0d' or %temp% contains 'kasper' Append Intro ***** WARNUNG! => F'Up2 *%Upper(%temp%)%* gesetzt ***** Set Header FollowUp-To: %Header(FollowUp-To)%,%Header(Newsgroups)% else Append Intro F'Up2 %temp% gesetzt endif endif If Header(Control)>"" Append Intro Control: %Header(Control)% endif If Header(Supersedes)>"" Append Intro Supersedes: %Header(Supersedes)% endif If ChangedIntro Append Intro ---------------------------------------------------------------------------------------------------------------- end endIf ... Do Save Changes 2.1.5 Ablaufsteuerung - - - - - - - - Um Funktionsblöcke o.ä. auslagern zu können, gibt es folgendes Statement: Do Include Diese Anweisungen werden beim Laden der Skriptdatei bereits ausgeführt, daher funktioniert auch die Anweisung: Set Intro Do Include "intro.txt" end Wobei für den Fall genausogut die 'richtige' Funktion Set Intro from "intro.txt" genommen werden kann. Der Inhalt der Datei wird genauso behandelt wie die "originalen" Zeilen des Skripts. Der Dateiname wird - sofern kein absoluter Pfad angegeben - relativ zum Arbeitsverzeichnis geladen. Mit Gosub ist ein Sprung in die gleichnamige "Sub" möglich. Beispiel: Gosub Teil1 Gosub Teil2 Quit Sub Teil1 ... endsub Sub Teil2 ... endsub Der Befehl "Quit" beendet das Skript und ist bei Verwendung von Subs nötig, da ansonsten die Fehlermeldung "Unbekannter Befehl 'Sub ...'" kommen würde. Zum Springen gibt es dann noch Goto Label welcher zum Sprungziel :Label führt. 2.1.6 Optionen umstellen - - - - - - - - - Mit dem Befehl Set Option Bezeichnung = Wert können jederzeit die INI-Optionen für den aktuellen Postingdurchlauf geändert werden, dauerhafte Änderungen sind damit allerdings nicht möglich. Dies kann man z.B. gezielt nutzen, indem man die Option CopyIfExist je nach Bedarf aktiviert oder deaktiviert: Set CopyIfExist = "0" Hinweis: Die Namen der Optionen können sich prinzipiell in neuen Programmfassungen ändern, auch wenn ich dies weitgehend zu vermeiden suche. 2.2 Text-Body bearbeiten -------------------- 2.2.1 Introductions - - - - - - - Für Hinweise auf Fup2-Fallen und ähnliches eigenen sich die Introductions - dies sind zusätzliche Zeilen, die zu Anfang des Postings eingefügt werden. Bei Zuweisungen gibt es zwei grundsätzliche Alternativen: Neusetzen oder Anhängen, die Anweisung heisset entsprechend "Set" bzw. "Append". Zudem gibt es drei Möglichkeiten, den Inhalt zu übergeben: Entweder in der CopyIf.def selber als Mehrzeiler: ( Set | Append ) [raw] Introduction Zeile #1 Zeile #2 Zeile #3 ... end Beispiel: If Header(Newsgroups) contains "," Set Intro Hinweis: Crossposting! Gruppen: %Header(Newsgroups)% end endif oder in der CopyIf.def als Einzeiler: If IntroLines > 0 then Append Intro: "==================================================================" oder in einer externen Datei, so daß in der CopyIf.def nur noch ein Verweis steht: Set Intro from "Postingkopf.txt" Mit Delete Intro kann das Intro auch wieder gelöscht werden, mit ChangedIntro sollten Änderungen abfragbar sein und IntroLines wurde ja bereits im Beispiel genutzt. 2.2.2 Texte suchen/ersetzen - - - - - - - - - - - Zur individuellen Änderung des eigentlichen Textes gibt es folgenden Befehl: Do Replace ( first | last | all ) Hiermit kann gezielt im Text geändert werden, der Suchausdruck ist dabei ein regulärer Ausdruck wie auch im Hamster oft zu finden. Beispiel: If (Header(References) > "") and (Not %FB% contains CR+">") then If %FB% contains CR+" >" Append Intro ... AMK-Quoting-Stil korrigiert. Do Replace All "^ >" with ">" Set %Save% = "1" endif endif erlaubt auch Agent-Nutzern, AMKs Postings angenehm zu lesen. Eine weitere Option ist z.B. ganz gut für Werbeanhänge brauchbar: Delete between last , , [, ] Der erste Parameter gibt den Suchausdruck für die einleitende Zeile an, der zweite Parameter den Suchausdruck für die schliessende Zeile, wenn dieser Suchausdruck leer ist, wird nur nach dem ersten Suchausdruck gesucht und alles darunter befindliche gelöscht. Der dritte Ausdruck bestimmt, ob die einleitende und schliessende Zeile auch gelöscht werden sollen, der letzte Ausdruck erlaubt es, einen Ersatz für den gelöschten Text einzubauen. Der Werbeblock der Hamster-Mailingliste sieht z.B. so aus: [Normales Posting] ------------------------------------------------------------------------ Explore the popular High-End Room - Go To Where The Smart People Shop-uBid.com http://xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/ ------------------------------------------------------------------------ Der passende Löschausdruck ist somit: Set %Werbezeile% = "^------------------------------------------------------------------------$" Delete between last %Werbezeile%, %Werbezeile%, false , "[ Nerv-Werbung gelöscht ]" bzw. Set %Werbezeile% = "^------------------------------------------------------------------------$" Delete between last %Werbezeile%, %Werbezeile%, true Mit dem Befehl Delete Empty Lines at end lassen sich überzählige Leerzeilen am Ende auch noch eleminieren. 2.2.3 Quoted-Printable => 8-Bit - - - - - - - - - - - - - Für Free Agent, OE- und Gravity-Nutzer sollte folgende Funktion noch interessant sein: Do Convert QPBody konvertiert QP-Postings nach 8-Bit incl. Headeranpassung. 2.2.4 OE-Begin-Bug umgehen - - - - - - - - - - Für OE-Nutzer, welche den "begin Dateiname"-Bug leid sind, steht die Funktion Do Convert OEBeginBug zur Verfügung, welche die beiden Leerzeichen auf ein Leerzeichen zusammenkürzt. 2.3 Header bearbeiten ----------------- 2.3.1 Definition zusätzlicher Header, Header ändern oder Header löschen - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Um einen Header zu ändern oder neu zu erzeugen, ist folgender Befehl gedacht: Set Header Headername: Inhalt Mit Set X-Hallo: Hi wird der Header X-Hallo eingefügt. Der Inhalt der Zuweisung darf Wildcards enthalten: Set X-Crosspost: %Str(Count(',',Header(Newsgroups)))% Das Löschen von Headern erfolgt über Delete Header X-MimeOLE Die Sortierung ist auch variabel: Do Sort Header , , ... erlaubt die Sortierung der Header nach dem eigenen Geschmack. Do Sort Header Newsgroups, FollowUp-To, From, Reply-To würde dafür sorgen, daß die genannten Header als erste kommen - wobei nicht existente Header schlicht ignoriert werden - und die restlichen Header darunter in ihrer Originalreihenfolge stehen bleiben. 2.3.2 ISO-Header => 8-Bit - - - - - - - - - - Damit auch Gravity oder Free Agent-Nutzer Header gut lesen können, ist die Anweisung Do Convert QPHeader eingebaut - sie wandelt nur ISO-8859-1/qp-Kodiertes, dies ist aber schon die Mehrzahl der entsprechenden Einträge. Für XNews-Anwender macht die Funktion ebenfalls Sinn, da XNews beim Dekodieren eines mehrzeiligen Headers fehlerhafterweise ein Leerzeichen zuviel einbaut, was speziell im Subject extrem unangenehm ist. Das Dekodieren mittels CopyIf umgeht diese fehlerhafte Dekodierung... 2.3.3 OE-Bug umgehen, der beim Antworten diverse Subject-Anfänge löscht - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OE hat die Angewohnheit, alles, was möglicherweise ein lokalisiertes "Re: " sein könnte, beim Antworten rigoros zu löschen. Aus "V: Schöne Spiele" wird bei OE beim Antworten "Re: Schöne Spiele" statt "Re: V: Schöne Spiele". Die Funktion Do Convert OEKillFalseReBug vermeidet diesen Fehler, indem er gegen sich selber eingesetzt wird: Aus "V: Schöne Spiele" macht CopyIf - sofern keine References-Header vorhanden sind - einfach "XX: V: Schöne Spiele", was beim Antworten mittels OE wieder zu "Re: V: Schöne Spiele" mutieren dürfte - und allen anderen einen heilen Thread bringt. Das "XX: " wird immer dann davor gesetzt, wenn das Posting keine Antwort ist und ein bis drei Zeichen (A-Z) enthält, denen ein Doppelpunkt folgt. Bitte diese Funktion nicht bei funktionierenden Newsreadern einsetzen, diese würden das "XX: " beim Antworten im Normalfall beibehalten... 2.4 Änderungen speichern -------------------- Damit man noch was von den Änderungen hat, müssen sie noch gespeichert werden. Dazu dienen zur Zeit drei Befehle: Do Copy to Group erstellt eine Kopie in die genannte Gruppe, die natürlich existieren muss. Do Export as speichert das Posting in eine Textdatei, diese muß im Namen einen "*" haben, der dann beim Speichern durch die erste noch freie Nummer ersetzt wird. Do Save Changes ersetzt das bisherige Posting, indem er die alten Instanzen löscht und den Artikel re-importiert. Do Touch erlaubt es, daß Gruppen, die man nicht direkt im Client liest, sondern über CopyIf in einer Pseudogruppe zusammengefaßt hat, trotzdem nicht als "tote" Gruppen in der täglichen Statistik auftauchen. Beispiel: ; Sofern man eine entsprechende Funktion definiert hat... Gosub MixGroup "hamster.de.", "hamster.de.all" ; Originalgruppenlesedatum aktualisieren: Do Touch "^hamster\.de\." ; Alternativschreibweise: Do Touch "^"+EscRegExp("hamster.de.") 2.5 Weiteres -------- Folgende Befehle passen nicht in die bisherigen Oberpunkte und sind somit hier gesammlt aufgeführt: Do ( Exec | Run ) [ , ] Hiermit können externe Programme aufgerufen werden, wobei zusätzlich noch Parameter übergeben werden dürfen: Do Run "Notepad.exe", "CopyIf.def" würde z.B. die "CopyIf.def" in den Editor laden. Das Skript hält dabei nicht an, sondern läuft nach Absetzen des Aufrufs direkt weiter, eventuell wird dies in zukünftigen Versionen per Parameter steuerbar sein. Do Include erlaubt das Auslagern von Teilen der CopyIf.def in andere Dateien, was den Einbau von Fremdskripten erleichten dürfte. Do Include "Skripte\iHinweis.def" würde die Datei "Skripte\iHinweis.def" ausgehend vom Arbeitsverzeichnis suchen und in die aktuelle "CopyIf.def" einbinden. Zum Debuggen sind ggf. noch zwei Funktionen nutzbar: Stop Geht in den Fehlerdialog, um z.B. Variablen u.ä. einsehen zu können Do Show zeigt das aktuelle Posting incl. der bisherigen Änderungen an. Do Inc counter [, ] Um freie Ausgaben zu erlauben, kann man mit Do Inc Counter "Antwortpostings" oder Do Inc Counter "Antwortpostings", Header(Newsgroups) Zeilen für den Enddialog erzeugen, jede Do Inc-Aufruf zählt dabei automatisch hoch, am Ende werden dann die Zeilen im linken Dialogteil ausgegeben. Bei Nutzung des zweiten Parameters kann man das Ganz noch nach einem Sub-Schlüssel aufteilen lassen. Do Define Counter , , ... Falls einem die Ausgabereihenfolge zu chaotisch ist, kann man mit diesem Befehl die Counter-Reihenfolge rechtzeitig festlegen, zwei Kommata nacheinander sorgen dabei für eine Leerzeile. 3. Syntax ~~~~~~ Die CopyIf.def ist zeilenweise organisiert, somit muß zwischen Block- strukturen, die sich über mehrere Zeilen erstrecken, und "normalen" Anweisungen unterschieden werden. Es gibt folgende Blockstrukturen: Sub | ... endsub If [then] | ... else if | ... else | ... endif ( Set | Append ) Intro[duction] ... end ( Set | Append ) raw Intro[duction] ... end Set [raw] Header Headername: ... ... end Set [raw] %Variablenname% ... ... end Folgende Sonderfälle gibt es noch: Do Include Diese Anweisung wird ausserhalb des normalen Ablaufs ausgeführt, somit führen Zugriffe auf Variablen automatisch zu Fehlern. :Label Labels dürfen nicht innerhalb von If-Blöcken/Anweisungen plaziert werden, da im ersten Fall die Anzahl der Endifs nicht mehr stimmen dürfte und im zweiten Fall der Label nicht gefunden wird. "Normale" Anweisungen können ggf. auch über mehrere Zeilen gehen, wenn sie jeweils mit einem "_" abgeschlossen werden, was vor allem bei längeren Bedingungen ganz gut nutzbar ist, intern werden sie zu einer großen Zeile zusammengesetzt und wie eine normale Zeile behandelt. Die einzelnen Elemente sind so definiert: Anweisung := ( [Set Header] ( = | : ) ( ) | Set Header from | Set = | Set from | ( Set | Append ) [raw] H[eader] ( = | : ) ) | Delete Header | Do Sort Header , , ... | ( Set | Append ) [raw] Intro[duction] = | ( Set | Append ) [raw] Intro[duction] from | Delete Intro[duction] | Do Replace ( first | all | last ) with | Delete between ( first | last ) , , [, ] | Delete Empty Lines at end | Set Opt[ion] = | Do ( Exec | Run ) , | If then | Gosub | Return | Goto