/*******************************************************************
             Interfaces d'Entre'es/Sorties Le-Lisp V15.2
                   utilisable sur tous les UNIX.
 *****************************************************************
   Ce fichier est en lecture seule hors du projet ALE de l'INRIA  
 Il est maintenu par : ILOG S.A. 9 rue Royale, 75008 Paris, France
 *****************************************************************
$Header: llstdio.c,v 4.10 89/01/09 12:48:03 neidl Exp $
 *******************************************************************/
/* liste des conditionnelles :
MAXCHAR
MAXCHAN
S5
BSD4x
BSD4.2
IBMRT
INRIA
FOREIGN
/*****************************************************
	Parame`tres du syste`me multi-fichiers
******************************************************/
#ifndef  MAXCHAR
#define  MAXCHAR  256	/* taille d'un tampon LLM3 */
#endif   MAXCHAR
#ifndef  MAXCHAN
#define  MAXCHAN  12	/* nombre de canaux disponibles */
#endif   MAXCHAN
#include <stdio.h>
#include <errno.h>
#ifdef S5
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#endif S5
#ifdef BSD42
#include <sys/time.h>
#include <signal.h>
#endif BSD42
#ifndef IBMRT
#include <sgtty.h>
#endif  IBMRT
extern int errno;
/* le nombre de canaux disponibles */
extern int maxchan;
/********************************************************
	L'impression des erreurs d'E/S
*********************************************************/
/* L'indicateur controlant l'impression des erreurs syste`me
   (de'fini dans lelisp.c) */
extern int **prtmsgs;
#define	errreturn(M,V)	{ \
				if (**prtmsgs != 0) { \
					perror(M); \
					write(2, "\r", 1); \
				} \
				return(V); \
			}
/* retour des codes d'erreur */
#define codereturn(v) { \
			if (v) \
				errreturn("Le←Lisp",1); \
			return(0); \
		      }
/********************************************************
	La fonction d'initialisation des d'E/S
*********************************************************/
/* llstdio: initialisation du syste`me multi-fichier
   ------------------------------------------------- */
llstdio () {
	maxchan = MAXCHAN;
}
/********************************************************
	Les fonctions utilisant le terminal
*********************************************************/
/* ttyin: lecture d'un caracte`re
   ------------------------------ */
char caractere;
char ttyin () {
#ifdef INRIA
	/*
	BSD4.2 pose un proble`me particulier, car il restarte
	les I/O interrompues, on fait donc une attente a` l'aide
	d'un select qui surveille stdin et termine sur un signal
	ou sur l'arrive'e d'un caracte`re.
	Malheureusement, il y a beaucoup de bugs dans les vieux
	noyaux BSD 4.2 :
	 - le ↑Z pendant un select peut tuer le processus.
	 - les pseudo-ttys esclaves ne sont pas reveille'es
	   pendant un select.
	*/
	int readfds = 1;
	char buf;
	extern char ttys();
	if (select(1,&readfds,0,0,0) < 0)  /* interompu ? */
          return(-1);                      /* retour Lelisp */
#endif INRIA
        switch (read(0,&caractere,1)) {    /* le resultat du read */
           case 0 : out(0);                /* EOF, on quitte Lelisp */
           case 1 : return(caractere);};   /* 1 caracter, on le rend */
        return((char) -1);                 /* erreur, retour Lelisp */
}
/* ttys: test de la frappe d'un caracte`re
   --------------------------------------- */
char ttys (buffer)
char *buffer;
{
	int nchars;
#ifdef BSD4x
        retry:
	         if (ioctl(0, FIONREAD, &nchars) == -1)
			 if (errno == EINTR)
				 goto retry;
			 else if (errno == EBADF) {
				 fprintf(stderr,
#ifdef FOREIGN
			   "\r\nLe-Lisp : standard input is closed !\r\n");
#else FOREIGN
			   "\r\nLe-Lisp : l'entree standard est fermee !\r\n");
#endif FOREIGN
	                         out(-1);
                               } else
	                 return(-1); 
#endif BSD4x
#ifdef S5
	int vfcntl;
	vfcntl = fcntl (0, F←GETFL, nchars);
	vfcntl |= O←NDELAY;
	vfcntl = fcntl (0, F←SETFL, vfcntl);
	nchars = read (0, buffer, 1);
	if (nchars <= 0)
		nchars = -1;
	else
		nchars = 0;
	vfcntl &= ~O←NDELAY;
	vfcntl = fcntl (0, F←SETFL, vfcntl);
	return (nchars);
#endif S5
	if(nchars == 0) return(-1);
        for (errno=EINTR; errno==EINTR;){ /* system call interupted */
           switch (read(0,buffer,1)) {    /* le resultat du read */
/*            case 0 : out(0);               en prevision du tys+eof SYS5 */
              case 1 : return(0);};}      /* 1 caractere lu, retour Lelisp */
        return(-1);                       /* autre erreur que EINTR */
}
/* ttyinstr: lecture d'une ligne directement par le syste`me
   --------------------------------------------------------- */
int ttyinstr (buffer, maxlen)
char *buffer;
int maxlen;
{
	int n, c;
	for (n = 0; n < maxlen; n++) {
		c = ttyin();
		if ((c == '\r') || (c == '\n'))
			return(n);
		*(buffer + n) = c;
	}
	return(maxlen);
}
/* ttyout: impression d'une chai↑ne de caracte`res
   ----------------------------------------------- */
ttyout (length, buffer)
int length;
char *buffer;
{
	int cc, count;
	for (count = 0; count != length; count += cc) {
		cc = write (1, buffer+count, length-count);
		if (cc < 0)
			break;
	}		
}
/* ttycrlf: impression de CR/LF
   ---------------------------- */
ttycrlf () {
	ttyout (2, "\r\n");
}
/********************************************************
	Les fonctions utilisant les fichiers
*********************************************************/
/* les tampons d'entre'e */
struct {
	char contents[MAXCHAR];
	char *position;
	char *last;
	int filedesc;
       } channels[MAXCHAN];
/* inbf: lecture d'une ligne sur un fichier texte
   ----------------------------------------------
   Lit la ligne suivante sur le canal argument.
   La taille de la ligne lue est rendue dans *ptaille.
   Retourne un code condition:
	  0: Ok on a une une ligne comple`te
	  1: On a rien lu a` cause de EOF
	  2: On a lu un de'but de ligne qui de'passe MAXCHAR
	  3: On a lu la dernie`re ligne du fichier qui ne se termine
	     pas par CR/LF
*/
int inbf (canal, buffer, ptaille)
int canal;
char *buffer;
int *ptaille;
{
	int resread;
	char *ficbuff;
	char *ficpos;
	char *ficlast;
	int ncars;
	ncars = 0;
again:
	ficpos = channels[canal].position;
	ficlast = channels[canal].last;
	while ((ncars < MAXCHAR) &&
	       (ficpos != ficlast) &&
	       ((*buffer++ = *ficpos++) != '\n')) {
		ncars += 1;
	}
	if (*(buffer - 1) == '\n') {
		channels[canal].position = ficpos;
		*ptaille = ncars;
		return(0);
	}
	if (ncars == MAXCHAR) {
		channels[canal].position = ficpos;
		*ptaille = ncars;
		return(2);
	}
	ficbuff = channels[canal].contents;
	resread = read (channels[canal].filedesc, ficbuff, MAXCHAR);
	if (resread <= 0) {
		channels[canal].position = channels[canal].contents;
		channels[canal].last = channels[canal].contents;
		if (ncars > 0) {
			*ptaille = ncars;
	 		return(2);
		} else {
			*ptaille = 0;
			return(1);
		}
	}
	channels[canal].last = ficbuff + resread;
	channels[canal].position = ficbuff;
	goto again;
}
/* inbfb: lecture d'un tampon sur un fichier binaire
   -------------------------------------------------
   code condition:
	0: EOF
	1: Ok
*/
int inbfb (canal, buffer, ptaille)
int canal;
char *buffer;
int *ptaille;
{
	int cc;
	cc = read (channels[canal].filedesc, buffer, MAXCHAR);
	if (cc > 0) {
		*ptaille = cc;
		return(0);
	} else {
		*ptaille = 0;
		return(1);
	}
}
/* inb: lecture d'un nb arbitraire de caracte`res sur un fichier binaire
   ---------------------------------------------------------------------
   code condition:
	0: EOF
	1: Ok
*/
int inb (canal, buffer, length, ptaille)
int canal;
char *buffer;
int *ptaille;
{
	int cc;
	cc = read (channels[canal].filedesc, buffer, length);
	if (cc > 0) {
		*ptaille = cc;
		return(0);
	} else {
		*ptaille = 0;
		return(1);
	}
}
/* outf: imprime une ligne sur un fichier texte
   -------------------------------------------- */
int outf (canal, length, buff)
int canal;
int length;
char *buff;
{
	if (write (channels[canal].filedesc, buff, length) != length)
		errreturn ("Le←Lisp : outf1 ", 1);
	if (write (channels[canal].filedesc, "\n", 1) != 1)
		errreturn ("Le←Lisp : outf2 ", 1);
	return(0);
}
/* outfl: imprime une ligne sans marque de fin de ligne sur un fichier texte
   ------------------------------------------------------------------------- */
int outfl (canal, length, buff)
int canal;
int length;
char *buff;
{
	if (write (channels[canal].filedesc, buff, length) != length)
		errreturn ("Le←Lisp : outfl ", 1);
	return(0);
}
/* llseek:    positionnement du canal <chan> a` la position <n1>*<n2>
   ------------------------------------------------------------------
  (uniquement pour des defexterns) */
int llseek (canal, n1, n2)
int canal, n1, n2;
{
	if (lseek (channels[canal].filedesc, n1*n2) != -1)
		errreturn ("Le←Lisp : llseek ", 1);
	return(0);
}
/* infile: ouverture d'un fichier en lecture
   ----------------------------------------- */
int infile (canal, buff)
int canal;
char *buff;
{
	if ((channels[canal].filedesc = open(buff, 0)) != -1) {
		channels[canal].position = channels[canal].contents;
		channels[canal].last = channels[canal].contents;
		return(0);
	}
	errreturn(buff, 1);
}
/* infile: ouverture d'un fichier en e'criture
   ------------------------------------------- */
int oufile (canal, buff)
int canal;
char *buff;
{
	int fd;
        fd = creat (buff, 0666); /* rw-rw-rw | umask */
	if ((channels[canal].filedesc = fd) != -1) {
		channels[canal].position = channels[canal].contents;
		channels[canal].last = channels[canal].contents;
		return(0);
	}
	errreturn(buff, 1);
}
/* apfile: ouverture d'une fichier en ajout
   ---------------------------------------- */
int apfile (canal, buff)
int canal;
char *buff;
{
	int fd;
	if ((fd = open (buff, 1)) == -1)
		fd = creat (buff, 0666); /* rw-rw-rw | umask */
	lseek (fd, 0, 2);
	if ((channels[canal].filedesc = fd) != -1){
		channels[canal].position = channels[canal].contents;
		channels[canal].last = channels[canal].contents;
		return(0);
	}
	errreturn(buff, 1);
}
/* fclos: fermeture d'un fichier
   ----------------------------- */
int fclos (canal)
int canal;
{
	codereturn (close (channels[canal].filedesc));
}
/* fdele: de'truit un fichier
   --------------------------
   retourne le code condition de C */
int fdele (buff)
char *buff;
{
	codereturn (unlink (buff));
}
/* frena: change le nom d'un fichier
   ---------------------------------
   retourne le code condition de C */
int frena (nom1, nom2)
char *nom1, *nom2;
{
#ifdef BSD42
	if (rename(nom1, nom2) == -1)
		errreturn("Le←Lisp : rename ", 1);
#else BSD42
	if (close (open(nom1,0)) == 0) {
		unlink(nom2);
		if (link(nom1, nom2) == 0)
			if (unlink(nom1) == 0)
				return(0);
			errreturn(nom1, 1);
	} else
		errreturn(nom1, 1);
#endif BSD42
}
/* fprobe: teste l'existence d'un fichier
   --------------------------------------
   retourne le code condition de C */
int fprobe (buff)
char *buff;
{
	int fd;
	fd = open(buff, 0);
	if (fd != -1) 
		codereturn (close (fd))		/* pas de ; */
	else
		return(fd);
}
int llgetchan(a)                            /* channel LL --> FD C */
   int a;{                                  /* un canal Lelisp */
   return(channels[a].filedesc);};          /* le file descriptor C */
int llsetchan(a,b)                          /* FD C --> channel LL */
   int a,b;{                                /* canal LL, FD C */
   channels[a].filedesc=b;                  /* remplacer l'ancien FD ... */
   return(b);};                             /* ... par le nouveau FD C */