#define fifo_c_RCS_ID $Id: fifo.c,v 1.6 2001/03/04 17:10:31 oz6bl Exp $
/*
 *   Copyright 2000 Bent Bagger (OZ6BL)
 *              All Rights Reserved
 *
 *   Based on material from 'diald.c'
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 1, or (at your option)
 *   any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include "fifo.h"

/* Initialized external variables (initialized elsewhere) */

extern MONITORS *monitors;	/* Monitor pipes */
extern PIPE *pipes;			/* Command pipes */
extern fd_set ctrl_fds;
extern PIPE *fifo_pipe ;
extern int stopFlag ;
extern int debugmask ;
extern char logFileName[]; /* Name defined by calling program */

/* Local prototypes */

int putEntity (char *, char *, int) ;

/*
 * Initialize a FIFO (pipe) 
 */

void pipe_init(PIPE *pipestruct, char *name, int fd, PIPE *pipe, int flush) {
    char buf[2];

    pipe->name = name;
    pipe->fd = fd;
    pipe->count = 0;
    fcntl(fd,F_SETFL,fcntl(fd,F_GETFL)|O_NONBLOCK);
    pipe->next = pipestruct;
    pipestruct = pipe;
    if (flush) {
        /* clear out any old garbage from the FIFO */
        while (read(fd,buf,1) > 0);
    }
}


/* Read from the file descriptor, and
 * return the number of characters in the buffer.
 * This all assumes that there are some characters to read.
 */

int pipe_read(PIPE *pipe)
{
    int i;
    if (pipe->count == sizeof(pipe->buf)) {
		return pipe->count;	/* No room for more input */
    }
    i = read(pipe->fd, pipe->buf+pipe->count, sizeof(pipe->buf)-pipe->count);
    if (i > 0) {
		pipe->count += i;
		return pipe->count;
    } else if (i == 0 || errno == EAGAIN) {
		/* A close of a pipe is only significant if it is a control
		 * pipe, not if it is a capture pipe for a script.
		 */
		return -1;
    } else if (errno == EINTR){
		return -1;
    } else {
		fprintf(stderr, "pipe_read error: %s", pipe->name);
		return -1;	/* error! shut down reader... */
    }
}

/* Drop count characters from the pipe's buffer */
void pipe_flush(PIPE *pipe,int count) {
	if (count >= pipe->count) {
		pipe->count = 0;
	} else {
		pipe->count -= count;
		memmove(pipe->buf, pipe->buf+count, pipe->count);
	}
}

/*
* Read a tag from the command pipe. 
* Return 0 if unable to completely read a tag (terminating '>' not 
* encountered before end-of-buffer)
* At entry: 'count' = start position in buffer
* Returns the next unused position in the buffer
*/
int getTag (PIPE *pipe, char *tag, int count) {
	int i = 0;

	if (count >= pipe->count) return 0 ;
	/* search for initial '<', skipping intermediate characters */
	while (pipe->buf[count++] != '<') {
		if (count == pipe->count) return 0 ;
	}
	while ((tag[i++] = pipe->buf[count++]) != '>') {
		if (count == pipe->count) return 0 ;
	}
	tag[i-1] = '\0' ; /* Make into string (overwrite '>' with '\0') */
	return count ;
}
/*
 * Read any tag from the command pipe. 
 */
int getAnyTag (PIPE *pipe, char *tag) {

	/* Always start at the beginning of the buffer */
	return getTag (pipe, tag, 0) ; 
}
/*
 * Get tag with value
 */
int getTagWithValue (PIPE *pipe, const char *wantedTag, char *value, int bufcount) {
	char tag[TAGSIZE] ;
	char tmpTag [TAGSIZE] ;

	/* Get opening tag */
	if ((bufcount = getTag (pipe, tag, bufcount)) == 0) return 0 ; 
	if (strcmp (tag, wantedTag)) {
		if ((debugmask & DEBUG_IN_TAG) == DEBUG_IN_TAG) {
			sprintf (debugline, "From FIFO: wanted: %s, got:%s", wantedTag, tag) ;
			WriteToLog (debugline) ;
			return 0 ; }
	}
	/* Got the opening tag - now get value and closing tag */
	strcpy (tmpTag, "/") ;
	strcat (tmpTag, wantedTag) ;
	return getValue (pipe, tmpTag, value, bufcount) ; 
}
/*
 * Get the value part of a tag with value
 */
int getValue (PIPE *pipe, const char *wantedTag, char *value, int count) {
	char tag[TAGSIZE] ;
	int i = 0 ;
	
	while ((tag[i++] = pipe->buf[count++]) != '<') 
		if (count == pipe->count) return 0 ;
	/* We got the opening '<' of the next tag - fine */
	tag[i-1] = '\0' ; /* Make into string (overwrite '<' with '\0') */
	--count ; /* leave '<' in the buffer */
	decodePCData (tag, value) ;
	/* Try to get the closing tag */
	if ((count = getTag (pipe, tag, count)) != 0) {
		if (strcmp (tag, wantedTag) != 0) {
			if ((debugmask & DEBUG_IN_TAG) == DEBUG_IN_TAG) {
				sprintf (debugline, "From FIFO: wanted: %s, got: %s", wantedTag, tag) ;
				WriteToLog (debugline) ; }
		}
	}
	return count ;
}

int putTag (FILE *fd, char *tag) {
	fputc ('<', fd) ;
	fputs (tag, fd) ;
	fputc ('>', fd) ;
	if ((debugmask & DEBUG_TO_FIFO) == DEBUG_TO_FIFO) {
		sprintf (debugline, "To FIFO  :<%s>", tag) ;
		WriteToLog (debugline) ; }
	return 0 ;
}

int putTagWithValue (FILE *fd, char *tag, char *value) {
	char temp [2*TAGSIZE] ;
	putTag (fd, tag) ;
	encodePCData (value, temp) ;
	fputs (temp, fd) ; 
	if ((debugmask & DEBUG_TO_FIFO) == DEBUG_TO_FIFO) {
		sprintf (debugline, "To FIFO  :  %s", temp) ;
		WriteToLog (debugline) ; }
	sprintf (temp, "/%s", tag) ;
	putTag (fd, temp) ;
	return 0 ;
}
/*
*  Send a line to the log file. The name of the log file 
*  is defined in external variable 'filename' by either
*  pb.c or pbctrl.c
*/
void WriteToLog (char *text) {
	FILE * fp;
	time_t t;
	struct tm *tm;

   if ((fp = fopen(logFileName, "a")) != NULL) {
	   /* add a time stamp to the text */
	   time (&t);
	   tm = gmtime(&t);
	   /* remove a possible NL at the end of the text */
	   if (text[strlen(text)-1] == '\n') text[strlen(text)-1] = '\0' ;
	   fprintf(fp, "%02d/%02d/%02d %02d:%02d:%02d - %s\n",
		   tm->tm_mday, tm->tm_mon + 1, tm->tm_year-100,
		   tm->tm_hour, tm->tm_min, tm->tm_sec, text);
	   fclose (fp);
   } else {
	   char c[100] ;
	   sprintf (c, "WriteToLog: File: +%s+", logFileName) ;
	   perror (c) ;
	   exit (1) ;
	}
}
/*
 * Encode a string - scan for interferring characters (&, <, >, ', ")
 */
void encodePCData (char *in, char *out) {
	int len = strlen(in), icnt, ocnt ;

	for (icnt = 0, ocnt = 0 ; icnt < len ; icnt++) {
		switch (in[icnt]) {
			case '&' : ocnt = putEntity ("&amp;",   out, ocnt) ; break ;
			case '<' : ocnt = putEntity ("&lt;",    out, ocnt) ; break ;
			case '>' : ocnt = putEntity ("&gt;",    out, ocnt) ; break ;
			case '\'': ocnt = putEntity ("&apos;",  out, ocnt) ; break ;
			case '"' : ocnt = putEntity ("&quote;", out, ocnt) ; break ;
			default: out[ocnt++] = in[icnt] ;
		}
	}
	out[ocnt] = '\0' ; /* terminate the resulting string */
}

int putEntity (char *entity, char *out, int cnt) {
	int i, len = strlen(entity) ;

	for (i = 0 ; i < len ; i++) 
		out[cnt++] = entity[i] ;
	return cnt ;
}
/*
 * Decode a string looking for occurrences of entities such as
 * &amp; &lt; &gt; &apos; or &quote;
 */
void decodePCData (char *in, char *out) {
	int len =strlen(in), i, icnt, ocnt ;
	char entity [100] ;
	
	for (icnt = 0, ocnt = 0; icnt < len; icnt++) {
		if (in[icnt] == '&') {
			i = 0 ;
			while ((in[++icnt] != ';') && (i < 8))
				entity[i++] = in[icnt] ;
			entity[i] = '\0' ; /* make it a string */
			if (strcmp (entity, "amp") == 0) out[ocnt++] = '&' ;
			else if (strcmp (entity, "lt")    == 0) out[ocnt++] = '<' ;
			else if (strcmp (entity, "gt")    == 0) out[ocnt++] = '>' ;
			else if (strcmp (entity, "apos")  == 0) out[ocnt++] = '\'' ;
			else if (strcmp (entity, "quote") == 0) out[ocnt++] = '"' ;
			if (in[icnt] != ';') /* we did not find the semicolon... */
				out[ocnt++] = '£' ;
		}
		else out[ocnt++] = in[icnt] ;
	}
	out[ocnt] = '\0' ; /* terminate the resulting string */
}
