UNIT jobs; {$project vt }
{ Job-Verwaltung zum Programm VideoText }

INTERFACE; FROM vt USES datei;

VAR editor,configpath: Str80;
    toplevel: Integer;
    active: Byte; { aktive Jobs in der Seitensuche }
    maxwait, burst,shuffle: Integer;

PROCEDURE sleep;
PROCEDURE wakeup;
PROCEDURE handle_queue;
PROCEDURE handle_jobs;
PROCEDURE attempt_input(job: Integer);
PROCEDURE add_job(entry: Str80; blind: Boolean);
PROCEDURE topselect(pg1,pg2,pg3: p_onepage);
PROCEDURE topgrab(pg1,pg2: p_onepage);
PROCEDURE getconfig(nr: Integer);
PROCEDURE edconfig(nr: Integer);
PROCEDURE dump_numbers;

{ ---------------------------------------------------------------------- }

IMPLEMENTATION;

{$ opt q,s+,i+} { keine Laufzeitprüfungen außer Stack und Feldindizes }

CONST SERMAG = $0800; { C11 }
      RENEW  = $0100; { C8 }
      STITLE = $0040; { C6 }
      HEADLN = $0020; { C5 }

VAR bedtime: zeiteintrag;

PROCEDURE uhrzeit(VAR zeit: zeiteintrag);
BEGIN
  telltime(zeit.tage,zeit.min,zeit.tics);
END;

PROCEDURE add_time(delta: zeiteintrag; VAR zeit: zeiteintrag);
BEGIN
  IF zeit.tics + delta.tics>=3000 THEN Inc(zeit.min);
  zeit.tics := (zeit.tics + delta.tics) MOD 3000;
  IF zeit.min + delta.min>=1440 THEN Inc(zeit.tage);
  zeit.min := (zeit.min + delta.min) MOD 1440;
  zeit.tage := zeit.tage + delta.tage;
END;

PROCEDURE sub_time(delta: zeiteintrag; VAR zeit: zeiteintrag);
BEGIN
  IF zeit.tics - delta.tics<0 THEN Dec(zeit.min);
  zeit.tics := (zeit.tics - delta.tics + 3000) MOD 3000;
  IF zeit.min - delta.min<0 THEN Dec(zeit.tage);
  zeit.min := (zeit.min - delta.min + 1440) MOD 1440;
  zeit.tage := zeit.tage - delta.tage;
END;

FUNCTION diff_time(alt, neu: zeiteintrag): Long;
{ liefert die Differenz zwischen zwei Zeitpunkten in Ticks (1/50-Sekunden) }
BEGIN
  diff_time := neu.tics - alt.tics + 3000 * ( (neu.min - alt.min)
               + 1440 * (neu.tage - alt.tage) );
END;

PROCEDURE sleep;
{ wird vor Unterbrechung der Hauptschleife (z. B. durch FileRequester) }
{ aufgerufen }
BEGIN
  uhrzeit(bedtime);
END;

PROCEDURE wakeup;
{ Verhindert in Zusammenarbeit mit sleep, daß Zeiten, während derer der }
{ VT-Decoder gar nicht abgefragt wird, zum Löschen von Jobs wegen }
{ Zeitüberschreitung führen. }
VAR delta: zeiteintrag;
    j: Integer;
BEGIN
  uhrzeit(delta);
  sub_time(bedtime,delta);
  FOR j := 0 TO maxactive-1 DO
    add_time(delta,activejobs[j].lastaction);
END;

PROCEDURE count_them;
{ durchzählen, wer in der Seitensuche alles bedient werden will: }
VAR j: Integer;
BEGIN
  active := 0;
  FOR j := 0 TO maxactive-1 DO
    IF (activejobs[j].pg>0) THEN Inc(active);
END;

PROCEDURE handle_queue;
{ Verwaltet das Aufrücken wartender Jobs. }
{ Jobs mit unbestimmter Unterseitennummer, also mit /0, /*, /., /! können }
{ aktive Jobs mit gleicher Seitennummer aus der Seitensuche verdrängen }
VAR i,j,killer: Integer;
BEGIN
  REPEAT
    j := -1;
    IF queued>0 THEN BEGIN
      { Soll ein aktiver Job verdrängt werden? }
      IF queue[firstinq].sp<1 THEN
        FOR i := 0 TO maxactive-1 DO
          IF activejobs[i].pg=queue[firstinq].pg THEN j := i;
      { Oder ist ein Platz in der Seitensuche frei? }
      IF j<0 THEN
        FOR i := maxactive-1 DOWNTO 0 DO
          IF activejobs[i].pg=0 THEN j := i;
    END;
    IF j>=0 THEN BEGIN
      { Empfangseinheit <j> mit wartendem Job belegen }
      { Ein wartender Job besteht nur aus Seiten- und Unterseitennummer, }
      { jetzt kommen eine Menge Informationen dazu. }
      WITH activejobs[j] DO BEGIN
        pg := queue[firstinq].pg; sp := queue[firstinq].sp;
        sp_count := 0; sp_max := 0;
        holen := False; erledigt := False;
        FOR i := 0 TO maxsubpage DO sp_check[i] := False;
        uhrzeit(lastaction);
        anfordern(j,pg,sp,%111);
      END;
      IF i2c_status<>0 THEN   { Oh Mann! Dummer I²C-Bus! }
        activejobs[j].pg := 0   { Kommando zurück }
      ELSE BEGIN
        { Warteschlange aufrücken: }
        Dec(queued); Inc(firstinq); IF firstinq>qlen THEN firstinq := 1;
        { Auswahlzeiger des Benutzers dem Aufrücken entsprechend nachführen: }
        IF thisjob=-1 THEN thisjob := j
          ELSE IF thisjob<0 THEN Inc(thisjob);
        IF thisjob>=0 THEN BEGIN
          aktspeicher := thisjob; display_select(aktspeicher); END;
        redraw_queue(-1);
      END;
    END;
  UNTIL j<0; { kein Platz mehr gefunden }
  IF queued=0 THEN fifo := True; { Warteschlange normalisieren }
  count_them;
END;

PROCEDURE rausschmeisser;
{ Prüfen, ob ein Job wegen Zeitüberschreitung gestrichen werden kann. }
{ Wenn der Job allerdings so lange wartet, weil er sich auf eine einzelne }
{ Unterseite bezieht, ist ihm verziehen und er wird lediglich ans Ende der }
{ Warteschlange gesetzt. Und für festgehaltene Seitennummern findet eine }
{ Prüfung auf Zeitüberschreitung gar nicht erst statt. }
VAR i, j: Integer;
    jetzt: zeiteintrag;
BEGIN
  uhrzeit(jetzt);
  FOR j := 0 TO maxactive-1 DO WITH activejobs[j] DO
    IF (pg<>0) AND (diff_time(lastaction,jetzt) > 50*maxwait)
    AND (sp>-2) THEN  { keine "festgehaltenen" Seiten! }
      IF sp IN [0,-1] THEN BEGIN
        { einfache Seitennummer streichen }
        pg := 0; sperren(j); redraw_queue(j);
      END ELSE IF (queued>0) AND (queued<qlen) THEN BEGIN
        { Unterseitenanforderung nur hintenanstellen }
        i := firstinq+queued; IF i>qlen THEN i := 1;
        Inc(queued);
        queue[i].pg := pg; queue[i].sp := sp;
        pg := 0; sperren(j); redraw_queue(-1);
      END;
  count_them;
END;

PROCEDURE handle_jobs;
{ Überprüft, ob in einem der Empfangskreise eine Seite eingetroffen ist, und }
{ wenn ja, ob sie aufgrund ihrer Unterseitennummer überhaupt eingelesen }
{ werden muß. Es wird auch entschieden, ob der Job durch die eingetroffene }
{ Seite erledigt ist. Kriterium für 'erledigt': }
{ Ein Job mit sp<>0 ist erledigt, sobald die eine angeforderte Unterseite }
{ da ist. Ein Job mit sp=0 ist erst dann erledigt, wenn alle seine }
{ Unterseiten eingelesen wurden. Ein festgehaltener Job (sp=-2) ist niemals }
{ erledigt und muß anderweitig entfernt werden. }
{ Einlesen der Seite geschieht an anderer Stelle, s. u. }
VAR j: Integer;
    dummy: p_onepage;
    sp_decval: Integer;
BEGIN
  rausschmeisser;
  { Schauen, ob irgendwo das Eintreffen einer Kopfzeile gemeldet wurde: }
  FOR j := 0 TO maxactive-1 DO WITH activejobs[j] DO
    IF (pg>0) AND NOT holen THEN IF seite_da(j) THEN BEGIN
      New(dummy);
      uhrzeit(lastaction);
      getpage(j,dummy,false); { *nur* Status und Seitennummer holen }
      sp_decval := bcd(dummy^.sp);
      { *** Schauen, ob die Seite eingelesen werden muß. Gründe, weswegen }
      {   man eine Seite evtl. nicht haben will: }
      { 1.: Unterseite wurde bereits eingelesen. }
      {   (Bei zu großen Unterseitennummern kann das nur über ein etwas }
      {   armseliges Hilfskriterium festgestellt werden.) }
      IF (sp_decval<=maxsubpage) THEN holen := NOT sp_check[sp_decval]
        ELSE holen := (dummy^.sp>sp_max);
      {   für festgehaltene Seiten ist "bereits eingelesen" überhaupt kein }
      {   Hinderungsgrund: }
      IF sp=-2 THEN holen := True;
      { 2.: Nur eine Unterseite war angefordert, und zwar nicht diese. }
      {   Das dürfte allerdings kaum vorkommen, da der CCT ausdrücklich auf }
      {   die eine Unterseite programmiert wurde. }
      IF (sp>0) THEN
        holen := (dummy^.sp=sp)
      { *** Unterseitenverwaltung aktualisieren: }
      IF holen THEN BEGIN
        Inc(sp_count);
        IF (sp_decval<=maxsubpage) THEN  sp_check[sp_decval] := True;
        IF dummy^.sp>sp_max  THEN sp_max := dummy^.sp;
        akt_sp := dummy^.sp;
        redraw_queue(j);
      END;
      { *** Prüfen, ob der Job durch die eingetroffene Seite erledigt ist. }
      {   Das geht z. B. durch Unterseite 0 (= einzige Unterseite), auf }
      {   keinen Fall aber durch eine Unterseite mit der bislang höchsten }
      {   Nummer. }
      IF sp_check[0] THEN
        erledigt := sp_count>bcd(sp_max)
      ELSE IF dummy^.sp<sp_max THEN
        erledigt := sp_count=bcd(sp_max);
      { Auch möglich: die einzig geforderte Unterseite ist da. }
      IF (sp<>0) AND holen THEN
        erledigt := True;
      { *** Schauen, ob magazinserielle oder -gemischte Übertragung vorliegt }
      {   und danach die Wartezeit festlegen: }
      IF (dummy^.cbits AND SERMAG)=0 THEN
        wartezeit := shuffle ELSE wartezeit := burst;
      IF NOT holen THEN
        anfordern(j,pg,sp,%111);   { Suche geht weiter, PBLF setzen }
      Dispose(dummy);
    END;
END;

PROCEDURE attempt_input{(job: Integer)};
{ Das Einlesen geschieht in einem zweiten Schritt, da nach Eintreffen der }
{ Kopfzeile erst eine Weile gewartet werden muß. }
{ Evtl. wird die Seite auch gleich ausgegeben: auf dem Bildschirm, falls }
{ <thisjob> auf den zugehörigen Job zeigt oder wenn eine festgehaltene }
{ Seite aktualisiert wurde; in die Ausgabedatei, falls es eine zu }
{ protokollierende Seite war. }
{ Jobs, die nach diesem Einlesen erledigt sind, wurden bereits entsprechend }
{ gekennzeichnet und werden aus der Liste gestrichen. }
VAR dummy,seite: p_onepage;
    jetzt: zeiteintrag;
    exciting: Boolean;
BEGIN
  { Prüfen, ob die Wartezeit für eine einzulesende Seite um ist: }
  WITH activejobs[job] DO
    IF (pg>0) AND holen THEN BEGIN
      uhrzeit(jetzt);
      IF diff_time(lastaction,jetzt) >= wartezeit THEN BEGIN
        { Seite einlesen: }
        busy_pointer;
        { Ist bereits eine alte Version dieser Seite in der Liste, die dann }
        { lediglich überschrieben werden müßte? }
        seite := hunt_in_list(pg,akt_sp,True);
        IF seite=Nil THEN BEGIN
          New(seite);
          getpage(job,seite,True);
          normal_pointer;
          IF i2c_status<>0 THEN BEGIN Dispose(seite); Exit; END;
          ins_to_list(seite);
          update_list(seite,1);
        END ELSE BEGIN
          getpage(job,seite,True);
          normal_pointer;
          IF i2c_status<>0 THEN Exit;
        END;
        { Hat sich eine überwachte Seite geändert bzw. ist zum ersten Mal}
        { eingetroffen? }
        exciting := (sp<-1) AND ((sp_count=1) OR (seite^.cbits AND RENEW<>0))
            AND (seite^.cbits AND STITLE=0); { Untertitel ausgenommen! }
        { bildschirmüberwachte Seite anzeigen: }
        IF (seite=thispage) OR (exciting AND (sp=-2)) THEN BEGIN
          mainline; IF seite<>thispage THEN
            Write('neuer Inhalt S. ',hexstr(pg,3),'!');
          writepage(seite,True);
          IF seite<>thispage THEN BEGIN
            mainline; Write('bitte Leertaste dr}cken'); END;
        END;
        { dateiüberwachte Seite abspeichern (diesmal auch Untertitel): }
        IF sp=-3 THEN
          IF exciting OR (seite^.cbits AND STITLE<>0) THEN BEGIN
            sleep; IF save_action(seite,1)=0 THEN BEGIN
              { nach einmal genehmigtem Überschreiben auf Anhängen umschalten: }
              overwrite := False; fileinfo;
            END ELSE
              sp_count := 0; { nach fehlgeschlagenem Speichern }
              { sofort beim nächsten Mal wieder versuchen lassen }
            wakeup;
          END ELSE
            mainline;
        holen := False;  { Wichtig !!! }
        anfordern(job,pg,sp,%111);   { um PBLF wieder zu setzen }
      END;
    END;
  { Erledigten Job löschen, außer es ist ein festgehaltener Job: }
  WITH activejobs[job] DO
    IF (pg>0) AND erledigt AND NOT holen THEN
      IF (sp<-1) THEN BEGIN   { nicht löschen }
        { nur die Zähler zurücksetzen, außer sp_count }
        sp_check[0] := False; sp_max := 0; erledigt := False;
        redraw_queue(job);
      END ELSE BEGIN  { löschen }
        pg := 0; sperren(job); redraw_queue(job);
      END;
  count_them;
END;

PROCEDURE add_job{(entry: Str80; blind: Boolean)};
{ Eine Eingabe, die eine Seitennummer darstellt (oder auch nicht), in die }
{ Warteschlange einzureihen versuchen. }
{ Bei blind=False wird noch überprüft, ob sich vielleicht bereits ein }
{ identischer Job in der Seitensuche befindet. }
VAR i,j,page,subpage: Integer;
    s: str80;
    ok: Boolean;
BEGIN
  j := Pos('/',entry);
  IF j > 0 THEN BEGIN
    page := hexval(Copy(entry,1,j-1));
    subpage := hexval(Copy(entry,j+1,Length(entry)-j));
    IF entry[j+1]='*' THEN subpage := -1;
    IF entry[j+1]='.' THEN subpage := -2;
    IF entry[j+1]='!' THEN subpage := -3;
  END ELSE BEGIN
    page := hexval(entry);
    subpage := 0;
  END;
  ok := (page>=$100) AND (page<$900);
  { evtl. weitere Einschränkung: mit bereits vorhandenen Jobs vergleichen }
  { und keinen Job zulassen, der ein Verdrängen bewirken würde }
  IF NOT blind THEN BEGIN
    FOR j := 0 TO maxactive-1 DO
      IF (activejobs[j].pg=page) AND (subpage<1) THEN ok := False;
    FOR j := 1 TO queued DO BEGIN
      i := (firstinq + j - 2) MOD qlen + 1;
      IF (queue[i].pg=page) AND (subpage<1) THEN ok := False;
    END;
  END;
  IF ok AND (queued<qlen) THEN BEGIN
    IF fifo THEN BEGIN
      j := firstinq+queued; IF j>qlen THEN j := 1;
    END ELSE BEGIN
      Dec(firstinq); IF firstinq=0 THEN firstinq := qlen; j := firstinq;
    END;
    Inc(queued);
    queue[j].pg := page; queue[j].sp := subpage;
  END;
END;

PROCEDURE topselect{(pg1,pg2,pg3: p_onepage)};
{ benutzt die TOPtext-Seiten 1F2 [1F3 [1F4]] für Seitenauswahl }
{ ergibt eine Tabelle von [2/4/6] * 23 Schlagworten und Seitennummern }
VAR i,j,k,npg,anz,mag: Integer;
    nums: ARRAY [1..23,1..6] OF Integer;
    names: ARRAY [1..23,1..6] OF String[13];
    select: ARRAY [1..23,1..6] OF Boolean;
    pg: ARRAY[1..3] OF p_onepage;
    color: ARRAY[0..8] OF String[10]; { Farbkombinationen für die 8 Magazine }
    s: str80;
    l: Long;
    ch: Char;
PROCEDURE entry(i,j: Integer; marked: Boolean);
BEGIN
  IF npg=3 THEN GotoXY(13*(j-1)+1,i) ELSE GotoXY(18*(j-1)+5,i);
  Write(#155'0m');
  IF marked THEN Write(#155'31;42m');
  IF select[i,j] THEN Write('*') ELSE Write(' ');
  Write(#155'0m',color[nums[i,j] DIV 100]);
  IF marked AND (npg=3) THEN
    Write(Copy(names[i,j],1,8)) ELSE Write(names[i,j]);
  IF (marked OR (npg<3)) AND (nums[i,j]>0) THEN
    Write(nums[i,j]:4)
END;
PROCEDURE toggle_by_click(x,y: Integer);
VAR jj: Integer;
BEGIN
  IF npg=3 THEN BEGIN
    jj := (x-1) DIV 13 + 1;
  END ELSE BEGIN
    jj := (x-5) DIV 18 + 1; IF (x-5) MOD 18=17 THEN jj := 0;
  END;
  IF (y IN [1..23]) AND (jj IN [1..2*npg]) THEN BEGIN
    entry(i,j,False);
    i := y; j := jj;
    IF nums[i,j]>0 THEN select[i,j] := NOT select[i,j];
    entry(i,j,True);
  END;
END;
BEGIN
  pg[1] := pg1; pg[2] := pg2; pg[3] := pg3;
  npg := 0; FOR i := 1 TO 3 DO IF pg[i]<>Nil THEN Inc(npg);
  color[0] := #155'0m'; { getarnt }
  color[1] := #155'3'+colperms[6]+';4'+colperms[0]+'m'; { Cyan auf Schwarz }
  color[2] := #155'3'+colperms[6]+';4'+colperms[4]+'m'; { Cyan auf Blau }
  color[3] := #155'3'+colperms[7]+';4'+colperms[5]+'m'; { Weiß auf Purpur }
  color[4] := #155'3'+colperms[7]+';4'+colperms[1]+'m'; { Weiß auf Rot }
  color[5] := #155'3'+colperms[3]+';4'+colperms[0]+'m'; { Gelb auf Schwarz }
  color[6] := #155'3'+colperms[3]+';4'+colperms[4]+'m'; { Gelb auf Blau }
  color[7] := #155'3'+colperms[3]+';4'+colperms[5]+'m'; { Gelb auf Purpur }
  color[8] := #155'3'+colperms[3]+';4'+colperms[1]+'m'; { Gelb auf Rot }
  ClrScr; GotoXY(1,25); Write(#155'0m');
  WriteLn(#155'2mTOP-Text Themen}bersicht:'#155'0m <Crsr>, <Sp>: markieren, <BS>/<Del>: Korrektur');
  Write('<Esc>: Abbruch, <Return>: Auswahl }bernehmen und Ende');
  FOR i := 1 TO 23 DO
    FOR j := 1 TO 2*npg DO BEGIN
      gettopnum(pg[(j+1) DIV 2],20*(1-j MOD 2),i,nums[i,j],k);
      IF (nums[i,j]<0) OR (k<0) OR (nums[i,j]>$899) THEN
        nums[i,j] := 0; { ungültige Codierung! }
      nums[i,j] := bcd(nums[i,j]);
      names[i,j][13] := #0;
      FOR k := 1 TO 12 DO BEGIN
        names[i,j][k] := Chr(pg[(j+1) DIV 2]^.chars[40*i+20*(1-j MOD 2)+7+k]);
        IF names[i,j][k]<' ' THEN names[i,j][k] := ' ';
      END;
      select[i,j] := False;
      entry(i,j,False);
    END;
  i := 1; j := 1; entry(i,j,True);
  REPEAT
    l := Wait(-1);
    ch := readkey; s := '';
    CASE ch OF
      ' ': BEGIN
          IF nums[i,j]>0 THEN select[i,j] := True;
          entry(i,j,False);
          IF i<23 THEN Inc(i) ELSE
            IF j<2*npg THEN BEGIN Inc(j); i := 1; END;
          entry(i,j,True);
        END;
      #8: BEGIN
          entry(i,j,False);
          IF i>1 THEN Dec(i) ELSE
            IF j>1 THEN BEGIN Dec(j); i := 23; END;
          select[i,j] := False; entry(i,j,True);
        END;
      #127: BEGIN
          select[i,j] := False; entry(i,j,True);
        END;
      #155: REPEAT ch := readkey; s := s+ch; UNTIL ch>='@';
      OTHERWISE;
    END;
    IF s[1] IN ['A'..'D','S','T'] THEN BEGIN
      entry(i,j,False);
      CASE s[1] OF
        'A': IF i>1 THEN Dec(i);
        'B': IF i<23 THEN Inc(i);
        'C': IF j<2*npg THEN Inc(j);
        'D': IF j>1 THEN Dec(j);
        'S': i := 23;
        'T': i := 1;
        OTHERWISE;
      END;
      entry(i,j,True);
    END;
    intui_events;
    IF mouseclicked THEN BEGIN
      toggle_by_click(clickedx,clickedy);
      mouseclicked := False;
    END;
  UNTIL (ch IN [#13,#27]) OR stop;
  IF ch=#13 THEN
    FOR i := 1 TO 23 DO
      FOR j := 1 TO 2*npg DO
        IF select[i,j] THEN add_job(IntStr(nums[i,j]),True);
END;

PROCEDURE topgrab{(pg1,pg2: p_onepage)};
{ alle laut TOP-Text verfügbaren Seiten anfordern, allerdings nur bis zu }
{ der durch <toplevel> vorgegebenen Priorität }
CONST interleave=10;
VAR i,j,n,level: Integer;
    pri: ARRAY[0..17] OF Char;
BEGIN
  pri := '-KABCDEFHGJI-----';
  fifo := True;
  FOR level := 1 TO toplevel DO
    FOR i := 0 TO interleave-1 DO
      FOR j := 0 TO 799 DIV interleave DO BEGIN
        n := 100 + j*interleave + i;
        IF (n<900) THEN BEGIN
          IF Ord(pri[topcode[pg1^.chars[n-60]]])-64=level THEN
            add_job(IntStr(n),True);   { nach absteigender Priorität }
        END;
      END;
  redraw_queue(-1);
END;

PROCEDURE getconfig{(nr: Integer)};
VAR datei: text;
    sender, zeile, name: Str80;
    is_header, found, anything: boolean;
    t: integer;
BEGIN
  IF activejobs[0].pg=0 THEN BEGIN
    anfordern(0,100,0,%000);
    t := 50;   { max. 1 sec warten! }
    REPEAT Delay(1); Dec(t) UNTIL seite_da(0) or (t=0);
  END;
  gethead(0,sender);
  IF activejobs[0].pg=0 THEN sperren(0);
  IF nr=2 THEN name := configpath+'VT2.config'
          ELSE name := configpath+'VT.config';
  Reset(datei,name);
  IF IOResult<>0 THEN BEGIN
    Write(#155'2m"',name,'" nicht gefunden!');
    Exit;
  END;
  Buffer(datei,500);
  found := False; anything := False;
  is_header := True;
  WHILE NOT EoF(datei) DO BEGIN
    ReadLn(datei,zeile);
    t := Length(zeile); WHILE t>0 DO BEGIN
      IF zeile[t]=' ' THEN zeile[t] := #0 ELSE t := 1;
      Dec(t);
    END;
    IF zeile='' THEN
      is_header := True { auf eine Leerzeile folgt ein Titel }
    ELSE BEGIN
      IF is_header THEN BEGIN
        found := (Pos(zeile,sender)>0);
        IF found THEN BEGIN
          mainline; Write('hole '); IF nr>1 THEN Write(nr,'. ');
          Write('Seitenauswahl f}r "',zeile,'"');
          anything := True;
        END;
      END ELSE
        IF found THEN add_job(zeile,True);
      is_header := False;
    END;
  END;
  IF NOT anything THEN Write('unbekannter Sender: "',Copy(sender,5,10),'"');
  Close(datei);
  redraw_queue(-1);
END;

PROCEDURE edconfig{(nr: Integer)};
VAR fenster: Text;
    s: Str80;
LIBRARY DosBase:  { Include-Files nein danke }
-222 : FUNCTION  Execute(D1: Str; D2,D3: Long): Boolean;
END;
BEGIN
  showscreen(False);
  Reset(fenster,'CON:100/100/440/80/VT-Ausgabefenster');
  IF nr=2 THEN s := editor + configpath + 'VT2.config'
          ELSE s := editor + configpath + 'VT.config';
  IF Execute(s,0,FileHandle(fenster)) THEN  Write('abgeschlossen')
  ELSE  Write(#155'2mDOS-Fehler');
  Close(fenster);
  showscreen(True);
END;

PROCEDURE dump_numbers;
{ Sendername und Seitennummern ins ClipBoard schreiben, Hilfe beim Erstellen }
{ der config-Dateien }
VAR anz,nextnr,i: Integer;
    hilf: p_onepage;
    ok: Boolean;
    s: str80;
BEGIN
  IF root=Nil THEN Exit;
  anz := 0; hilf := root;
  WHILE hilf<>Nil DO BEGIN
    IF hilf^.next=Nil THEN nextnr := -1 ELSE nextnr := hilf^.next^.pg;
    IF (hilf^.pg<$900) AND (hilf^.pg<>nextnr) THEN Inc(anz);
    hilf := hilf^.next;
  END;
  start_clip(26+4*anz);
  FOR i := 2 TO 25 DO BEGIN
    s[i] := Chr(root^.chars[i+6]);
    IF s[i]<' ' THEN s[i] := ' ';
  END;
  s[1] := #10; s[26] := #10; s[27] := #0;
  clip_it(s,26);
  hilf := root;
  WHILE hilf<>Nil DO BEGIN
    IF hilf^.next=Nil THEN nextnr := -1 ELSE nextnr := hilf^.next^.pg;
    IF (hilf^.pg<$900) AND (hilf^.pg<>nextnr) THEN BEGIN
      s := hexstr(hilf^.pg,3)+#10; clip_it(s,4);
    END;
    hilf := hilf^.next;
  END;
  end_clip;
END;

BEGIN  { Initialisierungen }
  editor := 'c:Ed '; { Editor für configs }
  configpath := '';
  toplevel := 4;  { nur Programm- und Blockseiten }
  maxwait := 80;
  burst := 0;
  shuffle := 20;
END.
