MSyntax10.Scn.Fnt`8qParcElemsAlloc qInfoElemsAllocVSyntax10.Scn.FntSIStampElemsAlloc24 Oct 97"Title": NetNews "Author": Hamader Peter "Abstract": Module exporting commands for the netnews client "Keywords": netnews internet nntp "Version": 1.0 "From": ? "Until":  "Changes": no changes [8FoldElemsNew#Syntax10.Scn.Fnt CR = 0DX; TAB = 9X; nameLen = 128; server = 0; group = 1; left = 2; middle = 1; right = 0; cancel = {left, middle, right}; 8H8#Syntax10.Scn.Fnt t: T.Text END; 8/8#Syntax10.Scn.Fnt44 inserted: BOOLEAN; key, val: NNE.String END; 88#Syntax10.Scn.Fnt// num: INTEGER; hel: HeaderEntryList END; 8g8#Syntax10.Scn.Fnt88 VAR pos: LONGINT; r: T.Reader; prep: FE.PrepSwitchMsg; loc: TF.Location; BEGIN WITH f: TF.Frame DO WITH m: O.InputMsg DO IF (m.id = O.track) & (m.keys = {middle}) & (m.X >= f.X + f.barW) THEN TF.TrackWord(f, m.X, m.Y, pos, m.keys); pos := TF.Pos(f, m.X, m.Y); TF.LocateLine(f, m.Y, loc); T.OpenReader(r, f(TF.Frame).text, pos); T.ReadElem(r); IF (r.elem = NIL) OR (pos # T.ElemPos(r.elem)) THEN T.OpenReader(r, f(TF.Frame).text, pos); T.ReadPrevElem(r); IF (r.elem # NIL) & (r.elem IS ME.Elem) THEN T.ReadPrevElem(r) END; IF (r.elem # NIL) & (r.elem IS NNE.Elem) & (r.elem(NNE.Elem).mode IN NNE.leftMode) & (loc.org <= T.ElemPos(r.elem)) THEN IF r.elem(NNE.Elem).CanSwitch() THEN IF m.keys # cancel THEN r.elem(NNE.Elem).HandlePrepSwitchMsg(prep) END ELSE NN.Msg("connection busy") END ELSE superHandle(f, m) END ELSE superHandle(f, m) END ELSE superHandle(f, m) END ELSE superHandle(f, m) END ELSE superHandle(f, m) END; END FrameHandler; 8]8#Syntax10.Scn.Fnt VAR t:T.Text; beg, time: LONGINT; BEGIN O.GetSelection(t, beg, end, time); IF time > 0 THEN T.OpenReader(r, t, beg); T.ReadElem(r); RETURN TRUE ELSE NN.Msg(msg); RETURN FALSE END END ReadSelectedElem; 8r8#Syntax10.Scn.Fntll (* causes the next articles to be shown (if available *) VAR end: LONGINT; r: T.Reader; BEGIN IF ReadSelectedElem(r, end, "no GroupElem selected") THEN IF (r.elem IS NNE.GroupElem) & (r.elem(NNE.GroupElem).mode IN {FE.expLeft, FE.expRight}) THEN r.elem(NNE.GroupElem).NextArticles ELSE NN.Msg("no GroupElem selected") END END END NextArticles; 88#Syntax10.Scn.Fnt (* closes all Server- and GroupElems causing the associated connections to be closed properly; finally the viewer is closed *) VAR v: Viewers.Viewer; prep: FE.PrepSwitchMsg; r: T.Reader; t: T.Text; par: O.ParList; pos: LONGINT; s: T.Scanner; BEGIN par := O.Par; v := NIL; IF par.frame = par.vwr.dsc THEN v := par.vwr ELSIF O.Pointer.on THEN v := O.MarkedViewer() END ; IF (v # NIL) & (v IS MV.Viewer) & (v.dsc.next IS TF.Frame) THEN t := v.dsc.next(TF.Frame).text ELSE RETURN END; T.OpenReader(r, t, 0); T.ReadElem(r); WHILE r.elem # NIL DO IF (r.elem IS NNE.ServerElem) & (r.elem(NNE.ServerElem).mode =FE.expLeft) THEN r.elem(NNE.ServerElem).conn.CancelAllRequests END; T.ReadElem(r) END; T.OpenReader(r, t, 0); T.ReadElem(r); WHILE r.elem # NIL DO IF (r.elem IS NNE.GroupElem) & (r.elem(NNE.GroupElem).mode = FE.expLeft) THEN pos := T.ElemPos(r.elem(NNE.Elem))+1; r.elem(NNE.GroupElem).handle(r.elem(NNE.GroupElem), prep); T.OpenReader(r, t, pos) END; T.ReadElem(r) END; T.OpenReader(r, t, 0); T.ReadElem(r); WHILE r.elem # NIL DO IF (r.elem IS NNE.ServerElem) & (r.elem(NNE.ServerElem).mode = FE.expLeft) THEN pos := T.ElemPos(r.elem(NNE.Elem))+1; r.elem(NNE.ServerElem).handle(r.elem(NNE.ServerElem), prep); T.OpenReader(r, t, pos) END; T.ReadElem(r) END; T.OpenScanner(s, v.dsc(TF.Frame).text, 0); T.Scan(s); IF (s.class IN {T.Name, T.String}) THEN T.Close(t, s.s) END; Viewers.Close(v) END Close; 88#Syntax10.Scn.FntZZ (* opens a text with the appropriate menu for the usage of the netnews client *) VAR beg, end, time: LONGINT; s: T.Scanner; t: T.Text; v: MV.Viewer; BEGIN T.OpenScanner(s, O.Par.text, O.Par.pos); T.Scan(s); IF (s.class = T.Char) & (s.c = "^") THEN O.GetSelection(t, beg, end, time); IF time > 0 THEN T.OpenScanner(s, t, beg); T.Scan(s) END END; IF s.class = T.Name THEN NNE.UpdatePreferences(TRUE); S.Insert(NNE.pref.stdDir, 0, s.s); NNE.ShowText(s.s, "^NetNews.Menu.Text", TF.Text(s.s), v); superHandle := v.dsc.next.handle; v.dsc.next.handle := FrameHandler END END Open; 8,8#Syntax10.Scn.Fnt (* text callback procedure; called after the information about all, at the corresponding server available groups has been fetched *) VAR r: T.Reader; ch: CHAR; t: T.Text; parc: TF.Parc; copy: T.CopyMsg; v: MV.Viewer; BEGIN t := req.tmpText; T.OpenReader(r, t, 0); T.Read(r, ch); WHILE ~r.eot DO IF ch = " " THEN T.Write(w, TAB) ELSE T.Write(w, ch) END; T.Read(r, ch) END; T.Delete(t, 0, t.len); T.Append(t, w.buf); TF.defParc.handle(TF.defParc, copy); parc := copy.e(TF.Parc); parc.nofTabs := 3; parc.tab[0] := 3168000; parc.tab[1] := 380000; parc.tab[2] := 4200000; T.WriteElem(w, parc); T.Insert(t, 0, w.buf); T.WriteString(w, "name of the group"); T.Write(w, TAB); T.WriteString(w, "highest Nr."); T.Write(w, TAB); T.WriteString(w, "smallest Nr."); T.Write(w, TAB); T.WriteString(w, "flags"); T.WriteLn(w); T.WriteLn(w); T.Insert(t, 1, w.buf); T.Delete(t, t.len-3, t.len); NNE.ShowText(req.data(NNE.Data).elem.name^, "^Edit.Menu.Text", t, v); END ShowAllGroups; 88#Syntax10.Scn.FntNN (* causes the information of all groups available at the selected server to be fetched *) VAR end: LONGINT; r: T.Reader; data: NNE.Data; e: FE.Elem; BEGIN IF ReadSelectedElem(r, end, "no ServerElem selected") THEN IF (r.elem # NIL) & (r.elem IS NNE.ServerElem) THEN IF r.elem(NNE.ServerElem).mode = FE.expLeft THEN e := r.elem(NNE.ServerElem) ELSIF r.elem(NNE.ServerElem).mode = FE.expRight THEN e := FE.Twin(r.elem(NNE.ServerElem)) ELSE NN.Msg("no open ServerElem selected"); RETURN END; IF ~e(NNE.ServerElem).conn.IsConnected() THEN e(NNE.ServerElem).conn.Connect(NIL, NN.SCallBackProc) END; NEW(data); data(NNE.Data).elem := e(NNE.ServerElem); e(NNE.ServerElem).conn.Req("list", NIL, data, ShowAllGroups, NN.SCallBackProc) ELSE NN.Msg("no open ServerElem selected") END END END AllGroups; 8):8#Syntax10.Scn.Fnt (* inserts a text element (either GroupElem or ServerElem) to be inserted into a text at the caret position after having selected the name of the element *) VAR v: Viewers.Viewer; t1, t2: T.Text; name: ARRAY nameLen OF CHAR; s: T.Scanner; i, port: INTEGER; beg, end, time, pos: LONGINT; ch: CHAR; BEGIN O.GetSelection(t1, beg, end, time); IF time > 0 THEN v := O.FocusViewer; name[0] := 0X; IF v.dsc.next IS TF.Frame THEN IF v.dsc.next(TF.Frame).hasCar THEN t2 := v.dsc.next(TF.Frame).text; T.OpenScanner(s, t1, beg); T.Read(s, ch); i := 0; WHILE ~s.eot & (ch # " ") & (ch # CR) & (ch # TAB) & (i="a")) OR ((ch<="Z") & (ch>="A")) END IsChar; 8 (* read a key; a key may consist of a character followed by a (n empty) number of characters / digits / "-" *) VAR i: INTEGER; PROCEDURE IsChar (ch: CHAR): BOOLEAN;  BEGIN IF ~IsChar(ch) THEN error := TRUE; RETURN END; i := 0; WHILE ~r.eot & (IsChar(ch) OR ((ch<="9") & (ch>="0")) OR (ch ="-")) DO key[i] := ch; INC(i); T.Read(r, ch) END; key[i] := 0X; error := r.eot END Key; 8*8#Syntax10.Scn.FntJJ (* read a value; a value may consist of an (empty) array of characters *) VAR i: INTEGER; BEGIN i := 0; WHILE ~r.eot & (i=32) DO val[i] := ch; INC(i); T.Read(r, ch) END; val[i] := 0X; IF i>=LEN(val)-1 THEN WHILE ~r.eot & (ORD(ch)>=32) DO T.Read(r,ch) END END; error := r.eot END Val; 8?A8#Syntax10.Scn.Fnt (* resolves a header line into key and value *) BEGIN T.Read(r, ch); ReadWS; IF error THEN RETURN FALSE END; Key(key); IF error THEN RETURN FALSE END; ReadWS; IF error THEN RETURN FALSE END; IF (ch = ":") THEN T.Read(r, ch); ReadWS ELSE RETURN FALSE END; IF error THEN RETURN FALSE END; Val(val); IF ~r.eot THEN pos := T.Pos(r)-1 ELSE pos := t.len END; RETURN (ch = CR) & ~error END HeaderLine; 8 (* parses the header of an article and returns the contents in 'he' *) CONST delta = 10; VAR r: T.Reader; ch: CHAR; error: BOOLEAN; newHe: HeaderEntryList; key, val: ARRAY 256 OF CHAR; j: INTEGER; PROCEDURE ReadWS;  PROCEDURE Key (VAR key: ARRAY OF CHAR);  PROCEDURE Val (VAR val: ARRAY OF CHAR);  PROCEDURE HeaderLine (VAR key, val: ARRAY OF CHAR): BOOLEAN;  BEGIN NEW(he.hel, delta); he.num := 0; T.OpenReader(r, t, 0); WHILE HeaderLine(key, val) DO NNE.Copy(key, he.hel[he.num].key); NNE.Copy(val, he.hel[he.num].val); he.hel[he.num].inserted := FALSE; INC(he.num); IF he.num = LEN(he.hel^) THEN NEW(newHe, LEN(he.hel^)+delta); FOR j := 0 TO he.num-1 DO newHe[j] := he.hel[j] END; he.hel := newHe END END END ParseHeader; 88#Syntax10.Scn.FntZZ (* searches for an entry with key 'key' in the list of header entries 'he' *) VAR i: INTEGER; BEGIN IF he.hel = NIL THEN COPY(alternative, val) END; i := 0; WHILE (i "); WHILE ~r.eot DO T.Write(w, ch); IF ch = CR THEN T.WriteString(w, "> ") END; T.Read(r, ch) END; T.Delete(t, 0, t.len) END; T.Append(t, w.buf); NNE.Beautify(t); signature := TF.Text(NNE.pref.posting.signature); IF signature.len # 0 THEN T.Write(w, CR); T.WriteString(w, "--"); T.Write(w, CR); T.Append(t, w.buf); T.Save(signature, 0, signature.len, w.buf); T.Append(t, w.buf) END; NNE.ShowText("NEW ARTICLE", "^Edit.Menu.Text", t, v) END ShowArticle; 8*^8#Syntax10.Scn.Fnt (* text callback procedure; shows an article prepared as a response *) BEGIN ShowArticle(req.tmpText, FALSE) END ShowReplyTo; 88#Syntax10.Scn.Fnt (* causes an article to be fetches which will be prepared as a reply *) VAR r: T.Reader; end: LONGINT; e: FE.Elem; se: NNE.ServerElem; reqStr: ARRAY NN.reqLen OF CHAR; t: T.Text; BEGIN IF ReadSelectedElem(r, end, "no ArticleElem selected") & (r.elem # NIL) & (r.elem IS NNE.ArticleElem) THEN IF r.elem(NNE.ArticleElem).mode IN NNE.leftMode THEN e := r.elem(NNE.ArticleElem) ELSE e := FE.Twin(r.elem(NNE.ArticleElem)) END; IF e.hidden.len = 0 THEN se := NNE.GetServerElem(e(NNE.ArticleElem)); ASSERT(se # NIL); IF ~se.conn.IsConnected() THEN se.conn.Connect(NIL, NN.SCallBackProc) END; reqStr := "article "; S.Append(e(NNE.ArticleElem).name^, reqStr); se.conn.Req(reqStr, NIL, NIL, ShowReplyTo, NN.SCallBackProc) ELSE t := TF.Text(""); T.Append(t, e.hidden); T.Save(t, 0, t.len, e.hidden); ShowArticle(t, FALSE) END ELSE t := TF.Text(""); T.WriteString(w, "Subject: "); T.Write(w, CR); T.Write(w, CR); T.Append(t, w.buf); ShowArticle(t, TRUE) END END NewArticle; 8#8#Syntax10.Scn.Fnt BEGIN NNE.UseConnection(~NNE.pref.useConnection); IF NNE.pref.useConnection THEN NN.Msg("online operation enabled") ELSE NN.Msg("offline operation enabled") END END ToggleConnectionUsage; 8$LMODULE NetNews;  IMPORT NN := NNTP, NNE:= NetNewsElems, T:=Texts, TF := TextFrames, Viewers, MV := MenuViewers, O:=Oberon, FE := FoldElems, S := Strings, D := Display, ME := MarkElems; CONST  TYPE Data = POINTER TO DataDesc; DataDesc = RECORD (NNE.DataDesc)  HeaderEntryList = POINTER TO ARRAY OF RECORD  HeaderEntries = RECORD  VAR w: T.Writer; superHandle: D.Handler; PROCEDURE FrameHandler (f: D.Frame; VAR m: D.FrameMsg);  PROCEDURE ReadSelectedElem(VAR r: T.Reader; VAR end: LONGINT; msg: ARRAY OF CHAR): BOOLEAN;  PROCEDURE NextArticles*;  PROCEDURE Close*;  PROCEDURE Open*;  PROCEDURE ShowAllGroups (req: NN.Request);  PROCEDURE AllGroups*;  PROCEDURE InsertNNElem (type: INTEGER);  PROCEDURE InsertGroup*;  PROCEDURE InsertServer*;  PROCEDURE MakePersistent*;  PROCEDURE MarkAsRead*;  PROCEDURE UpdatePreferences*;  PROCEDURE SendArticle (req: NN.Request);  PROCEDURE ParseHeader (t: T.Text; VAR he: HeaderEntries; VAR pos: LONGINT);  PROCEDURE GetHeaderEntry (he: HeaderEntries; key, alternative: ARRAY OF CHAR; ins: BOOLEAN; VAR val: ARRAY OF CHAR);  PROCEDURE Post*;  PROCEDURE ShowArticle(t: T.Text; empty: BOOLEAN);  PROCEDURE ShowReplyTo (req: NN.Request);  PROCEDURE NewArticle*;  PROCEDURE ToggleConnectionUsage*;  BEGIN T.OpenWriter(w) END NetNews.