DIRECTORY Arpa USING [Address, nullAddress], ArpaNameUDP, ArpaNameUDPBuf, ArpaUDP USING [AllocBuffers, Create, Destroy, Error, FreeBuffers, Get, GetRemoteAddress, Handle, Kick, Milliseconds, ReceivedError, Put, SetUserBytes], ArpaUDPBuf USING [Buffer, Port], Basics USING [BytePair, HighByte, LongNumber, LowByte], BasicTime USING [GetClockPulses, Pulses, PulsesToMicroseconds], Rope USING [Concat, Fetch, Find, FromChar, Length, ROPE, Substr]; ArpaNameUDPImpl: CEDAR PROGRAM IMPORTS ArpaUDP, Basics, BasicTime, Rope EXPORTS ArpaNameUDP = BEGIN OPEN ArpaNameUDP; ROPE: TYPE = Rope.ROPE; Error: PUBLIC ERROR [code: ATOM] = CODE; EndOfData: SIGNAL = CODE; sequenceNumber: CARDINAL _ 0; Pointer: TYPE = REF PointerRec; PointerRec: TYPE = RECORD[ ptr: CARDINAL _ 0 ]; Query: PUBLIC PROC [ server: Arpa.Address _ Arpa.nullAddress, query: Rope.ROPE _ NIL, type: QType _ a, class: QClass _ in, recurDesired: BOOL _ FALSE, port: ArpaUDPBuf.Port _ domainPort, timeout: ArpaUDP.Milliseconds _ 20000, retry: CARDINAL _ 0, acceptErrors: BOOL _ FALSE] RETURNS [reply: Reply_NIL] = { {ENABLE {UNWIND => NULL; ArpaUDP.Error => Error[code]; ArpaUDP.ReceivedError => Error[code]}; packetStart: BasicTime.Pulses; id: CARDINAL _ (sequenceNumber _ sequenceNumber + 1); done: BOOL _ FALSE; sendBuf: ArpaUDPBuf.Buffer; replyBuf: ArpaUDPBuf.Buffer; sendNameBuf: ArpaNameUDPBuf.Buffer; replyNameBuf: ArpaNameUDPBuf.Buffer; length: CARDINAL _ 0; handle: ArpaUDP.Handle _ ArpaUDP.Create[remoteAddress: server, remotePort: port, getTimeout: timeout, acceptErrors: acceptErrors]; IF handle = NIL THEN RETURN[]; sendBuf _ ArpaUDP.AllocBuffers[handle]; TRUSTED { sendNameBuf _ LOOPHOLE[sendBuf]; }; sendNameBuf.hdr3.id _ id; sendNameBuf.hdr3.qr _ query; sendNameBuf.hdr3.opcode _ query; sendNameBuf.hdr3.authoritative _ FALSE; sendNameBuf.hdr3.truncated _ FALSE; sendNameBuf.hdr3.recurDesired _ recurDesired; sendNameBuf.hdr3.recurAvail _ FALSE; sendNameBuf.hdr3.rcode _ ok; sendNameBuf.hdr3.qdCount _ 0; sendNameBuf.hdr3.anCount _ 0; sendNameBuf.hdr3.nsCount _ 0; sendNameBuf.hdr3.arCount _ 0; FOR i: CARDINAL IN [0..retry] UNTIL done DO length _ AppendQuery[sendNameBuf, query, type, class]; ArpaUDP.SetUserBytes[sendBuf, length + ArpaNameUDPBuf.hdrBytes]; packetStart _ BasicTime.GetClockPulses[]; ArpaUDP.Put[sendBuf]; UNTIL done OR (replyBuf _ ArpaUDP.Get[handle]) = NIL DO packetStop: BasicTime.Pulses _ BasicTime.GetClockPulses[]; milliSeconds: LONG CARDINAL _ (BasicTime.PulsesToMicroseconds[packetStop-packetStart])/1000; remoteAddress: Arpa.Address _ ArpaUDP.GetRemoteAddress[handle].address; TRUSTED { replyNameBuf _ LOOPHOLE[replyBuf]; }; reply _ NEW[ReplyRecord]; reply.nRetries _ i; reply.responseTime _ milliSeconds; reply.source _ remoteAddress; reply.udpLength _ LOOPHOLE[replyNameBuf.hdr2.length]; reply.hdr _ replyNameBuf.hdr3; ProcessDomainPacket[replyNameBuf, reply]; done _ TRUE; ENDLOOP; ENDLOOP; IF replyBuf # NIL THEN ArpaUDP.FreeBuffers[replyBuf]; sendNameBuf _ NIL; replyNameBuf _ NIL; sendBuf _ NIL; replyBuf _ NIL; ArpaUDP.Kick[handle]; ArpaUDP.Destroy[handle]; }}; ProcessDomainPacket: PROC [udp: ArpaNameUDPBuf.Buffer, reply: Reply] = { p: Pointer _ NEW[PointerRec]; p.ptr _ 0; LoadQueries[udp, reply, p]; LoadAnswerRRs[udp, reply, p]; LoadAuthorityRRs[udp, reply, p]; LoadAdditionalRRs[udp, reply, p]; }; LoadQueries: PROC [udp: ArpaNameUDPBuf.Buffer, reply: Reply, p: Pointer] = { ENABLE {UNWIND => NULL; EndOfData => GOTO Quit}; reply.questions _ NEW[QSequenceBody[reply.hdr.qdCount]]; FOR i: INT IN[0..reply.hdr.qdCount) DO q: QuestionRecord _ NEW[QBody]; q.name _ GetCompressedName[udp, p]; q.type _ VAL[Basics.LowByte[GetTwoBytes[udp, p]]]; q.class _ VAL[Basics.LowByte[GetTwoBytes[udp, p]]]; reply.questions[i] _ q; reply.qdCount _ reply.qdCount +1; ENDLOOP; EXITS Quit => RETURN; }; LoadAnswerRRs: PROC [udp: ArpaNameUDPBuf.Buffer, reply: Reply, p: Pointer] = { reply.answers _ NEW[RRSequenceBody[reply.hdr.anCount]]; reply.anCount _ LoadRRs[reply.hdr.anCount, reply.answers, udp, p]; }; LoadAuthorityRRs: PROC [udp: ArpaNameUDPBuf.Buffer, reply: Reply, p: Pointer] = { reply.authority _ NEW[RRSequenceBody[reply.hdr.nsCount]]; reply.nsCount _ LoadRRs[reply.hdr.nsCount, reply.authority, udp, p]; }; LoadAdditionalRRs: PROC [udp: ArpaNameUDPBuf.Buffer, reply: Reply, p: Pointer] = { reply.additional _ NEW[RRSequenceBody[reply.hdr.arCount]]; reply.arCount _ LoadRRs[reply.hdr.arCount, reply.additional, udp, p]; }; LoadRRs: PROC [inCount: CARDINAL, rrs: RRSequence, udp: ArpaNameUDPBuf.Buffer, p: Pointer] RETURNS[outCount: CARDINAL_0] = { ENABLE {UNWIND => NULL; EndOfData => GOTO Quit}; FOR i: INT IN[0..inCount) DO name: ROPE _ GetCompressedName[udp, p]; type: RRType _ VAL[Basics.LowByte[GetTwoBytes[udp, p]]]; thisRR: ResourceRecord _ NewRR[type]; thisRR.type _ type; thisRR.name _ name; thisRR.class _ VAL[Basics.LowByte[GetTwoBytes[udp, p]]]; thisRR.ttl _ GetTtl[udp, p]; thisRR.dataLength _ GetTwoBytes[udp, p]; WITH thisRR SELECT FROM rr: ARR => rr.address _ GetIPAddress[udp, p]; rr: NsRR => rr.serverRope _ GetCompressedName[udp, p]; rr: CNameRR => rr.cNameRope _ GetCompressedName[udp, p]; rr: PtrRR => rr.ptrRope _ GetCompressedName[udp, p]; rr: MbRR => rr.mbRope _ GetCompressedName[udp, p]; rr: MgRR => rr.mgRope _ GetCompressedName[udp, p]; rr: MrRR => rr.mrRope _ GetCompressedName[udp, p]; rr: MdRR => rr.mdRope _ GetCompressedName[udp, p]; rr: MfRR => rr.mfRope _ GetCompressedName[udp, p]; rr: NullRR => rr.nullRope _ GetCompressedName[udp, p]; rr: SoaRR => { rr.soaRec.primaryServer _ GetCompressedName[udp, p]; rr.soaRec.domainContact _ GetCompressedName[udp, p]; rr.soaRec.serial _ GetCard[udp, p]; rr.soaRec.refresh _ GetCard[udp, p]; rr.soaRec.retry _ GetCard[udp, p]; rr.soaRec.expire _ GetCard[udp, p]; rr.soaRec.minTtl _ GetCard[udp, p]}; rr: WksRR => { tempPortArray: ARRAY[0..100) OF CARDINAL; nPorts: CARDINAL _ 0; port: INT _ 0; rr.wksRec.address _ GetIPAddress[udp, p]; rr.wksRec.protocol _ GetOneByte[udp, p]; FOR j: INT IN [5..rr.dataLength) DO -- parse port bit array byte: INT [0..255] _ GetOneByte[udp, p]; FOR k: INT IN [0..8) DO IF byte >= 80H THEN { byte _ byte - 80H; tempPortArray[nPorts] _ port; nPorts _ nPorts +1}; byte _ byte * 2; port _ port + 1; ENDLOOP; ENDLOOP; rr.wksRec.nPorts _ nPorts; rr.wksRec.ports _ NEW[PortSequenceBody[nPorts]]; FOR j: INT IN [0..nPorts) DO rr.wksRec.ports[j] _ tempPortArray[j]; ENDLOOP; }; rr: HinfoRR => { rr.hinfoRec.cpu _ GetRope[udp, p]; rr.hinfoRec.os _ GetRope[udp, p]}; rr: MinfoRR => { rr.minfoRec.rmailbx _ GetCompressedName[udp, p]; rr.minfoRec.emailbx _ GetCompressedName[udp, p]}; rr: MxRR => { rr.mxRec.preference _ GetCardinal[udp, p]; rr.mxRec.host _ GetCompressedName[udp, p]}; ENDCASE => p.ptr _ p.ptr + thisRR.dataLength; -- unknown type rrs[i] _ thisRR; outCount _ outCount +1; ENDLOOP; EXITS Quit => RETURN; }; NewRR: PROC [type: RRType] RETURNS[ResourceRecord] = { RETURN[ SELECT type FROM a => NEW[a RRBody], ns => NEW[ns RRBody], cName => NEW[cName RRBody], ptr => NEW[ptr RRBody], mb => NEW[mb RRBody], mg => NEW[mg RRBody], mr => NEW[mr RRBody], md => NEW[md RRBody], mf => NEW[mf RRBody], null => NEW[null RRBody], soa => NEW[soa RRBody], wks => NEW[wks RRBody], hinfo => NEW[hinfo RRBody], minfo => NEW[minfo RRBody], mx => NEW[mx RRBody], ENDCASE => NIL ]; }; AppendQuery: PROC [ udp: ArpaNameUDPBuf.Buffer, query: ROPE, type: QType, class: QClass] RETURNS[length: CARDINAL_0] = { p: Pointer _ NEW[PointerRec]; p.ptr _ 0; AppendName[udp, query, p]; AppendTwoBytes[udp, LOOPHOLE[type], p]; AppendTwoBytes[udp, LOOPHOLE[class], p]; udp.hdr3.qdCount _ udp.hdr3.qdCount + 1; length _ p.ptr; }; AppendName: PROC [ udp: ArpaNameUDPBuf.Buffer, name: ROPE, p: Pointer] = { DO dot: INT _ Rope.Find[name, "."]; IF dot = -1 THEN EXIT; IF dot = 0 THEN EXIT; -- Bounds fault AppendFragment[udp, Rope.Substr[name, 0, dot], p]; name _ Rope.Substr[name, dot+1] ENDLOOP; IF Rope.Length[name] # 0 THEN AppendFragment[udp, name, p]; AppendFragment[udp, NIL, p]; }; AppendFragment: PROC [ udp: ArpaNameUDPBuf.Buffer, rope: ROPE, p: Pointer] = { ptr: CARDINAL _ p.ptr; chars: CARDINAL _ Rope.Length[rope]; udp.body.bytes[ptr] _ chars; FOR i: CARDINAL IN [0..chars) DO udp.body.bytes[ptr+i+1] _ LOOPHOLE[Rope.Fetch[rope, i]]; ENDLOOP; p.ptr _ p.ptr + chars + 1; }; AppendTwoBytes: PROC [ udp: ArpaNameUDPBuf.Buffer, data: UNSPECIFIED, p: Pointer] = { ptr: CARDINAL _ p.ptr; udp.body.bytes[ptr] _ Basics.HighByte[data]; udp.body.bytes[ptr+1] _ Basics.LowByte[data]; p.ptr _ p.ptr + 2; }; AppendIPAddress: PROC [ udp: ArpaNameUDPBuf.Buffer, address: Arpa.Address, p: Pointer] = { ptr: CARDINAL _ p.ptr; udp.body.bytes[ptr+0] _ address.a; udp.body.bytes[ptr+1] _ address.b; udp.body.bytes[ptr+2] _ address.c; udp.body.bytes[ptr+3] _ address.d; p.ptr _ p.ptr + 4; }; GetCompressedName: PROC [udp: ArpaNameUDPBuf.Buffer, p: Pointer] RETURNS [rope: ROPE] = { ptr: CARDINAL _ p.ptr; indirect: CARDINAL _ 300B; rope _ NIL; DO bytes: CARDINAL; IF ptr >= ArpaNameUDPBuf.maxBodyBytes THEN SIGNAL EndOfData; bytes _ udp.body.bytes[ptr]; ptr _ ptr + 1; IF bytes = 0 THEN EXIT; IF bytes >= indirect THEN { -- Indirect link, keep length we have temp: CARDINAL; IF ptr >= ArpaNameUDPBuf.maxBodyBytes THEN SIGNAL EndOfData; temp _ (bytes-indirect)*256 + udp.body.bytes[ptr]; temp _ temp - ArpaNameUDPBuf.hdrBytes; ptr _ ptr + 1; p.ptr _ temp; rope _ Rope.Concat[rope, GetCompressedName[udp, p]]; EXIT; }; FOR i: CARDINAL IN [0..bytes) DO IF ptr >= ArpaNameUDPBuf.maxBodyBytes THEN SIGNAL EndOfData; rope _ Rope.Concat[rope, Rope.FromChar[LOOPHOLE[udp.body.bytes[ptr]]]]; ptr _ ptr + 1; ENDLOOP; IF ptr >= ArpaNameUDPBuf.maxBodyBytes THEN SIGNAL EndOfData; IF udp.body.bytes[ptr] # 0 THEN rope _ Rope.Concat[rope, "."]; ENDLOOP; p.ptr _ ptr; }; longTime: LONG CARDINAL = 10*365*86400; GetTtl: PROC [ udp: ArpaNameUDPBuf.Buffer, p: Pointer] RETURNS [INT] = { card: LONG CARDINAL _ GetCard[udp, p]; IF card > longTime THEN card _ longTime; RETURN[card]; }; GetCard: PROC [ udp: ArpaNameUDPBuf.Buffer, p: Pointer] RETURNS [LONG CARDINAL] = { ln: Basics.LongNumber; ln.hi _ GetCardinal[udp, p]; ln.lo _ GetCardinal[udp, p]; RETURN[ln.lc]; }; GetCardinal: PROC [ udp: ArpaNameUDPBuf.Buffer, p: Pointer] RETURNS [CARDINAL] = { RETURN[GetTwoBytes[udp, p]]; }; GetTwoBytes: PROC [ udp: ArpaNameUDPBuf.Buffer, p: Pointer] RETURNS [UNSPECIFIED] = { ptr: CARDINAL _ p.ptr; temp: Basics.BytePair; IF ptr +1 >= ArpaNameUDPBuf.maxBodyBytes THEN SIGNAL EndOfData; temp.high _ udp.body.bytes[ptr]; temp.low _ udp.body.bytes[ptr+1]; p.ptr _ p.ptr + 2; RETURN[LOOPHOLE[temp]]; }; GetIPAddress: PROC [ udp: ArpaNameUDPBuf.Buffer, p: Pointer] RETURNS [a: Arpa.Address] = { ptr: CARDINAL _ p.ptr; IF ptr + 3 >= ArpaNameUDPBuf.maxBodyBytes THEN SIGNAL EndOfData; a.a _ udp.body.bytes[ptr+0]; a.b _ udp.body.bytes[ptr+1]; a.c _ udp.body.bytes[ptr+2]; a.d _ udp.body.bytes[ptr+3]; p.ptr _ p.ptr + 4; }; GetOneByte: PROC [ udp: ArpaNameUDPBuf.Buffer, p: Pointer] RETURNS [UNSPECIFIED] = { ptr: CARDINAL _ p.ptr; temp: Basics.BytePair; IF ptr >= ArpaNameUDPBuf.maxBodyBytes THEN SIGNAL EndOfData; temp.high _ 0; temp.low _ udp.body.bytes[ptr]; p.ptr _ p.ptr + 1; RETURN[LOOPHOLE[temp]]; }; GetChar: PROC [ udp: ArpaNameUDPBuf.Buffer, p: Pointer] RETURNS [c: CHAR] = { ptr: CARDINAL _ p.ptr; IF ptr >= ArpaNameUDPBuf.maxBodyBytes THEN SIGNAL EndOfData; c _ LOOPHOLE[udp.body.bytes[ptr]]; p.ptr _ p.ptr + 1; RETURN[c]; }; GetRope: PROC [ udp: ArpaNameUDPBuf.Buffer, p: Pointer] RETURNS [rope: Rope.ROPE_NIL] = { chars: CARDINAL _ GetOneByte[udp, p]; rope _ NIL; FOR i: INT IN [0..chars) DO rope _ Rope.Concat[rope, Rope.FromChar[GetChar[udp, p]]]; ENDLOOP; RETURN[rope]; }; END. –ArpaNameUDPImpl.mesa John Larson, October 22, 1987 3:53:05 pm PDT Codes: $destUnreachable : raised by ArpaUDP. Raised when no more udp data Κ—˜šœ™Icodešœ,™,—J˜šΟk ˜ Jšœœ˜"Jšœ ˜ Jšœ˜JšœœŠ˜—Jšœ œ˜ Jšœœ+˜7Jšœ œ0˜?šœœ)œ ˜AJ˜——šΟnœœ˜Jšœ!˜(Jšœ˜Jš œ ˜J˜šœœœ˜J˜—J˜šžœ œœœ˜(™K™%K˜——J˜Jšž œœœ˜J™J™J™Jšœœ˜J˜J˜Jšœ œœ ˜šœ œœ˜Jšœœ˜Jšœ˜J˜—J˜J˜šΠbnœœœ˜Jšœ)˜)Jšœ œœ˜Jšœ˜J˜Jšœœœ˜Jšœ$˜$Jšœ'˜'Jšœœ˜Jš œœœœœ˜;JšœœœœG˜^Jšœ˜Jšœœ)˜5Jšœœœ˜Jšœ˜Jšœ˜Jšœ#˜#Jšœ$˜$Jšœœ˜Jšœ‚˜‚Jšœ œœœ˜J˜Jšœ'˜'Kšœœ ˜-K˜Icode2šœ˜Lšœ˜Lšœ ˜ Lšœ!œ˜'Lšœœ˜#Lšœ-˜-Lšœœ˜$Lšœ˜Lšœ˜Lšœ˜Lšœ˜Lšœ˜J˜š œœœ œ˜+Lšœ6˜6Lšœ@˜@Jšœ)˜)Lšœ˜šœœ$œ˜7Jšœ:˜:JšœœœA˜\JšœG˜GKšœœ˜/Jšœ œ˜Jšœ˜Jšœ"˜"Jšœ˜Jšœœ˜5Jšœ˜Lšœ)˜)Jšœœ˜ Jšœ˜—Jšœ˜—Jšœ œœ!˜7Jš œœœ œ œ˜EJšœ˜Jšœ˜Jšœ˜J˜—šžœœ0˜IJšœ œ ˜Jšœ ˜ Jšœ˜Jšœ˜Jšœ ˜ Jšœ!˜!J˜J˜—J˜šž œœ<˜MJšœœœœ˜1Jšœœ#˜8šœœœ˜&Jšœœ˜Jšœ#˜#Jšœ œ&˜2Jšœ œ&˜3Jšœ˜Jšœ!˜!Jšœ˜—šœ˜Jšœœ˜—Jšœ˜J˜J˜—šž œœ<˜OJšœœ$˜7JšœB˜BJ˜J˜—šžœœ<˜RJšœœ$˜9JšœD˜DJ˜J˜J˜—šžœœ<˜SJšœœ$˜:JšœE˜EJ˜J˜J˜—š žœœ œ;œ œ˜}Jšœœœœ˜1šœœœ ˜Jšœœ˜'Jšœœ&˜8Jšœ%˜%Jšœ˜Jšœ˜Jšœœ&˜8Jšœ˜Jšœ(˜(J˜šœœ˜Jšœ.˜.Jšœ6˜6Jšœ8˜8Jšœ4˜4Jšœ2˜2Jšœ2˜2Jšœ2˜2Jšœ2˜2Jšœ2˜2Jšœ6˜6šœ˜Jšœ4˜4Jšœ4˜4Jšœ#˜#Jšœ$˜$Jšœ"˜"Jšœ#˜#Jšœ$˜$—šœ˜Jšœœ œœ˜)Jšœœ˜Jšœœ˜Jšœ)˜)Jšœ(˜(š œœœœΟc˜=Jšœœ˜(šœœœ˜šœ œ˜J˜Jšœ˜Jšœ˜—J˜J˜Jšœ˜—Jšœ˜—Jšœ˜Jšœœ˜0šœœœ ˜Jšœ&˜&Jšœ˜—Jšœ˜—šœ˜Jšœ#˜#Jšœ"˜"—šœ˜Jšœ1˜1Jšœ1˜1—šœ ˜ Jšœ*˜*Jšœ+˜+—Jšœ) ˜?—Jšœ˜Jšœ˜Jšœ˜—šœ˜Jšœœ˜—Jšœ˜J˜J˜—šžœœœ˜6šœ˜šœ˜Jšœœ ˜Jšœœ ˜Jšœ œ˜Jšœœ ˜Jšœœ ˜Jšœœ ˜Jšœœ ˜Jšœœ ˜Jšœœ ˜Jšœœ˜Jšœœ ˜Jšœœ ˜Jšœ œ˜Jšœ œ˜Jšœœ ˜Jšœœ˜Jšœ˜——J˜Jšœ˜J˜J˜—J™šž œœ˜Jšœ#œœ œ˜eJšœ œ ˜Jšœ ˜ Jšœ˜Jšœœ ˜'Jšœœ ˜(Jšœ)˜)Jšœ˜Jšœ˜J˜—šž œœ˜Jšœ"œ˜8š˜Jšœœ˜ Jšœ œœ˜Jšœ œœ ˜%Jšœ2˜2Jšœ˜Jšœ˜—Jšœœ˜;Jšœœ˜J˜—šžœœ˜Jšœ"œ˜8Jšœœ ˜Jšœœ˜$Jšœ˜šœœœ ˜ Jšœœ˜8Jšœ˜—Jšœ˜J˜—šžœœ˜Jšœ" œ˜?Jšœœ ˜Jšœ,˜,Jšœ-˜-Jšœ˜J˜—šžœœ˜JšœC˜CJšœœ ˜Jšœ"˜"Jšœ"˜"Jšœ"˜"Jšœ"˜"Jšœ˜J˜—šžœœ*˜AJšœœ˜Jšœœ ˜Jšœ œ˜Jšœœ˜ š˜Jšœœ˜Jšœ$œœ ˜Jšœ˜—Jšœ˜J˜—Jšœ œœ˜'šžœœ˜Jšœ(œœ˜9Jšœœœ˜&Jšœœ˜(Jšœ ˜J˜—šžœœ˜Jšœ(œœœ˜CJšœ˜Jšœ˜Jšœ˜Jšœ ˜J˜—šž œœ˜Jšœ(œœ˜>Jšœ˜J˜—šž œœ˜Jšœ(œ œ˜BJšœœ ˜Jšœ˜Jšœ'œœ ˜?Jšœ ˜ Jšœ!˜!Jšœ˜Jšœœ ˜J˜—šž œœ˜Jšœ(œ˜FJšœœ ˜Jšœ(œœ ˜@Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜J˜—šž œœ˜Jšœ(œ œ˜BJšœœ ˜Jšœ˜Jšœ%œœ ˜=Jšœ˜Jšœ˜Jšœ˜Jšœœ ˜J˜—šžœœ˜Jšœ(œœ˜>Jšœœ ˜Jšœ%œœ ˜=Jšœœ˜"Jšœ˜Jšœ˜ J˜—J˜šžœœ˜Jšœ(œ œœ˜JJšœœ˜&Jšœœœ˜ šœœœ ˜Jšœ9˜9Jšœ˜—Jšœ ˜J˜J˜J˜—Jšœ˜J˜J˜J˜—J˜—…—-Τ=