On Wed, 5 Nov 2008, rihad wrote:

Imagine this shell pipeline:

sh prog1 | sh prog2


As given above, prog1 blocks if prog2 hasn't yet read previously written
data (actually, newline separated commands) or is busy. What I want is
for prog1 to never block:

sh prog1 | buffer | sh prog2

[and misc/buffer is unsuitable]

I found an old piece of code laying around that I wrote for this purpose. Looking at it, I can see a number of inefficiencies, but it might do in a pinch. You're welcome to use it; I hereby release it to the public domain.

Another hack that you could use, if you don't mind storing the buffer on disk rather than memory, is

sh prog1 > tmpfile &
tail -f -c +0 tmpfile | sh prog2

Here's my program.

/* Buffering filter. */

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

/* Size of a single buffer. */

#define BUFSIZE 512

struct buffer {
  struct buffer *next;
  size_t length;
  unsigned char buf[BUFSIZE];
};

struct buffer *reader;
struct buffer *writer;


int max_mem = 100 * 1024;

int current_mem;

#define OK 0
#define WAIT 1
#define GIVEUP 2

int read_one (int fd)
{
  int result;

  if (current_mem > (max_mem - sizeof(*reader->next)))
    {
      fprintf(stderr, "Reached max_mem!\n");
      return WAIT;
    }
  /* Get a new buffer. */
  reader->next = malloc(sizeof(*reader->next));
  if (reader->next)
    {
      current_mem += sizeof(*reader->next);
      fprintf(stderr, "\rReading: \t%u bytes in buffer                 ",
              current_mem);
    }
  else
    {
      fprintf(stderr, "Virtual memory exhausted\n");
      return WAIT;
    }

  reader = reader->next;
  reader->next = NULL;

  result = read(fd, reader->buf, BUFSIZE);
  if (result > 0)
    reader->length = result;
  else if (result == 0)
    {
      fprintf(stderr, "Hit EOF on reader\n");
      return GIVEUP;
    }
  else if (result < 0)
    {
      fprintf(stderr, "Error on reader: %s\n", strerror(errno));
      return GIVEUP;
    }
  return OK;
}



int write_one (int fd)
{
  struct buffer *newwriter;

  if (reader == writer)
    return WAIT; /* the reader owns the last buffer */

  if (writer->length > 0)
    {
      int result;
      result = write(fd, writer->buf, writer->length);
      if (result == 0)
        {
          fprintf(stderr, "Hit EOF on writer\n");
          return GIVEUP;
        }
      else if (result < 0)
        {
          fprintf(stderr, "Error on writer: %s\n", strerror(errno));
          return GIVEUP;
        }
    }
  newwriter = writer->next;
  free(writer);
  current_mem -= sizeof(*writer);
  fprintf(stderr, "\rWriting: \t%u bytes in buffer                 ",
          current_mem);
  writer = newwriter;
  return OK;
}

void move_data(int in_fd, int out_fd)
{
  int reader_state = OK;
  int writer_state = OK;

  int maxfd = ((in_fd > out_fd) ? in_fd : out_fd) + 1;

  reader = malloc(sizeof(*reader));
  if (!reader)
    {
      fprintf(stderr, "No memory at all!\n");
      return;
    }
  reader->next = NULL;
  reader->length = 0;
  writer = reader;
  current_mem = sizeof(*reader);

  while (1) /* break when done */
    {
      int result;
      fd_set read_set, write_set;
      FD_ZERO(&read_set);
      FD_ZERO(&write_set);
      if (reader_state == OK)
        FD_SET(in_fd, &read_set);
      if (writer_state == OK)
        FD_SET(out_fd, &write_set);
      result = select(maxfd, &read_set, &write_set, NULL, NULL);

      /* If we're ready to do something, do it.  Also let the other
         end get a chance if something changed. */

      if (FD_ISSET(in_fd, &read_set))
        {
          reader_state = read_one(in_fd);
          if (writer_state == WAIT)
            writer_state = OK;
        }

      if (FD_ISSET(out_fd, &write_set))
        {
          writer_state = write_one(out_fd);
          if (reader_state == WAIT)
            reader_state = OK;
        }

      /* Check for termination */
      if (writer_state == GIVEUP)
        break; /* can't write any more */
      if (reader_state == GIVEUP && writer_state == WAIT)
        break; /* can't read any more, and wrote all we have */
    }
}

int main(void)
{
  move_data(0, 1);
  return 0;
}

--

Nate Eldredge
[EMAIL PROTECTED]
_______________________________________________
freebsd-hackers@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-hackers
To unsubscribe, send any mail to "[EMAIL PROTECTED]"

Reply via email to