Vorlufige Programmier-Doku zu IConnect
--------------------------------------


Sven Kopacz 96/97/98


Gtz Hoffart arbeitet an der Umsetzung zum
Hypertext.


Allgemeine Hinweise:

Die offensichtlichsten Unterschiede zu Unix-sockets sind folgende Funktionen, 
die im Gegensatz zum Original ein s vor dem Namen haben:
sread
swrite
sfcntl
sclose

Diese Implementierung stimmt die socket-handles nicht mit den file-handles des 
Gemdos ab. Der Grund dafr ist vor allem, da diese Implementierung eine 
dynamische Speicherverwaltung fr alle Protokollbelange einsetzt. Normalerweise 
mu man fr grere zu bertragende Datenmengen u.U. einigen Aufwand alleine 
in die send und receive-Calls fr die Abfrage der bernommenen Datenmenge sowie 
die Verwaltung der programmeigenen Buffer (setsockopt) stecken.
Das ist hier alles nicht ntig, weil bei ausreichendem Speicher z.B. send() 
grundstzlich alle Daten bernimmt, ansonsten mit ENSMEM zurckkehrt.
recv lscht auerdem nicht die noch anstehenden Daten, falls der bergebene 
Empfangspuffer nicht gro genug ist.

Fr die dynamische Speicherverwaltung hlt in dieser Implementierung der Kernel-
Server her, das ist hier ICONNECT.PRG



Zu den Funktionen:

-Unix-Funktionen-
Die Fehlercodes befinden sich in ATARIERR.H, sofern es sich um bekannte
Gemdos-Fehler handelt. Alle anderen befinden sich in SOCKERR.H.

Welche Include-Dateien sonst notwendig sind, habe ich hier nicht aufgefhrt, da 
mu man entweder einfach alle includen oder selbst nachschauen. Das kommt aber 
in den geplanten Hypertext.

Der socket-owner wird ber den Prozedeskriptor bestimmt, d.h. ein Proze
hat immer nur Zugriff auf seine eigenen Sockets.

Selbst beantragte Ports (bind) drfen einen beliebigen int-Wert haben, d.h. es
gibt hier keine Beschrnkung der Ports < 1025 auf Prozesse mit bestimmtem
Status (einfach weil es auf dem Atari keinen solchen Status gibt).
Vom Kernel zugeteilte Ports liegen im Bereich von 1025-4999. Das kann aber 
vom Server verndert werden (ebenso wie ettliche TCP-Parameter und anderes
Zeugs). Man sollte sich also nicht auf Default-Werte verlassen sondern falls 
ntig diejenigen Parameter auf bestimmte Werte prfen, die in IConf gendert
werden knnen.


-Spezielle Atari-Funktionen-
(In diesem Text kommt man am schnellsten zu den speziellen Funktionen, wenn 
man im Editor oder Viewer nach "***ATARI***" suchen lt.)
Es gibt einige Funktionen, die nur in dieser Implementierung (nicht unter
Unix oder sonstwo) verfgbar sind. Dabei handelt es sich zum einen um
Hilfsfunktionen, die man immer mal brauchen kann (zerlegen einer URL) und
um Funktionen zur Abfrage der User-Konfiguration (USIS). BITTE diese 
Funktionen IMMER benutzen, bevor man den User in irgendwelchen Dialogen
nach der IP fr einen Proxy oder sonstwas fragt, was bereits in ICONF
eingstellt werden kann!


Wer Fehler entdeckt oder sonst irgendwas loswerden mchte, sei es da ein 
Fehlercode nicht korrekt ist oder was auch immer, der kann mich 
natrlich jederzeit per PM erreichen.


  /* interna */
  ***ATARI***
int sock_init(void)

 Diese Funktion mu auf jeden Fall zu Programmbeginn aufgerufen werden, 
 genauer: bevor irgendeine der folgenden Funktionen aufgerufen wird.
 Sie liefert entweder
 SE_NINSTALL=SOCKET.PRG ist nicht installiert.
 SE_NSUPP=SOCKET.PRG ist zu alt und wird von dieser Lib nicht untersttzt.

 E_OK=weitermachen, alles ok

 Bei SE_NINSTALL sollte man den User darauf hinweisen, da er SOCKET.PRG
 in den AUTO-Ordner legen mu.

 Die Funktion ist Atari-spezifisch.
 

  /* sockets */
int socket(int af, int type, int protocol)
	int af: 	AF_INET untersttzt, andere liefern EAFNOSUPPORT
	int type:	SOCK_STREAM und SOCK_DGRAM untersttzt, andere
						liefern ESOCKTNOSUPPORT
	int	protocol:	fr SOCK_STREAM: IPPROTO_IP oder IPPROTO_TCP, andere
																 liefern EPROTONOSUPPORT
								fr SOCK_DGRAM:  IPPROTO_IP oder IPPROTO_UDP, andere
																 liefern EPROTONOSUPPORT

  Legt einen neuen socket an.
  Ist kein Handle mehr verfgbar, wird ENHNDL geliefert.
	Ist kein Speicher mehr verfgbar, wird ENSMEM geliefert, ansonsten
	das socket-handle.
  (Alle handles liegen zwischen 1 und FD_SETSIZE, s. TYPES.H und select).


int bind(int s, const void *addr, int addrlen)
	int s:				von socket() geliefertes Handle, andere liefern ENOTSOCK
	void *addr:		Zeiger auf Adress-Struktur, darf nur sockaddr_in sein
	int addrlen:	darf nur sizeof(sockaddr_in) sein, andere liefern EINVAL

  Legt die lokale Adresse (bestehend aus IP und port) des sockets fest.
  Ist sin_port<=0, wird ein port vom Kernel zugewiesen.
  sin_addr wird ignoriert, da es ohnehin nur die lokale IP sein kann.
	Wurde fr den socket ein Shutdown veranlat, wird EINVAL geliefert.
	Ist der socket nicht vom Typ SOCK_STREAM oder SOCK_DGRAM (was z.Zt.
  berhaupt nicht mglich ist) wird EOPNOTSUPP geliefert.
	Ist der socket bereits an einen port gebunden, wird EINVAL geliefert.
	Fr SOCK_STREAM sockets wird ansonsten E_OK geliefert.
  Ist der socket vom Typ SOCK_DGRAM:
  Ist der ber addr angeforderte port bereits von einem anderen socket belegt, 
  wird EADDRINUSE geliefert.
  Ist kein Speicher mehr verfgbar, wird ENSMEM geliefert.
  Ist kein UDP-Port mehr verfgbar, wird ENHNDL geliefert.
	Ansonsten wird E_OK geliefert.


int listen(int s, int backlog)
	int s:				von socket() geliefertes Handle, andere liefern ENOTSOCK
	int backlog:	Anzahl der maximal entgegenzunehmenden connections auf diesem
								socket. Wert mu zwischen 1 und einem (festen) Maximal-
								Wert liegen. Dieser Wert kann vom Kernel-Server verndert 
								werden (Option in ICONF, Seite TCP), Default ist 16.

	Bereitet den socket vor, um auf TCP-Ebene connections entgegenzunehmen.
	Wurde noch kein lokaler port zugewiesen (bind), wird das nun vom Kernel
	erledigt (was idR natrlich nicht erwnscht ist, deshalb vorher bind() 
	benutzen).
	Liegt backlog auerhalb des zulssigen Wertebereichs, wird backlog auf die
	obere/untere Grenze gesetzt (ohne Rckmeldung an den Proze).
	Ist der socket nicht vom Typ SOCK_STREAM, wird EOPNOTSUPP geliefert.
	Besteht bereits eine connection auf diesem socket wird EINVAL geliefert.
	Wurde fr den socket ein Shutdown veranlat, wird EINVAL geliefert.
	Wurde auf diesem socket bereits eine connection initiiert, wird EINPROGRESS
	geliefert.
	Kam die initiierte connection eben zustande, wird EISCONN geliefert.
	Ist kein Speicher mehr verfgbar, wird ENSMEM geliefert.
	
	Wurde (unabhngig von backlog) mindestens ein tcp-port fr den Empfang
	vorbereitet, wird E_OK geliefert.
	
	
int accept(int s, const void *addr, int *addrlen)
	int s:				von socket() geliefertes Handle, andere liefern ENOTSOCK
	void *addr:		Zeiger auf Adress-Struktur, darf nur sockaddr_in sein
	int addrlen:	darf nur sizeof(sockaddr_in) sein, andere liefern EINVAL

  Nimmt eine auf einem listen-socket zustande gekommene connection in einem
  neuen socket entgegen.
	Ist der socket nicht vom Typ SOCK_STREAM, wird EOPNOTSUPP geliefert.
	Wurde fr den socket ein Shutdown veranlat, wird EINVAL geliefert.
	Wurde fr den socket kein listen veranlat, wird EINVAL geliefert.
	Steht keine connection bereit und ist O_NDELAY oder O_NONBLOCK gesetzt,
	wird EWOULDBLOCK geliefert. Ansonsten kehrt accept erst zurck, wenn
	auf TCP-Ebene eine connection aufgebaut wurde (sich also ein Client gemeldet 
	hat).
	Ist kein Handle fr einen neuen Socket mehr verfgbar, wird ENHNDL
	geliefert.
	Terminiert whrend des Wartens der Kernel-Server, wird ENETDOWN geliefert.
	Ist kein Speicher mehr verfgbar, wird ENSMEM geliefert.
	Ansonsten wird das Handle des neuen sockets geliefert, addr wird mit den
	Daten des Remote Host gefllt. (Hinweis: addr und damit der Speicher fr 
	diese Struktur mu vom Aufrufer zur Verfgung gestellt werden).
	
		
int connect(int s, const void *addr, int addrlen)
	int s:				von socket() geliefertes Handle, andere liefern ENOTSOCK
	void *addr:		Zeiger auf Adress-Struktur (darf nur sockaddr_in sein) oder
								NULL
	int addrlen:	darf nur sizeof(sockaddr_in) sein, andere liefern EINVAL

  Veranlat eine Verbindung zum Remote Host addr.
	Wurde fr den socket ein Shutdown veranlat, wird EINVAL geliefert.
	Wurde fr den socket ein listen veranlat, wird EINVAL geliefert.
  Ist der socket vom Typ SOCK_DGRAM, wird ein evtl. bereits zugewiesener
  Ziel-port freigegeben. Ist addr nicht NULL, wird der neue Ziel-port
  zugewiesen.
  (Der socket akzeptiert dann nur Nachrichten von/an den Ziel-port).
  connect() liefert dann E_OK.
  Ist der socket vom Typ SOCK_STREAM:
  Ist addr NULL, wird EINVAL geliefert.
  Wurde bereits eine connection initiiert, wird EALREADY geliefert.
  Besteht bereits eine connection, wird EISCONN geliefert.
  Besteht bereits eine identische Verbindung auf einem anderen socket, wird
  EADDRINUSE geliefert.
  Ist kein Speicher mehr verfgbar, wird ENSMEM geliefert.
  Ist kein Kernel-Server aktiv (d.h. es ist keine Schnittstelle zugewiesen),
  wird ENETDOWN geliefert.
  
  Bestand auf diesem socket bereits eine Verbindung, die aber inzwischen
  vom Remote Host abgebrochen wurde, wird ECONNRESET geliefert. (Um die
  Verbindung wieder aufzunehmen, mu ein neuer socket angelegt werden).
  Wurde fr den socket O_NDELAY oder O_NONBLOCK veranlat, wird EINPROGRESS
  geliefert. D.h. es wird nun versucht die Verbindung aufzubauen, die 
  Kontrolle kehrt aber zum aufrufenden Prozess zurck.
  
  Ansonsten wird die Verbindung aufgebaut. Kommt es zu einem Timeout, wird
  ETIMEDOUT geliefert. Wurde die Verbindung vom Remote Host abgelehnt, wird
  ECONNREFUSED geliefert.
	Terminiert whrend des Wartens der Kernel-Server, wird ENETDOWN geliefert.
  Kam die Verbindung zustande, wird E_OK geliefert.
  
  
int swrite(int s, const void *msg, int len)
	=send(s, msg, len, 0)


int send(int s, const void *msg, int len, int flags)
	int s:				von socket() geliefertes Handle, andere liefern ENOTSOCK
	void *msg:		Zeiger auf die zu versendende Nachricht
	int	len:			Lnge der Nachricht in Bytes, ist len < 0 wird EINVAL geliefert
	int	flags:		Es wird nur MSG_OOB untersttzt, andere liefern EOPNOTSUPP.

  Schickt msg ber den socket.
	
	MSG_OOB lst auf TCP-Ebene das Setzen des urgent-pointers aus, falls der
	socket vom Typ SOCK_STREAM ist, ansonsten wird MSG_OOB ignoriert.
	
	Wurde ein shutdown > 0 veranlat, wird EPIPE geliefert.

	Ist der socket vom Typ SOCK_DGRAM und kein Zielport zugewiesen, wird
	EDESTADDRREQ geliefert. Ansonsten wird sendto() geliefert.
	
	Ist der socket vom Typ SOCK_STREAM:
	Besteht keine connection, wird ENOTCONN geliefert.
	Wird die Verbindung gerade beendet, wird EPIPE geliefert.
	Wurde bereits ein Shutdown veranlat (aber auf TCP-Ebene noch nicht aus-
	gefhrt) wird ESHUTDOWN geliefert.
	Ist kein Speicher mehr verfgbar, wird ENSMEM geliefert.
	Ansonsten wird die Anzahl der bernommenen Bytes geliefert (diese landen
	zunchst in der Transmission-queue). Diese Anzahl ist wegen der internen 
	dynamischen Speicherverwaltung garantiert die Anzahl der bergebenen Bytes.
		

int sendto(int s, const void *msg, int len, int flags, void *to, int tolen)
	int s:				von socket() geliefertes Handle, andere liefern ENOTSOCK
	void *msg:		Zeiger auf die zu versendende Nachricht
	int	len:			Lnge der Nachricht in Bytes, ist len < 0 wird EINVAL geliefert
	int	flags:		Es wird nur MSG_OOB untersttzt, andere liefern EOPNOTSUPP.
	void *to:			Zeiger auf Adress-Struktur, darf nur sockaddr_in sein
	int tolen:		darf nur sizeof(sockaddr_in) sein, andere liefern EINVAL
	
  Schickt msg an to ber den socket.

	Ist der socket vom Typ SOCK_STREAM, wird to ignoriert und send() geliefert.
	
	Ist der socket vom Typ SOCK_DGRAM:
	Ist dem socket bereits ein Ziel-port zugewiesen und ist dieser nicht
	identisch mit to, wird EISCONN geliefert.
	Ist dem socket kein lokaler port zugewiesen, wird das nun erledigt. Ist kein
	port mehr verfgbar, wird ENHNDL geliefert.	
  Ist kein Kernel-Server aktiv (d.h. es ist keine Schnittstelle zugewiesen),
  wird ENETDOWN geliefert.
	Ist kein Speicher mehr verfgbar, wird ENSMEM geliefert.
	Ansonsten wird die Anzahl der gesendeten Bytes geliefert, wobei das gleiche 
	wie unter send() gilt.

 
long sread(int s, void *buf, long len)
	=recv(s, buf, len, 0)


long recv(int s, void *buf, long len, int flags)
	int s:				von socket() geliefertes Handle, andere liefern ENOTSOCK
	void *buf:		Zeiger auf den Empfangsbuffer
	long len: 		Lnge des Buffers in Bytes, ist len < 0 wird EINVAL geliefert
	int	flags:		Es wird nur MSG_PEEK untersttzt, andere liefern EOPNOTSUPP.
                Ist MSG_PEEK gesetzt, werden die gelieferten Daten als nicht
                gelesen betrachtet und beim nchsten sread/recv/recvfrom
                erneut geliefert.

  Liest den Empfangsbuffer des sockets aus.
                
  Ist der socket vom Typ SOCK_DGRAM, wird recvfrom(...,NULL,NULL) geliefert.
  
  Ist der socket vom Typ SOCK_STREAM:
  Besteht keine connection, oder ist eine angeforderte connection noch nicht
  aufgebaut, wird ENOTCONN geliefert.
  Hat der Remote Host bereits die bertragung von seiner Seite beendet (nicht
  die connection!), wird ECONNRESET geliefert.
  Wurde ein Empfangs-shutdown ausgelst, wird 0 geliefert.
  Wurde ein Sende-shutdown ausgelst und die Gegenstelle hat ebenfalls
  begonnen, die Verbindung abzubauen, wird ESHUTDOWN geliefert.
  Wurde O_NONBLOCK gesetzt und liegen keine Daten im Empfangspuffer, wird
  EWOULDBLOCK geliefert.
  Wurde O_NDELAY gesetzt und liegen keine Daten im Empfangspuffer, wird
  0 geliefert.
  Ansonsten kehrt die Routine erst zurck, wenn Daten empfangen wurden.
	Terminiert whrend des Wartens der Kernel-Server, wird ENETDOWN geliefert.
  Zurckgegeben wird die Anzahl der in buf kopierten Bytes.

  Achtung: Auch bei SOCK_STREAM-sockets werden empfangene Daten, die nicht
  mehr in den per buf angegebenen Puffer passen, NICHT gelscht. Ich sag das 
  nur, weil einige Unix-Systeme das anders machen, die lschen dann den Rest.


long recvfrom(int s, void *buf, long len, int flags, void *from, int *fromlen); 
	int s:				von socket() geliefertes Handle, andere liefern ENOTSOCK
	void *buf:		Zeiger auf den Empfangsbuffer
	long len: 		Lnge des Buffers in Bytes, ist len < 0 wird EINVAL geliefert
	int	flags:		Es wird nur MSG_PEEK untersttzt, andere liefern EOPNOTSUPP.
                Ist MSG_PEEK gesetzt, werden die gelieferten Daten als nicht
                gelesen betrachtet und beim nchsten sread/recv/recvfrom
                erneut geliefert.
  void *from:   Zeiger auf eine vom Aufrufer bereitgestellte Struktur zur
                Aufnahme der Absenderadresse. Es wird nur sockaddr_in
                untersttzt! Ist from=NULL, werden keine Absenderdaten kopiert.
  int *fromlen: Enthlt beim Aufruf die Gre des per from zur Verfgung
                gestellten Speicherbereichs. Wurden Absenderdaten nach from
                kopiert, enthlt fromlen nach dem Aufruf die Anzahl der
                kopierten Bytes (maximal sizeof(sockaddr_in) oder fromlen,
                falls dieses kleiner ist).

  Liest den Empfangsbuffer des sockets aus und fllt from mit den Absender-
  daten.
                
  Ist der socket vom Typ SOCK_STREAM, werden from und fromlen ignoriert und
  recv(...) geliefert.
  
  Ist der socket vom Typ SOCK_DGRAM:
  Wurde ein shutdown fr den Empfang ausgelst, wird 0 geliefert.
  Stehen keine Daten im Empfangspuffer bereit:
  -wird EWOULDBLOCK geliefert, falls O_NONBLOCK gesetzt ist.
  -wird 0 geliefert, falls O_NDELAY gesetzt ist.
  -wird auf eintreffende Daten gewartet, falls keines der Flags gesetzt ist.
  Ist from nicht NULL, werden die Absenderdaten in der Form sockaddr_in nach
  from kopiert.
  Ist der socket per connect an einen bestimmten Absender gebunden worden und
  entspricht dieser nicht dem Absender der empfangenen Daten, wird 0 geliefert.
  Ansonsten wird die Anzahl der nach buf kopierten Bytes geliefert.
 

int select(int nfds, fd_set  *readlist,  fd_set *writelist, fd_set *exceptlist, timeval *TimeOut); 
  int nfds:           Lnge des Bitsrings (in BIT)
  fd_set *readlist:   Zeiger auf Bitstring oder NULL
  fd_set *writelist:  Zeiger auf Bitstring oder NULL
  fd_set *exceptlist: Zeiger auf Bitstring oder NULL
  timeval *TimeOut:   Zeiger auf Zeitangabe fr Timeout oder NULL (kein Timeout)
  
  Ist nfds grer FD_SETSIZE (in types.h als 2048 definiert und damit auch
  compiliert, umdefinieren bringt also nichts), wird EINVAL geliefert.
  Die Bitstrings geben an, welche sockets geprft werden sollen. Dazu wird
  das n-te Bit auf 1 gesetzt, wenn der Socket mit Handle n geprft werden soll.
  Dabei ist das 0-te Bit dasjenige, auf das der Zeiger zeigt. Grere Bitnummern
  stehen demzufolge bei greren Speicheradressen.
  
  Ist readlist nicht NULL, werden alle in readlist gewhlten sockets auf 
  vorliegende Daten im Empfangspuffer geprft.
  
  Ist writelist nicht NULL, werden alle in writelist gewhlten sockets auf
  Bereitschaft zum Senden geprft. sockets vom Typ SOCK_DGRAM liefern hier
  immer ein positives Ergebnis (Schnittstellenprobleme u.. werden erst bei
  einem tatschlichen Sendeversuch erkannt, s. sendto).
  sockets vom Typ SOCK_STREAM liefern nur ein positives Ergebnis, wenn sich 
  die Verbindung auf TCP-Ebene in einem sendefhigen Zustand befindet. 
  
  Ist exceptlist nicht NULL, werden alle in exceptlist gewhlten sockets auf
  eine vorhandene Ausnahmebedingung geprft. Eine Ausnahmebedingung wird z.Zt.
  nur auf sockets vom Typ SOCK_STREAM anerkannt, und zwar genau dann, wenn der
  entfernte Host auf TCP-Ebene PSH oder URG (Push-Flag/Urgent-Pointer)
  signalisiert hat.
  
  Ist Timeout NULL, kehrt select erst zurck, wenn mindestens einer der zu
  prfenden Zustnde eingetreten ist oder wenn keiner der Zustnde eintreten
  kann.
  Ansonsten kehrt select sptestens nach der in TimeOut angegeben Zeit zurck.
  Hinweis: timeval (in TYPEDEF.H) lst in MIKROsekunden auf, diese select-
           Implementierung jedoch nur in Einheiten von 100 MILLIsekunden, d.h.
           die Eingabe wird aufgerundet.
  
  select liefert die Anzahl der eingetretenen Zustnde zurck. Ist diese
  Anzahl 0, dann sind die Bitstrings unverndert. Ansonsten sind nur noch
  diejenigen Bits auf 1 gesetzt, fr die der gefragte Zustand eingetreten ist.
  Eine zurckgegebene 0 bedeutet entweder einen Timeout, oder da keiner der 
  gefragten Zustnde mehr eintreten kann (wartet man z.B. auf read an einem TCP-
  socket und der Remote Host lst einen connection reset aus, dann gibt es 
  nichts mehr zu lesen).
  
  In TYPEDEF.H befinden sich die Makros FD_SET, FD_CLR, FD_ISSET, FD_ZERO, mit
  denen man die Bits der Bitstrings bequem lschen, setzen und prfen kann.
  
  FD_SET(n,p)-setzen des n-ten Bits im String p (p ist Pointer auf den String)
  FD_CLR(n,p)-lschen
  FD_ISSET(n,p)-prfen (liefert 0 oder den Bitwert im entsprechenden long
                        also nicht unbedingt 1 (!))
  FD_ZERO(p)-lscht alle Bits des Strings p.
  

int shutdown(int s, int how);
	int s:				von socket() geliefertes Handle, andere liefern ENOTSOCK
  int how:		  Shutdown-Modus
  							0=Kein weiterer Empfang
  							1=Kein weiteres Senden
  							2=Kein weiteres Senden und Empfangen
                  andere Parameter liefern EINVAL

  Bereitet das Beenden einer Verbindung vor (Gegenstck zu connect).
                  
  Wird ein shutdown fr den Empfang ausgefhrt, fhren weiter sread/recv/
  recvfrom-calls fr diesen socket zur Rckgabe von 0 (=Keine Daten empfangen).
  
	Wird ein shutdown fr das Senden ausgefhrt, fhren weiter swrite/send/
	sendto-calls fr diesen socket zur Rckgabe von EPIPE.
	
  SOCK_DGRAM-sockets liefern E_OK.
  
  Fr SOCK_STREAM sockets:
  Ist how=0, wird E_OK geliefert und der socket fr weiteren Empfang gesperrt.
  Ist how>0 und wurde bereits ein Shutdown > 1 veranlat, wird ESHUTDOWN
  geliefert.
  Ansonsten wird je nach Status auf TCP-Ebene ein Disconnect initiiert, der
  zu folgenden Fehlern fhren kann:
  Ist kein Kernel-Server aktiv (d.h. es ist keine Schnittstelle zugewiesen),
  wird ENETDOWN geliefert.
  Ist kein Speicher verfgbar, wird ENSMEM geliefert
  Ansonsten wird E_OK geliefert. Zu diesem Zeitpunkt ist der Disconnect auf
  TCP-Ebene noch nicht abgeschlossen!

  Wurde ein socket fr Empfang und Senden gesperrt, kann nur noch ein sclose
  auf diesen socket ausgefhrt werden (alle anderen Operationen liefern ent-
  sprechende Fehler).

  
int sclose(int s); 
  int s:				von socket() geliefertes Handle, andere liefern ENOTSOCK

  Lscht den socket.

  Ist der socket vom Typ SOCK_DGRAM, werden alle Daten, die noch im Empfangs-
  puffer liegen gelscht, der lokale UDP-Port wird zur weiteren Verwendung
  freigegeben und es wird E_OK geliefert.

  Fr SOCK_STREAM sockets:
  Besteht auf TCP-Ebene eine Verbindung (oder wird gerade initiiert oder
  abgegbaut), so wird diese in Abhngigkeit von SO_LINGER behandlet, s. dazu
  setsockopt.
  Anschlieend wird der socket gelscht und E_OK geliefert.


  /* Misc */ 
int getsockname(int s, void *addr, int *addrlen)
  int s:				von socket() geliefertes Handle, andere liefern ENOTSOCK
  void *addr:   Vom Aufrufer zur Verfgung gestellter Speicherbereich
  int *addrlen: Gre in Bytes des Speicherbereichs
  
  getsockname liefert die lokale Adresse des sockets s in Form eines
  sockaddr_in structs. Dabei werden maximal addrlen Bytes nach addr kopiert.
  addrlen enthlt nach dem Aufruf die Anzahl der tatschlich kopierten Bytes
  und getsockname liefert E_OK.
  Es sollte also addr also auf ein sockaddr_in-struct zeigen und
  addrlen=sizeof(sockaddr_in) sein.
  Wurde fr den socket ein shutdown fr Empfang und Senden ausgelst, wird
  EINVAL geliefert.


int getsockopt(int s, int level, int optname, void *optval, int *optlen)
  int s:				von socket() geliefertes Handle, andere liefern ENOTSOCK
  int level:    Ebene, auf der die Option gesetzt werden soll.
  int optname:  Option
  void *optval: Zeiger auf den Optionswert
  int *optlen:  Gre in Byte des Optionswerts
ebenso:  
int setsockopt(int s, int level, int optname, void *optval, int *optlen)

 Mit setsockopt knnen Optionen fr einen socket gesetzt werden, mit
 getsockopt knnen die gesetzten Optionen erfragt werden.
 Es werden nur Optionen auf socket-Ebene, nicht auf Protokollebene unter-
 sttzt, d.h. wenn level nicht SOL_SOCKET ist, wird EOPNOTSUPP geliefert.
 blich ist unter bestimmten Umstnden den Nagle-Algorithmus auf TCP-Ebene
 ber setsocktop abzustellen, da dieser aber ohnehin nicht implementiert ist,
 gibt es auch die Option nicht.
 Wurde fr den socket ein Empfangs- und Sendeshutdown ausgelst, wird EINVAL
 geliefert.
 In optval wird bei getsockopt geschrieben (maximal optlen Bytes), bei 
 setsockopt gelesen. Je nach optname zeigt optval auf einen anderen Datentyp.
 optname:
 Boolsche Optionen (werden nur fr SOCK_STREAM sockets untersttzt, andere 
 liefern EOPNOTSUPP) haben fr set einen Integer als Datentyp, d.h. optval
 ist int* und *optlen ist sizeof(int). Der Wert des Integers mu ungleich
 0 sein, um die Option zu setzen. Ist der Wert 0 oder optval ein NULL-Pointer,
 wird die Option gelscht.
 Wird eine Boolsche Option mit get ermittelt, wird E_OK geliefert, wenn sie
 gesetzt ist, sonst ENOPROTOOPT. optval/optlen werden nicht benutzt.
 SO_DEBUG, SO_DONTROUTE, SO_KEEPALIVE, SO_BROADCAST, SO_USELOOPBACK und
 SO_OOBINLINE knnen gesetzt und gelesen werden, bewirken aber z.Zt. nichts. 
 SO_REUSEADDR wird ebenfalls ignoriert, steht aber intern immer auf TRUE.
 D.h. eine lokale TCP-Adresse kann mehrfach benutzt werden. Eine connection
 dagegen natrlich nicht. Dabei wird nicht zwischen verschiedenen Prozessen
 unterschieden.
 SO_LINGER:
  mit set setzen: optval zeigt auf linger-struct, optlen ist sizeof(linger)
  mit set lschen: optval ist NULL-Pointer
  Das linger-struct besteht aus l_onoff und l_linger. Damit wird sclose() auf
  einem SOCK_STREAM socket beeinflut.
  a)Ist SO_LINGER gesetzt und l_onoff nicht 0 und l_linger nicht 0, dann werden
  evtl. noch ausstehende Daten an den Remote host gesendet und die Verbindung
  abgebaut. Erst dann kehrt sclose() zurck (oder wenn die Verbindung abbricht).
  b)Ist l_onoff nicht 0 aber l_linger 0, wird die Verbindung abgebrochen (auf
  TCP-Ebene wird dazu ein Reset gesendet) und sclose() kehrt sofort zurck.
  Ist l_onoff 0 oder SO_LINGER nicht gesetzt, wird die Verbindung wie in a)
  abgebaut, sclose() kehrt aber sofort zurck.

  Achtung! Es wird nicht der Pointer vermerkt, sondern der Inhalt des structs
  kopiert, d.h. eine nderung der linger-Elemente ist nur ber ein erneutes
  setsockopt mglich (und nicht etwa durch einfaches ndern der Werte in dem
  struct, dessen Pointer bergeben wurde).  
  
 SO_SNDBUF und SO_RCVBUF (ndern der Puffergren)
 set: liefern immer E_OK, da die interne Speicherverwaltung vollstndig
      dynamisch ist.
 get: optval ist long*, optlen ist sizeof(long), geliefert wird in optval 
 			die Anzahl der Bytes in den internen TCP-Buffern, fr die noch keine 
 			Empfangsbesttigung eingetroffen ist.
			In bufsize wird auch dann 0 geliefert, wenn gar keine TCP-Verbindung 
			besteht o.., die Funktion lt also keine Aussage ber den Zustand 
			der TCP-Connection zu.
 
 SO_SNDTIMEO, SO_RCVTIMEO, SO_SND_COPYAVOID und SO_RCV_COPYAVOID liefern
 EINVAL, da nicht untersttzt.
 
 Optionen, die nur mit get genutzt werden knnen (set liefert EINVAL):
 SO_ACCEPTCONN: prft, ob fr den socket ein listen() ausgefhrt wurde.
 Liefert E_OK wenn ja, ENOPROTOOPT wenn nein. optlen/optval unbenutzt.
 SO_TYPE: optval ist int*, optlen ist sizeof(int). Schreibt in optval den
 socket-Typ (SOCK_STREAM, SOCK_DGRAM) und liefert E_OK. Ungltiges optlen
 liefert EINVAL.
 SO_ERROR: optval ist int*, optlen ist sizeof(int). Schreibt in optval den
 zuletzt bei einer socket-Funktion (~alle oben aufgefhrten) aufgetretenen 
 Fehler, lscht diesen und liefert E_OK. Ungltiges optlen liefert EINVAL.
 SO_SNDLOAT, SO_RCVLOWAT (send/receive buffer low water mark): 
 optval ist long*, optlen ist sizeof(long). Beide Optionen schreiben immer 
 0 in optval und liefern E_OK (bzw. EINVAL fr ungltiges optlen).
 
  
long sfcntl(int FileDescriptor, long Command, long Argument); 
 	int FileDescriptor:  von socket() geliefertes Handle, andere liefern ENOTSOCK
  long Command:        F_GETFL oder F_SETFL, andere liefern EINVAL
  long Argument:       zu setzende Flags
  
  Argument wird bei F_GETFL ignoriert, sfcntl liefert die aktuell gesetzten
  Flags.
  ber F_SETFL knnen folgenden Flags gesetzt werden:
  O_NDELAY: socket-Funktionen kehren ohne Verzgerung zurck. Die Auswirkung
            ist bei den jeweiligen Funktionen dokumentiert, z.B. liefern
            Empfangsfunktionen bei leerem Buffer 0.
  O_NONBLOCK: socket-Funktionen werden nicht blockierend ausgefhrt. Die
              Auswirkung ist bei den jeweiligen Funktionen dokumentiert,
              z.B. liefern Empfangsfunktionen bei leerem Buffer EWOULDBLOCK.
              


  /* resolver */ 
void res_init(void);
 Mit dieser Funktion mu der Resolver vor der Benutzung initialisiert werden.
 Sollte eigentlich zum Auslesen von /etc/resolv.conf fhren, momentan passiert
 aber garnix, da weder Konfigurationsvariablen noch Cache-Listen noch sonstwas
 beachtet werden. Der interne resolver kann nichts weiter, als per UDP bei dem
 ber den Kernel-server eingetragenen DNS eine Anfrage durchfhren und
 Auswerten.

 Im Moment sind die Resolver-Funktionen nicht weiter dokumentiert, da sie
 IDR nur fr Spezialanwendungen bentigt werden. Normalerweise verwendet
 man einfach gethostbyname().
 Weitere Doku ist per "man <funktion>" auf den meisten Unix-Rechnern
 verfgbar.

 Hier sind zumindest die Prototypen der Resolver-Funktionen:

int res_query(char *dname, int class, int type, uchar *answer, int anslen); 
int res_search(char *dname, int class, int type, uchar *answer, int anslen); 
int res_mkquery(int op, char *dname, int class, int type, char *data, int datalen, void *notused, char *buf, int buflen); 
int res_send(char *msg, int msglen, char *answer, int anslen); 
int dn_expand(uchar *msg, uchar *eomorig, uchar *comp_dn, uchar *exp_dn, int length); 
int dn_comp(uchar *exp_dn, uchar *comp_dn, uchar **dnptrs, uchar **lastdnptr, int length); 

Hinweis: Verwendet man nur gethostbyname und keine Resolver-
Funktionen, mu man res_init() nicht extra aufrufen, darum kmmert sich die Lib.
 

/* DNS (Domain Name Server) Untersttzung */
Die hostent-Struktur (NETDB.H)
typedef struct
{
 char *h_name;
 char **h_aliases;
 int  h_addrtype;
 int  h_length;
 char **h_addr_list;
}hostent;
/* In einigen Unix-Implementationen enthlt hostent nur eine Adresse: */
#define h_addr h_addr_list[0];

h_name="Klartext"name des Hosts
h_aliases=NULL-terminiertes Array aus Pointern auf alternative host-Name(n)
h_addrtype=Der Typ der zurckgegebenen Adressen, immer AF_INET
h_length=Lnge in Bytes der Adressen, immer 4
h_addr_list=NULL-terminiertes Array aus Pointern auf die IP-Adresse(n) des 
hosts (als ulong!)


hostent  *gethostent(void); 
 Wenn die DNS-Anfragen untersttzt werden, wird immer NULL geliefert.


int  sethostent(int stayopen);
 Ist stayopen !=0, soll dies den Resolver zur Aufnahme einer TCP-Connection
 zum DNS veranlassen.
 Da dies nicht untersttzt wird, liefert sethostent bei stayopen!=0 den
 Fehler EOPNOTSUPP, ansonsten E_OK.
 
 
int endhostent(void); 
 Liefert E_OK. (Soll eigentlich die TCP-Connection schlieen)
 
 
hostent  *gethostbyname(char *name); 
 char *name:  Name des Hosts, dessen IP-Adresse ermittelt werden soll.
 
 gethostbyname veranlat eine Anfrage beim DNS (ber den Resolver) und
 liefert das Ergebnis in einer hostent-Struktur oder NULL, falls der
 Resolver einen Fehler meldet (ENETDOWN, ENSMEM etc.).
 Ist name die Textform einer IP-Adresse (also punktgetrennte Dezimalzahlen)
 wird das Ergebnis ohne Umweg ber den Resolver geliefert (h_aliases ist
 dann leer).
 
 Hinweis: Diese Funktion ist in der Lib enthalten und verwendet einen
 statischen Speicherbereich fr den zurckgegebenen Zeiger. Das ist 
 besonders dann zu beachten, wenn man per Thread o.. einen Proze startet,
 der auf die selbe Lib-Funktion zugreift! Es mu dann selbst sichergestellt
 werden, da dies nicht gleichzeitg mit dem Hauptprogramm geschieht, da sich
 die Aufrufe sonst gegenseitig den Speicherbereich berschreiben.
 Selbstverstndlich mu man auch das Ergebnis vor einem weiteren Aufruf der
 Funktion in eigenen Variablen sichern, falls man damit weiterarbeiten will.
 
 
hostent *gethostbyaddr(char *addr, int len, int type); 
 Z.Zt. nicht untersttzt, liefert daher immer NULL. Sollte eine umgekehrte
 DNS-Anfrage ausfhren (IP in, name out).


Wird vom Kernel-Server kein DNS eingetragen (d.h. 255.255.255.255 als DNS-IP), 
laufen o.g. Funktionen ber die Datei hosts. Diese liegt "normalerweise" (Unix) 
in etc/. Da es dieses Verzeichnis beim Atari nicht unbedingt gibt, wird der 
Pfad in IConf, Seite "Sockets" festgelegt. Der Ordner darf auch einen anderen 
Namen haben.
Dort liegen dann auch die Dateien networks, services, protocol und rpc.
(protocol heit sonst protocols, hat dann aber einen Buchstaben zuviel fr
 8+3 Systeme).
Auf die Eintrge kann ber jeweils hnliche Funktionen zugegriffen werden:
<typ>=host,net,serv,prot oder rpc

int set<typ>ent(int stayopen)
 int stayopen: Legt fest, ob die Datei geffnet bleiben soll (0=nein, sonst ja)
 
 ffnet die Datei und (oder wenn die Datei schon geffnet ist) setzt den 
 Dateizeiger auf den Dateianfang. Liefert entweder E_OK oder einen Gemdos-
 Error.

 
<typ>ent *get<typ>ent(void)
 Liefert die jeweils nchste Zeile zurck, wobei die Eintrge schon auf-
 geschlsselt in der <typ>ent-Struktur vorliegen. Fr diese Funktionen gilt
 der selbe Hinweis bzgl. statischem Speicher wie fr gethostent im DNS-
 Abschnitt. Hier gilt er sogar unabhngig von <typ>, d.h. ein Aufruf von
 z.B. getservent berschreibt das Ergebnis eines vorhergehenden z.B.
 getprotoent!!!
 War die Datei noch nicht geffnet, wird dies zuerst mit dem zuletzt gesetzten
 stayopen-Parameter durchgefhrt. Wurde set<typ>ent noch garnicht aufgerufen, 
 ist stayopen immer 0.
 Ist stayopen=0, wird die Datei nach diesem Aufruf geschlossen.
 Ist EOF erreicht oder kann die Datei nicht gelesen/geffnet werden, wird
 statt der jeweiligen Struktur ein NULL-Zeiger geliefert.


int end<typ>ent(void)
 Schliet die Datei, falls sie geffnet ist. Liefert immer E_OK.


<typ>ent get<typ>by<art1>(...)
<typ>ent get<typ>by<art2>(...)
 Diese beiden Funktionen sind die bequemsten, sie durchsuchen selbst-
 stndig die Datei nach einem gewnschten Eintrag und liefern einen
 Pointer auf die jeweilige Struktur zurck (oder NULL, falls kein passender
 Eintrag gefunden wurde oder die Datei nicht gelesen/geffnet werden konnte).
 Hinweise: Diese Funktionen beachten ebenfalls stayopen, schlieen also ggf.
 die Datei.
 In Abhngigkeit des Typs kann jeweils ber den einen oder anderen Parameter
 in den Dateien gesucht werden, die hier zuerst aufgefhrte Funktion ist die 
 in der Praxis jeweils gebruchlichere:
 <host>
 gethostbyname(const char *name)
 	char *name=Zeiger auf String mit dem Hostnamen, zu dem die IP gesucht wird.
 gethostbyaddr(const char *addr, int len, int type)
  char *addr=Zeiger auf IP-Adresse in 4 Byte Darstellung (KEIN String)
  int len=immer sizeof(unsigned long) (bzw. sizeof(in_addr))
  int type=immer AF_INET
  
 <net>
 getnetbyname(const char *name)
  char *name=Zeiger auf String mit dem Netznamen, zu dem die Nummer gesucht wird
 getnetbyaddr(int net, int type)
  int net=Netzwerknummer, zu der der Netzwerkname gesucht wird
  int type=immer AF_INET
  
 <serv>
 getservbyname(const char *name, const char *proto)
  char *name=Name des Services, zu dem der well-known-port gesucht wird
  char *proto=Name des gewnschten Protokolls (idR "tcp" oder "udp")
 getservbyport(int port, const char *proto)
  int port=Nummer des well-known-port, zu dem der Servicename gesucht wird
  char *proto=Name des gewnschten Protokolls (idR "tcp" oder "udp")
  
<proto>
 getprotobyname(const char *name)
  char *name=Name des Protokolls, dessen Nummer gesucht wird
 getprotobynumber(int proto)
  int proto=Nummer des Protokolls, zu dem der Name gesucht wird
  
<rpc>
 getrpcbyname(const char *name)
  char *name=Name der RPC-Funktion, deren Nummer gesucht wird
 getrpcbynumber(int number)
  int number=Nummer der RPC-Funktion, deren Name gesucht wird



/* host */
ulong gethostid(void); 
 Liefert die IP-Adresse des lokalen Hosts (also des eigenen Rechners) in
 4 Byte Form.
 
 
int    gethostname(char *name, int namelen); 
 char *name:    Speicherbereich, der vom Aufrufer zur Verfgung gestellt wird
 int namelen:   Gre des Speicherbereichs in Bytes
 
 Kopiert die IP-Adresse des lokalen Hosts als String, also als 4 Dezimal-
 zahlen mit Punkt-Trennung, nach name. Dabei werden maximal namelen Bytes
 kopiert. Bietet name nicht gengend Platz, bleibt der String unterminiert,
 ansonsten wird er nullterminiert (wie bei strncpy).
 gethostname liefert immer E_OK.




/* inet */

unsigned long inet_addr(char *cp);
 char *cp:  IP-Adresse als punktgetrennte Zahlen
            (anstelle von Dezimalzahlen sind auch Hex- und Oktalzahlen in
             der entsprechenden C-Notation zugelassen).
            Zulssig sind die Formate a.b.c.d, a.b.c und a.b
            
 inet_addr liefert die IP-Adresse in 4 Byte Darstellung zurck. Tritt bei
 der Umwandlung ein Fehler auf (ungltiger String), wird INADDR_NONE
 zurckgegeben (in IN.H als 0xffffffff definiert).
 Sind weniger als 4 Zahlen im String enthalten, wird die letzte auf 16 bzw.
 24 Bit erweitert.

unsigned long inet_network(char *cp);
 char *cp:  Netzwerkadresse als punkgetrennte Zahlen
            (anstelle von Dezimalzahlen sind auch Hex- und Oktalzahlen in
             der entsprechenden C-Notation zugelassen).

 Liefert hnlich inet_addr das Ergebnis als 4 Byte Darstellung zurck,
 erweitert die letzte Zahl aber nicht.


unsigned long inet_lnaof(struct in_addr in)
 in_addr in:   Hostadresse
 
 Gibt den lokalen Teil der Adresse <in> in 4 Byte Darstellung zurck
 

struct in_addr inet_makeaddr(ulong net, ulong host)
 ulong net:  Netzwerknummer
 ulong host: Lokale Hostadresse
 
 inet_makeaddr liefert die komplette Adresse von <host> zurck.


char *inet_ntoa(unsigned long in); 
 unsigned long in:	IP-Adresse in 4 Byte Darstellung
 
 inet_ntoa liefert einen Zeiger auf einen String, der die bergebene IP-
 Adresse in punktgetrennter Dezimaldarstellung enthlt.

 Hinweis: Diese Funktion ist in der Lib enthalten und verwendet einen
 statischen Speicherbereich fr den zurckgegebenen Zeiger. Das ist 
 besonders dann zu beachten, wenn man per Thread o.. einen Proze startet,
 der auf die selbe Lib-Funktion zugreift! Es mu dann selbst sichergestellt
 werden, da dies nicht gleichzeitg mit dem Hauptprogramm geschieht, da sich
 die Aufrufe sonst gegenseitig den Speicherbereich berschreiben.
 Selbstverstndlich mu man auch das Ergebnis vor einem weiteren Aufruf der
 Funktion in einer eigenen Variablen sichern, falls man damit weiterarbeiten
 will.



/* <a>to<b> */

Diese Funktionen werden benutzt, um die Bytefolge von Netzwerkadressen zwischen
Maschinenreihenfolge und Standardreihenfolge (big endian) zu konvertieren. Das 
ist auf Motorola-CPUs nicht besonders schwierig, man macht einfach garnix.
Die Makros sind in IN.H definiert.

ntohl	-	network to host, long
ntohs - network to host, short
htonl - host to network, long
htons - host to network, short




***ATARI***

Spezielle Funktionen fr diese Implementierung:


/* USIS (User setup information system) */

Mittels USIS kann man an Account-abhngige Einstellungen des Benutzers
gelangen. Diese Funktion sollte IMMER aufgerufen werden, bevor man den
Benutzer selbst nach den jeweiligen Parametern fragt.

int usis_query(USIS_REQUEST *ur)
Vor dem Aufruf ist *ur auszufllen. Dabei mu .request immer gesetzt sein,
die anderen Variablen je nach Anfragetyp

typedef struct
{
	int							request;
	unsigned long		ip;
	int							port;
	char						*result;
	char						*free1;
	char						*free2;
	void						*other;
}USIS_REQUEST;

usis_request liefert UA_FOUND, wenn ein Eintrag gefunden wurde, UA_NOTSET
wenn z.B. der User im Setup keinen Eintrag angegeben hat, UA_UNKNOWN wenn
der Anfragetyp (.request) unbekannt ist und UA_OFFLINE wenn der Kernel-
Server (der die usis-Funktion implementiert) gar nicht aktiv ist.
(Kommt kein UA_OFFLINE, heit das aber nicht, da der User sich tatschlich
eingewhlt hat. Es bedeutet lediglich, da der Server, also ICONNECT, aktiv
ist. Den Online-Status erfhrt man ber die Fehlercodes der Socket-
Funktionen oder UR_LOCAL_IP).

Anfragetypen fr .request:

UR_ETC_PATH:
  .result mu auf einen String zeigen, der mindestens die Lnge eines Gemdos-
  Pfads aufnehmen kann. In diesen String wird der Pfad kopiert, in dem sich
  die Dateien hosts, services etc.. befinden. Der Pfad wird immer mit einem
  Backslash abgeschlossen.
  USIS prft nicht, ob der Pfad tatschlich existiert!
  Wurde der Pfad ber eine Environment-Variable angegeben und diese nicht
  gefunden, wird UA_NOTSET geliefert.
 
UR_LOCAL_IP:
	In .ip wird die IP-Adresse des lokalen Rechners geschrieben. Besteht keine
	Verbindung (die Adresse ist dann 0.0.0.0) wird UA_NOTSET geliefert.

UR_REMOTE_IP:
	Wie UR_LOCAL_IP, es wird aber die Adresse des Rechners geliefert, mit dem
	man ber Modem verbunden ist. Das Ergebnis lt hier aber keinen Schlu ber
  den Online-Status zu, weil bei SLIP-Verbindungen (und PPP je nach Peer-
  Konfig) diese IP berhaupt nicht bekannt ist.
	
UR_DNS_IP:
	Es wird die Adresse des DNS geliefert.
	
UR_POP_IP, UR_SMTP_IP, UR_NEWS_IP, UR_TIME_IP:
  Falls der Benutzer den jeweilige Server angegeben hat, wird dessen IP
  in .ip zurckgeliefert, ansonsten UA_NOTSET.
    
UR_PROXY_IP:
  .free1 mu auf einen String zeigen, der den gewnschten Service (z.B. 
  "http") enthlt, .free2 mu auf einen String zeigen, der den Host
  enthlt (z.B. "www.buntibunti.com"). Soll nicht auf einen bestimmten
  Host eingegrenzt werden, ist .free2 entweder als Leerstring oder NULL zu
  bergeben.
  USIS prft, ob es fr diesen Service einen Proxy-Eintrag gibt. Wenn nicht
  (bzw. wenn die dort angegebene IP 0.0.0.0 ist) wird UA_NOTSET geliefert.
  Wird ein Eintrag gefunden, werden alle "not use"-Eintrge (incl. Wildcards)
  aus IConf mit dem angegebenen Host verglichen. Bei einem Treffer wird 
  UA_NOTSET geliefert.
  Abschlieend wird der Port-Eintrag des Proxies geprft. Steht hier 0,
  wird aus etc/services die Port-Nummer zum bergebenen Service gelesen.
  Klappt das nicht, wird UA_NOTSET geliefert.
  Ansonsten wird in .ip die IP-Adresse des Proxy-Servers und in .port die
  Port-Nummer geliefert, unter der der Proxy-Service zu erreichen ist.

UR_ONLINE_TIME:
  Falls UA_FOUND geliefert wird, erhlt man die vergangene Online-Zeit, die 
  z.B. auch in IConnect angezeigt wird.
  Das Ergebnis wird ber .other als Zeiger auf ein ONLINE_TIME struct 
  geliefert:

  typedef struct
	{
		long		total_sec;								
		
		int			hours, minutes, seconds;	
		char		hh_mm_ss[10];							
	}ONLINE_TIME;

	total_sec liefert die vergangene Zeit in Sekunden. In hours, minutes und
  seconds wird die Zeit in aufgelster Form geliefert und in hh_mm_ss wird
  ein fertig aufgebauter String der Form "HH:MM:SS" geliefert	(null-
  terminiert).
  Wichtig: Das struct wird _nicht_ vom Aufrufer zur Verfgung gestellt 
	sondern als Ergebnis geliefert.
  Es handelt sich um ein statisches struct, das nur gelesen
  und nicht beschrieben werden darf. Der Pointer auf dieses struct kann
  vom Aufrufer vermerkt und weiterverwendet werden, es wird stndig
  automatisch aktualisiert solange IConnect luft.
	Terminiert IConnect, liefert ein weiterer Aufruf von UR_ONLINE_TIME
	den Fehler UA_OFFLINE. Der Pointer behlt aber seine Gltigkeit.

UR_BYTES:
	Falls UA_FOUND geliefert wird, erhlt man die bertragenen Bytes
	(idR lediglich fr statistische Zwecke interessant).
	Das Ergebnis wird ber .other als Zeiger auf ein BYTE_EXCHANGE struct
	geliefert:

	typedef struct
	{
		long		bytes_sent;
		long		bytes_rcvd;
		
		char		t_sent[13];
		char		t_rcvd[13];
	}BYTE_EXCHANGE;

	Die beiden longs liefern jeweils die gesamte Anzahl aller versandten
	bzw. empfangenen Bytes auf der aktiven Verbindung.

	Die beiden Strings liefern die Anzahl jeweils fertig formatiert in
	folgender Form:
	"x[x[x[x[.x]]]] [K|M|G]Byte"
  D.h. berschreitet die Byte-Anzahl 1024, wird solange durch 1024
	geteilt, bis die Zahl wieder kleiner ist, entsprechend wird der
	Bereich K/M/G fr Kilo, Mega und Gigabyte gewhlt. Ist der Bereich
	nicht Byte, wird zustzlich eine Nachkommaziffer angehngt.
	Der String ist daher nie lnger als 12 Zeichen (plus Nullterminierung,
	also insgesamt 13).
  Wichtig: Das struct wird _nicht_ vom Aufrufer zur Verfgung gestellt
	sondern als Ergebnis geliefert.
  Es handelt sich um ein statisches struct, das nur gelesen
  und nicht beschrieben werden darf. Der Pointer auf dieses struct kann
  vom Aufrufer vermerkt und weiterverwendet werden, es wird stndig
  automatisch aktualisiert solange IConnect luft.
	Terminiert IConnect, liefert ein weiterer Aufruf von UR_BYTES
	den Fehler UA_OFFLINE. Der Pointer behlt aber seine Gltigkeit.


Hinweis: Es ist mglich (in einer spteren Version), da der User whrend 
einer bestehenden Verbindung nderungen am aktiven Setup per ICONF vornimmt
(z.B. weil der Mail-Server down ist und er deshalb einen anderen eintragen 
will). 
Man sollte also soweit sinnvoll die Parameter nicht nur zu Programmbeginn 
abfragen, sondern vor jeder Benutzung.


/* Verschiedenes/Hilfsfunktionen */  

int	parse_url(char *s, URL *url, int default_port, char *default_name)
  s=Zeiger auf String mit URL
  url=Zeiger auf url-struct (des Aufrufers)
  default_port=Port-Nummer, die eingetragen werden soll, wenn der Service
  =default_name oder nicht angegeben ist
  
 typedef struct
 {
	int		port;
	char	service[14];
	char	host[128];
	char	path[512]; 	/* Ab Version 1.4, vorher path[256] */
 }URL;
  
  Die Funktion zerlegt eine URL der Form
  [service://]<[host.]name[.domain]>[:port][path]
  
  und schreibt das Ergebnis in das struct.
  
  Vorgehensweise:

	Ist [:port] angegeben, wird dieser immer nach .port geschrieben.
	
  Ist [service://] nicht angegeben, wird default_name nach .service
  geschrieben.
  Ist [service://] angegeben und ist dieser gleich default_name, wird
  default_name nach .service und default_port nach .port geschrieben.
  Ist [service://] nicht default_name, wird der Service in der services-
  Datei gesucht (sofern kein [:port] angegeben ist).
  Wird er nicht gefunden, liefert die Funktion -1.
  
  Der Sinn von default_port und default_name ist der, die Suchzeiten zu
  verkrzen. D.h. CAB.OVL wird z.B. 80, "http" bergeben, damit nicht
  fr jede einzelne URL die services-Datei durchsucht werden mu. Es
  gengt, wenn das OVL das einmal beim Starten macht und die gefundene
  Port-Nummer eben immer als default_port bergibt.
  Soll die services-Datei immer durchsucht werden, mu als default_port
  -1 bergeben werden. Unabhngig davon mu default_name immer auf
  einen String zeigen, d.h. ein NULL-Zeiger ist nicht erlaubt (ein String,
  der nur ein 0-Byte enthlt ist dagegen erlaubt).
  
  Wird [host.] und [.domain] nicht angegeben, also nur der name, wird "www." 
  vorangestellt und ".com" angehngt. Fehlt nur eine der Angaben, wird
  nichts gendert (da es hostnamen gibt, die nur einen Punkt enthalten).
  
  Beispiel:
  parse_url("ftp://ftp.server.de:28/pub/atari", &url, 21, "ftp")
  liefert
  {
    28,
    "ftp",
    "ftp.server.de"
    "pub/atari"
   }
  
  Das gleiche ohne die explizite Port-Angabe:
  parse_url("ftp://ftp.server.de/pub/atari", &url, 21, "ftp")
  liefert
  {
    21,
    "ftp",
    "ftp.server.de"
    "pub/atari"
   }
  
  parse_url("bunti/welcome/toll.html", &url, 80, "http")
  liefert
  {
  	80,
  	"http",
  	"www.bunti.com",
  	"welcome/toll.html"
  }
  
  und einer noch:
  parse_url("bunti/welcome/toll.html", &url, -1, "http")
  liefert
  {
  	80, (oder eine andere Nummer, was halt in etc/services eingetragen ist)
  	"http",
  	"www.bunti.com",
  	"welcome/toll.html"
  }
  Dieser Aufruf sucht die Port-Nummer zu "http" auf jeden Fall in der
  etc/services-Datei.
  
  Die Funktion liefert 0, wenn die URL aufgelst werden konnte oder -1,
  wenn keine port-Nummer gefunden werden konnte.
  


-EOF-
