/*******************************************************************

             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 */