Skip to content
Snippets Groups Projects
Forked from Anders Blomdell / LabComm
449 commits behind the upstream repository.
labcomm_fd_writer.c 3.72 KiB
/*
  labcomm_fd_writer.c -- LabComm writer for Unix file descriptors.

  Copyright 2006-2013 Anders Blomdell <anders.blomdell@control.lth.se>

  This file is part of LabComm.

  LabComm 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 3 of the License, or
  (at your option) any later version.

  LabComm 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, see <http://www.gnu.org/licenses/>.
*/

#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include "labcomm_private.h"
#include "labcomm_fd_writer.h"

#define BUFFER_SIZE 2048

struct labcomm_fd_writer {
  struct labcomm_writer writer;
  struct labcomm_writer_action_context action_context;
  int fd;
  int close_fd_on_free;
};

static int fd_flush(struct labcomm_writer *w, 
		    struct labcomm_writer_action_context *action_context);

static int fd_alloc(struct labcomm_writer *w, 
		    struct labcomm_writer_action_context *action_context, 
		    struct labcomm_encoder *encoder,
		    char *version,
		    labcomm_encoder_enqueue enqueue)
{
  w->data = labcomm_memory_alloc(w->memory, 0, BUFFER_SIZE);
  if (! w->data) {
    w->error = -ENOMEM;
    w->data_size = 0;
    w->count = 0;
    w->pos = 0;
  } else {
    w->data_size = BUFFER_SIZE;
    w->count = BUFFER_SIZE;
    w->pos = 0;
    if (version && version[0]) {
      labcomm_write_string(w, version);
      fd_flush(w, action_context);
    }
  }

  return w->error;
}

static int fd_free(struct labcomm_writer *w, 
		   struct labcomm_writer_action_context *action_context)
{
  struct labcomm_fd_writer *fd_writer = action_context->context;
  struct labcomm_memory *memory = w->memory;

  labcomm_memory_free(memory, 0, w->data);
  w->data = 0;
  w->data_size = 0;
  w->count = 0;
  w->pos = 0;

  if (fd_writer->close_fd_on_free) {
    close(fd_writer->fd);
  }
  labcomm_memory_free(memory, 0, fd_writer);
  return 0;
}

static int fd_start(struct labcomm_writer *w, 
		    struct labcomm_writer_action_context *action_context,
		    int index,
		    struct labcomm_signature *signature,
		    void *value)
{
  w->pos = 0;
  
  return w->error;
}

static int fd_flush(struct labcomm_writer *w, 
		    struct labcomm_writer_action_context *action_context)
{
  struct labcomm_fd_writer *fd_context = action_context->context;
  int start, err;
  
  start = 0;
  err = 0;
  while (start < w->pos) {
    err = write(fd_context->fd, &w->data[start], w->pos - start);
    if (err <= 0) {
      break;
    }
    start = start + err;
  }
  if (err < 0) {
    w->error = -errno;
  } else if (err == 0) {
    w->error = -EINVAL;
  }
  w->pos = 0;
   
  return w->error;
}

static const struct labcomm_writer_action action = {
  .alloc = fd_alloc,
  .free = fd_free,
  .start = fd_start,
  .end = fd_flush,
  .flush = fd_flush,
  .ioctl = NULL
};

struct labcomm_writer *labcomm_fd_writer_new(struct labcomm_memory *memory,
					     int fd, int close_fd_on_free)
{
  struct labcomm_fd_writer *result;

  result = labcomm_memory_alloc(memory, 0, sizeof(*result));
  if (result == NULL) {
    return NULL;
  } else {
    result->action_context.next = NULL;
    result->action_context.action = &action;
    result->action_context.context = result;
    result->writer.action_context = &result->action_context;
    result->writer.memory = memory;
    result->fd = fd;
    result->close_fd_on_free = close_fd_on_free;
    return &result->writer;
  }
}