/*
 *  $Id: quote.c,v 1.2 94/05/25 07:09:09 bert Exp $
 *
 *  various useful functions that all ultimately call write()
 *  (part of diswww, a Discuss->WWW gateway)
 */

#ifndef lint
static char rcsid_quote[] = "$Id: quote.c,v 1.2 94/05/25 07:09:09 bert Exp $";
#endif

#include "proto.h"  /* the copyright notice is included in this file, too. */
#include "extern.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include "status.h"

extern char w_buf[];     /* temporary buffer for random output */

/*
 * This macro returns true if the argument char doesn't need to be quoted.
 */
#define ok_char(c) ((c!='\r')&&(c!='\n')&&(c!='<')&&(c!= '>')&&(c!= '&'))

/*
 * a HTTP- and network-happy version of write().
 * It translates "<", ">" and "&" into correct HTTP incantations.
 * It also removes "\r" and translates "\n" to "\r\n".
 *    fd --     file descriptor
 *    buf --    pointer to the (character) data
 *    nbytes -- number of bytes to be written
 * returns: number of bytes taken from the buffer. (The number of bytes
 *          written to fd could be larger, due to quoting.)
 */
int write_quoted(int fd, char *buf, int nbytes)
{
  char *qq;
  int cnt;
  int pos = 0;
  int ret = 0;

  qq = buf;

  for (cnt=0; (cnt<nbytes) && ok_char(*qq); cnt++, qq++);

  while (cnt < nbytes) {
    if (cnt > 0) {
      if ((ret = write(fd, buf, cnt)) < 0) return ret;
    }

    switch (*qq) {  /* since cnt<nbytes, *qq points somewhere */
    case '\n': if ((ret = write(fd, "\r\n", 2)) < 0) return ret;  break;
    case '<':  if ((ret = write(fd, "&lt;", 4)) < 0) return ret;  break;
    case '>':  if ((ret = write(fd, "&gt;", 4)) < 0) return ret;  break;
    case '&':  if ((ret = write(fd, "&amp;", 5)) < 0) return ret; break;
    }

    buf = (++qq);
    nbytes -= (cnt+1);
    pos += (cnt+1);
    for (cnt=0; (cnt<nbytes) && ok_char(*qq); cnt++, qq++);
  }
  if (nbytes > 0) {
    ret = write(fd, buf, nbytes);
    if (ret < 0) return ret;
    else return (pos+ret);
  }
  else return pos;
}

/*
 *  a simple wrapper for write to work with strings
 *     fd --   file descriptor
 *     text -- a null-terminated character string
 *  returns: number of bytes written
 *  See also: fd_write_quoted
 */
int fd_write(int fd, char* text)
{
  return write(fd, text, strlen(text));
}

/*
 *  a simple wrapper for write_quoted to work with strings
 *     fd --   file descriptor
 *     text -- a null-terminated character string
 *  returns: number of bytes used (see write_quoted)
 *  See also: fd_write
 */
int fd_write_quoted(int fd, char* text)
{
  return write_quoted(fd, text, strlen(text));
}

/*
 *  similar to fprintf, but without buffering.
 *     fd --  file descriptor
 *     fmt -- printf-style format
 *     ... -- printf-style arguments
 *  returns: number of bytes written
 *  The caller (or programmer) should make sure that the length of the
 *  output string won't be more than SRV_REPLYSIZE bytes long.
 *  See also: fd_print_quoted
 */
int fd_print(int fd, const char* fmt, ...)
{
  va_list pvar;

  va_start(pvar, fmt);
  vsprintf(w_buf, fmt, pvar);
  /* note: we can't use the return value from vsprintf on non-POSIX systems */
  va_end(pvar);
  return write(fd, w_buf, strlen(w_buf));
}

/*
 *  like fd_print, but using 
 *     fd --  file descriptor
 *     fmt -- printf-style format
 *     ... -- printf-style arguments
 *  returns: number of bytes written
 *  The caller (or programmer) should make sure that the length of the
 *  output string won't be more than SRV_REPLYSIZE bytes long.
 *  See also: fd_print
 */
int fd_print_quoted(int fd, const char* fmt, ...)
{
  va_list pvar;

  va_start(pvar, fmt);
  vsprintf(w_buf, fmt, pvar);
  /* note: we can't use the return value from vsprintf on non-POSIX systems */
  va_end(pvar);
  return write_quoted(fd, w_buf, strlen(w_buf));
}

/*
 *  copy the contents of a file to the specified file descriptor.
 *     fd --    file descriptor
 *     fname -- file name for input
 */
void fd_spew_file(int fd, char* fname)
{
  int in, rr, ww, wp;

  if ((in = open(fname, 0, O_RDONLY)) < 0) {
    SYSLOG_ERROR("open() failed");
    exit(10);
  }

  while ((rr = read(in, w_buf, SRV_REPLYSIZE)) > 0) {
    wp = 0;
    while (wp < rr) {
      if ((ww = write(fd, &w_buf[wp], rr-wp)) < 0) {
	SYSLOG_ERROR("write() failed");
	exit(10);
      }
      wp += ww;
    }
  }
  if (rr < 0) {
    SYSLOG_ERROR("read() failed");
    exit(10);
  }

  close(in);
}
