Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA. */
-/* AIX requires this to be the first thing in the file. */
#include <config.h>
-#if __GNUC__
-#define alloca __builtin_alloca
-#else
-#if HAVE_ALLOCA_H
-#include <alloca.h>
-#else
-#ifdef _AIX
-#pragma alloca
-#else
-#ifndef alloca /* predefined by HP cc +Olibcalls */
-char *alloca ();
-#endif
-#endif
-#endif
-#endif
-
-#include <assert.h>
+#include "error.h"
+#include "dfm.h"
#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
#include "str.h"
#include "vfm.h"
-#undef DEBUGGING
-/*#define DEBUGGING 1*/
#include "debug-print.h"
+/* Flags for DFM readers. */
+enum dfm_reader_flags
+ {
+ DFM_EOF = 001, /* At end-of-file? */
+ DFM_ADVANCE = 002, /* Read next line on dfm_get_record() call? */
+ DFM_SAW_BEGIN_DATA = 004, /* For inline_file only, whether we've
+ already read a BEGIN DATA line. */
+ DFM_TABS_EXPANDED = 010, /* Tabs have been expanded. */
+ };
+
/* file_handle extension structure. */
-struct dfm_fhuser_ext
+struct dfm_reader_ext
{
struct file_ext file; /* Associated file. */
- char *line; /* Current line, not null-terminated. */
- size_t len; /* Length of line. */
-
- char *ptr; /* Pointer into line that is returned by
- dfm_get_record(). */
- size_t size; /* Number of bytes allocated for line. */
- int advance; /* Nonzero=dfm_get_record() reads a new
- record; otherwise returns current record. */
+ struct file_locator where; /* Current location in data file. */
+ struct string line; /* Current line. */
+ size_t pos; /* Offset in line of current character. */
+ struct string scratch; /* Extra line buffer. */
+ enum dfm_reader_flags flags; /* Zero or more of DFM_*. */
};
-/* These are defined at the end of this file. */
static struct fh_ext_class dfm_r_class;
-static struct fh_ext_class dfm_w_class;
static void read_record (struct file_handle *h);
-\f
-/* Internal (low level). */
-/* Closes the file handle H which was opened by open_file_r() or
- open_file_w(). */
+/* Asserts that H represents a DFM reader and returns H->ext
+ converted to a struct dfm_reader_ext *. */
+static inline struct dfm_reader_ext *
+get_reader (struct file_handle *h)
+{
+ assert (h != NULL);
+ assert (h->class == &dfm_r_class);
+ assert (h->ext != NULL);
+
+ return h->ext;
+}
+
+/* Closes file handle H opened by dfm_open_for_reading(). */
static void
-dfm_close (struct file_handle *h)
+close_reader (struct file_handle *h)
{
- struct dfm_fhuser_ext *ext = h->ext;
+ struct dfm_reader_ext *ext = get_reader (h);
/* Skip any remaining data on the inline file. */
if (h == inline_file)
- while (ext->line != NULL)
+ while ((ext->flags & DFM_EOF) == 0)
read_record (h);
msg (VM (2), _("%s: Closing data-file handle %s."),
- fh_handle_filename (h), fh_handle_name (h));
- assert (h->class == &dfm_r_class || h->class == &dfm_w_class);
+ handle_get_filename (h), handle_get_name (h));
+ assert (h->class == &dfm_r_class);
if (ext->file.file)
{
fn_close_ext (&ext->file);
free (ext->file.filename);
ext->file.filename = NULL;
}
- free (ext->line);
+ ds_destroy (&ext->line);
+ ds_destroy (&ext->scratch);
free (ext);
}
-/* Initializes EXT properly as an inline data file. */
-static void
-open_inline_file (struct dfm_fhuser_ext *ext)
+/* Opens a file handle for reading as a data file. Returns
+ nonzero only if successful. */
+int
+dfm_open_for_reading (struct file_handle *h)
{
- /* We want to indicate that the file is open, that we are not at
- eof, and that another line needs to be read in. */
- ext->file.file = NULL;
- ext->line = xmalloc (128);
-#if !PRODUCTION
- strcpy (ext->line, _("<<Bug in dfm.c>>"));
-#endif
- ext->len = strlen (ext->line);
- ext->ptr = ext->line;
- ext->size = 128;
- ext->advance = 1;
-}
+ struct dfm_reader_ext *ext;
-/* Opens a file handle for reading as a data file. */
-static int
-open_file_r (struct file_handle *h)
-{
- struct dfm_fhuser_ext ext;
+ if (h->class != NULL)
+ {
+ if (h->class == &dfm_r_class)
+ return 1;
+ else
+ {
+ msg (ME, _("Cannot read from file %s already opened for %s."),
+ handle_get_name (h), gettext (h->class->name));
+ return 0;
+ }
+ }
- h->where.line_number = 0;
- ext.file.file = NULL;
- ext.line = NULL;
- ext.len = 0;
- ext.ptr = NULL;
- ext.size = 0;
- ext.advance = 0;
+ ext = xmalloc (sizeof *ext);
+ ext->where.filename = handle_get_filename (h);
+ ext->where.line_number = 0;
+ ext->file.file = NULL;
+ ds_init (&ext->line, 64);
+ ds_init (&ext->scratch, 0);
+ ext->flags = DFM_ADVANCE;
msg (VM (1), _("%s: Opening data-file handle %s for reading."),
- fh_handle_filename (h), fh_handle_name (h));
+ handle_get_filename (h), handle_get_name (h));
assert (h != NULL);
- if (h == inline_file)
- {
- char *s;
-
- /* WTF can't this just be done with tokens?
- Is this really a special case?
- FIXME! */
- do
- {
- char *cp;
-
- if (!getl_read_line ())
- {
- msg (SE, _("BEGIN DATA expected."));
- err_failure ();
- }
-
- /* Skip leading whitespace, separate out first word, so that
- S points to a single word reduced to lowercase. */
- s = ds_value (&getl_buf);
- while (isspace ((unsigned char) *s))
- s++;
- for (cp = s; isalpha ((unsigned char) *cp); cp++)
- *cp = tolower ((unsigned char) (*cp));
- ds_truncate (&getl_buf, cp - s);
- }
- while (*s == '\0');
-
- if (!lex_id_match_len ("begin", 5, s, strcspn (s, " \t\r\v\n")))
- {
- msg (SE, _("BEGIN DATA expected."));
- err_cond_fail ();
- lex_preprocess_line ();
- return 0;
- }
- getl_prompt = GETL_PRPT_DATA;
-
- open_inline_file (&ext);
- }
- else
+ if (h != inline_file)
{
- ext.file.filename = xstrdup (h->norm_fn);
- ext.file.mode = "rb";
- ext.file.file = NULL;
- ext.file.sequence_no = NULL;
- ext.file.param = NULL;
- ext.file.postopen = NULL;
- ext.file.preclose = NULL;
- if (!fn_open_ext (&ext.file))
+ ext->file.filename = xstrdup (handle_get_filename (h));
+ ext->file.mode = "rb";
+ ext->file.file = NULL;
+ ext->file.sequence_no = NULL;
+ ext->file.param = NULL;
+ ext->file.postopen = NULL;
+ ext->file.preclose = NULL;
+ if (!fn_open_ext (&ext->file))
{
- msg (ME, _("An error occurred while opening \"%s\" for reading "
- "as a data file: %s."), h->fn, strerror (errno));
- err_cond_fail ();
- return 0;
+ msg (ME, _("Could not open \"%s\" for reading "
+ "as a data file: %s."),
+ handle_get_filename (h), strerror (errno));
+ goto error;
}
}
h->class = &dfm_r_class;
- h->ext = xmalloc (sizeof (struct dfm_fhuser_ext));
- memcpy (h->ext, &ext, sizeof (struct dfm_fhuser_ext));
-
- return 1;
-}
-
-/* Opens a file handle for writing as a data file. */
-static int
-open_file_w (struct file_handle *h)
-{
- struct dfm_fhuser_ext ext;
-
- ext.file.file = NULL;
- ext.line = NULL;
- ext.len = 0;
- ext.ptr = NULL;
- ext.size = 0;
- ext.advance = 0;
-
- h->where.line_number = 0;
-
- msg (VM (1), _("%s: Opening data-file handle %s for writing."),
- fh_handle_filename (h), fh_handle_name (h));
-
- assert (h != NULL);
- if (h == inline_file)
- {
- msg (ME, _("Cannot open the inline file for writing."));
- err_cond_fail ();
- return 0;
- }
-
- ext.file.filename = xstrdup (h->norm_fn);
- ext.file.mode = "wb";
- ext.file.file = NULL;
- ext.file.sequence_no = NULL;
- ext.file.param = NULL;
- ext.file.postopen = NULL;
- ext.file.preclose = NULL;
-
- if (!fn_open_ext (&ext.file))
- {
- msg (ME, _("An error occurred while opening \"%s\" for writing "
- "as a data file: %s."), h->fn, strerror (errno));
- err_cond_fail ();
- return 0;
- }
-
- h->class = &dfm_w_class;
- h->ext = xmalloc (sizeof (struct dfm_fhuser_ext));
- memcpy (h->ext, &ext, sizeof (struct dfm_fhuser_ext));
-
+ h->ext = ext;
return 1;
-}
-
-/* Ensures that the line buffer in file handle with extension EXT is
- big enough to hold a line of length EXT->LEN characters not
- including null terminator. */
-#define force_line_buffer_expansion() \
- do \
- { \
- if (ext->len + 1 > ext->size) \
- { \
- ext->size = ext->len * 2; \
- ext->line = xrealloc (ext->line, ext->size); \
- } \
- } \
- while (0)
-
-/* Counts the number of tabs in string STRING of length LEN. */
-static inline int
-count_tabs (char *s, size_t len)
-{
- int n_tabs = 0;
-
- for (;;)
- {
- char *cp = memchr (s, '\t', len);
- if (cp == NULL)
- return n_tabs;
- n_tabs++;
- len -= cp - s + 1;
- s = cp + 1;
- }
-}
-
-/* Converts all the tabs in H->EXT->LINE to an equivalent number of
- spaces, if necessary. */
-static void
-tabs_to_spaces (struct file_handle *h)
-{
- struct dfm_fhuser_ext *ext = h->ext;
-
- char *first_tab; /* Location of first tab (if any). */
- char *second_tab; /* Location of second tab (if any). */
- size_t orig_len; /* Line length at function entry. */
- /* If there aren't any tabs then there's nothing to do. */
- first_tab = memchr (ext->line, '\t', ext->len);
- if (first_tab == NULL)
- return;
- orig_len = ext->len;
-
- /* If there's just one tab then expand it inline. Otherwise do a
- full string copy to another buffer. */
- second_tab = memchr (first_tab + 1, '\t',
- ext->len - (first_tab - ext->line + 1));
- if (second_tab == NULL)
- {
- int n_spaces = 8 - (first_tab - ext->line) % 8;
-
- ext->len += n_spaces - 1;
-
- /* Expand the line if necessary, keeping the first_tab pointer
- valid. */
- {
- size_t ofs = first_tab - ext->line;
- force_line_buffer_expansion ();
- first_tab = ext->line + ofs;
- }
-
- memmove (first_tab + n_spaces, first_tab + 1,
- orig_len - (first_tab - ext->line + 1));
- memset (first_tab, ' ', n_spaces);
- } else {
- /* Make a local copy of original text. */
- char *orig_line = local_alloc (ext->len + 1);
- memcpy (orig_line, ext->line, ext->len);
-
- /* Allocate memory assuming we need to add 8 spaces for every tab. */
- ext->len += 2 + count_tabs (second_tab + 1,
- ext->len - (second_tab - ext->line + 1));
-
- /* Expand the line if necessary, keeping the first_tab pointer
- valid. */
- {
- size_t ofs = first_tab - ext->line;
- force_line_buffer_expansion ();
- first_tab = ext->line + ofs;
- }
-
- /* Walk through orig_line, expanding tabs into ext->line. */
- {
- char *src_p = orig_line + (first_tab - ext->line);
- char *dest_p = first_tab;
-
- for (; src_p < orig_line + orig_len; src_p++)
- {
- /* Most characters simply pass through untouched. */
- if (*src_p != '\t')
- {
- *dest_p++ = *src_p;
- continue;
- }
-
- /* Tabs are expanded into an equivalent number of
- spaces. */
- {
- int n_spaces = 8 - (dest_p - ext->line) % 8;
-
- memset (dest_p, ' ', n_spaces);
- dest_p += n_spaces;
- }
- }
-
- /* Supply null terminator and actual string length. */
- *dest_p = 0;
- ext->len = dest_p - ext->line;
- }
-
- local_free (orig_line);
- }
+ error:
+ err_cond_fail ();
+ free (ext);
+ return 0;
}
/* Reads a record from H->EXT->FILE into H->EXT->LINE, setting
static void
read_record (struct file_handle *h)
{
- struct dfm_fhuser_ext *ext = h->ext;
+ struct dfm_reader_ext *ext = get_reader (h);
if (h == inline_file)
{
+ if ((ext->flags & DFM_SAW_BEGIN_DATA) == 0)
+ {
+ char *s;
+
+ ext->flags |= DFM_SAW_BEGIN_DATA;
+
+ /* FIXME: WTF can't this just be done with tokens?
+ Is this really a special case? */
+ do
+ {
+ char *cp;
+
+ if (!getl_read_line ())
+ {
+ msg (SE, _("BEGIN DATA expected."));
+ err_failure ();
+ }
+
+ /* Skip leading whitespace, separate out first
+ word, so that S points to a single word reduced
+ to lowercase. */
+ s = ds_c_str (&getl_buf);
+ while (isspace ((unsigned char) *s))
+ s++;
+ for (cp = s; isalpha ((unsigned char) *cp); cp++)
+ *cp = tolower ((unsigned char) (*cp));
+ ds_truncate (&getl_buf, cp - s);
+ }
+ while (*s == '\0');
+
+ if (!lex_id_match_len ("begin", 5, s, strcspn (s, " \t\r\v\n")))
+ {
+ msg (SE, _("BEGIN DATA expected."));
+ lex_preprocess_line ();
+ goto eof;
+ }
+ getl_prompt = GETL_PRPT_DATA;
+ }
+
if (!getl_read_line ())
{
msg (SE, _("Unexpected end-of-file while reading data in BEGIN "
- "DATA. This probably indicates "
- "a missing or misformatted END DATA command. "
- "END DATA must appear by itself on a single line "
- "with exactly one space between words."));
+ "DATA. This probably indicates "
+ "a missing or misformatted END DATA command. "
+ "END DATA must appear by itself on a single line "
+ "with exactly one space between words."));
err_failure ();
}
- h->where.line_number++;
+ ext->where.line_number++;
if (ds_length (&getl_buf) >= 8
- && !strncasecmp (ds_value (&getl_buf), "end data", 8))
+ && !strncasecmp (ds_c_str (&getl_buf), "end data", 8))
{
- lex_set_prog (ds_value (&getl_buf) + ds_length (&getl_buf));
+ lex_set_prog (ds_c_str (&getl_buf) + ds_length (&getl_buf));
goto eof;
}
- ext->len = ds_length (&getl_buf);
- force_line_buffer_expansion ();
- strcpy (ext->line, ds_value (&getl_buf));
+ ds_replace (&ext->line, ds_c_str (&getl_buf));
}
else
{
- if (h->recform == FH_RF_VARIABLE)
+ if (handle_get_mode (h) == MODE_TEXT)
{
- /* PORTME: here you should adapt the routine to your
- system's concept of a "line" of text. */
- int read_len = getline (&ext->line, &ext->size, ext->file.file);
-
- if (read_len == -1)
- {
+ ds_clear (&ext->line);
+ if (!ds_gets (&ext->line, ext->file.file))
+ {
if (ferror (ext->file.file))
{
msg (ME, _("Error reading file %s: %s."),
- fh_handle_name (h), strerror (errno));
+ handle_get_name (h), strerror (errno));
err_cond_fail ();
}
goto eof;
}
- ext->len = (size_t) read_len;
}
- else if (h->recform == FH_RF_FIXED)
+ else if (handle_get_mode (h) == MODE_BINARY)
{
+ size_t record_width = handle_get_record_width (h);
size_t amt;
- if (ext->size < h->lrecl)
- {
- ext->size = h->lrecl;
- ext->line = xmalloc (ext->size);
- }
- amt = fread (ext->line, 1, h->lrecl, ext->file.file);
- if (h->lrecl != amt)
+ if (ds_length (&ext->line) < record_width)
+ ds_rpad (&ext->line, record_width, 0);
+
+ amt = fread (ds_c_str (&ext->line), 1, record_width,
+ ext->file.file);
+ if (record_width != amt)
{
if (ferror (ext->file.file))
msg (ME, _("Error reading file %s: %s."),
- fh_handle_name (h), strerror (errno));
+ handle_get_name (h), strerror (errno));
else if (amt != 0)
msg (ME, _("%s: Partial record at end of file."),
- fh_handle_name (h));
+ handle_get_name (h));
else
goto eof;
else
assert (0);
- h->where.line_number++;
+ ext->where.line_number++;
}
- /* Strip trailing whitespace, I forget why. But there's a good
- reason, I'm sure. I'm too scared to eliminate this code. */
- if (h->recform == FH_RF_VARIABLE)
- {
- while (ext->len && isspace ((unsigned char) ext->line[ext->len - 1]))
- ext->len--;
-
- /* Convert tabs to spaces. */
- tabs_to_spaces (h);
-
- ext->ptr = ext->line;
- }
+ ext->pos = 0;
return;
eof:
- /*hit eof or an error, clean up everything. */
- if (ext->line)
- free (ext->line);
- ext->size = 0;
- ext->line = ext->ptr = NULL;
- return;
+ /* Hit eof or an error, clean up everything. */
+ ext->flags |= DFM_EOF;
}
-\f
-/* Public (high level). */
-
-/* Returns the current record in the file corresponding to HANDLE.
- Opens files and reads records, etc., as necessary. Sets *LEN to
- the length of the line. The line returned is not null-terminated.
- Returns NULL at end of file. Calls fail() on attempt to read past
- end of file. */
-char *
-dfm_get_record (struct file_handle *h, int *len)
+
+/* Returns nonzero if end of file has been reached on HANDLE.
+ Reads forward in HANDLE's file, if necessary to tell. */
+int
+dfm_eof (struct file_handle *h)
{
- if (h->class == NULL)
- {
- if (!open_file_r (h))
- return NULL;
- read_record (h);
- }
- else if (h->class != &dfm_r_class)
+ struct dfm_reader_ext *ext = get_reader (h);
+ if (ext->flags & DFM_ADVANCE)
{
- msg (ME, _("Cannot read from file %s already opened for %s."),
- fh_handle_name (h), gettext (h->class->name));
- goto lossage;
+ ext->flags &= ~DFM_ADVANCE;
+ if ((ext->flags & DFM_EOF) == 0)
+ read_record (h);
+ else
+ {
+ msg (SE, _("Attempt to read beyond end-of-file on file %s."),
+ handle_get_name (h));
+ err_cond_fail ();
+ }
}
- else
- {
- struct dfm_fhuser_ext *ext = h->ext;
- if (ext->advance)
- {
- if (ext->line)
- read_record (h);
- else
- {
- msg (SE, _("Attempt to read beyond end-of-file on file %s."),
- fh_handle_name (h));
- goto lossage;
- }
- }
- }
+ return (ext->flags & DFM_EOF) != 0;
+}
- {
- struct dfm_fhuser_ext *ext = h->ext;
-
- if (ext)
- {
- ext->advance = 0;
- if (len)
- *len = ext->len - (ext->ptr - ext->line);
- return ext->ptr;
- }
- }
-
- return NULL;
-
-lossage:
- /* Come here on reading beyond eof or reading from a file already
- open for something else. */
- err_cond_fail ();
+/* Returns the current record in the file corresponding to
+ HANDLE. Aborts if reading from the file is necessary or at
+ end of file, so call dfm_eof() first. Sets *LINE to the line,
+ which is not null-terminated. The caller must not free or
+ modify the returned string. */
+void
+dfm_get_record (struct file_handle *h, struct len_string *line)
+{
+ struct dfm_reader_ext *ext = get_reader (h);
+ assert ((ext->flags & DFM_ADVANCE) == 0);
+ assert ((ext->flags & DFM_EOF) == 0);
+ assert (ext->pos <= ds_length (&ext->line));
+
+ line->string = ds_data (&ext->line) + ext->pos;
+ line->length = ds_length (&ext->line) - ext->pos;
+}
- return NULL;
+/* Expands tabs in the current line into the equivalent number of
+ spaces, if appropriate for this kind of file. Aborts if
+ reading from the file is necessary or at end of file, so call
+ dfm_eof() first.*/
+void
+dfm_expand_tabs (struct file_handle *h)
+{
+ struct dfm_reader_ext *ext = get_reader (h);
+ struct string temp;
+ size_t ofs, new_pos, tab_width;
+
+ assert ((ext->flags & DFM_ADVANCE) == 0);
+ assert ((ext->flags & DFM_EOF) == 0);
+ assert (ext->pos <= ds_length (&ext->line));
+
+ if (ext->flags & DFM_TABS_EXPANDED)
+ return;
+ ext->flags |= DFM_TABS_EXPANDED;
+
+ if (handle_get_mode (h) == MODE_BINARY
+ || handle_get_tab_width (h) == 0
+ || memchr (ds_c_str (&ext->line), '\t', ds_length (&ext->line)) == NULL)
+ return;
+
+ /* Expand tabs from ext->line into ext->scratch, and figure out
+ new value for ext->pos. */
+ tab_width = handle_get_tab_width (h);
+ ds_clear (&ext->scratch);
+ new_pos = 0;
+ for (ofs = 0; ofs < ds_length (&ext->line); ofs++)
+ {
+ unsigned char c;
+
+ if (ofs == ext->pos)
+ new_pos = ds_length (&ext->scratch);
+
+ c = ds_c_str (&ext->line)[ofs];
+ if (c != '\t')
+ ds_putc (&ext->scratch, c);
+ else
+ {
+ do
+ ds_putc (&ext->scratch, ' ');
+ while (ds_length (&ext->scratch) % tab_width != 0);
+ }
+ }
+
+ /* Swap ext->line and ext->scratch and set new ext->pos. */
+ temp = ext->line;
+ ext->line = ext->scratch;
+ ext->scratch = temp;
+ ext->pos = new_pos;
}
/* Causes dfm_get_record() to read in the next record the next time it
is executed on file HANDLE. */
void
-dfm_fwd_record (struct file_handle *h)
+dfm_forward_record (struct file_handle *h)
{
- struct dfm_fhuser_ext *ext = h->ext;
+ struct dfm_reader_ext *ext = get_reader (h);
+ ext->flags |= DFM_ADVANCE;
+}
- assert (h->class == &dfm_r_class);
- ext->advance = 1;
+/* Cancels the effect of any previous dfm_fwd_record() executed
+ on file HANDLE. Sets the current line to begin in the 1-based
+ column COLUMN. */
+void
+dfm_reread_record (struct file_handle *h, size_t column)
+{
+ struct dfm_reader_ext *ext = get_reader (h);
+ ext->flags &= ~DFM_ADVANCE;
+ if (column < 1)
+ ext->pos = 0;
+ else if (column > ds_length (&ext->line))
+ ext->pos = ds_length (&ext->line);
+ else
+ ext->pos = column - 1;
}
-/* Cancels the effect of any previous dfm_fwd_record() executed on
- file HANDLE. Sets the current line to begin in the 1-based column
- COLUMN, as with dfm_set_record but based on a column number instead
- of a character pointer. */
+/* Sets the current line to begin COLUMNS characters following
+ the current start. */
void
-dfm_bkwd_record (struct file_handle *h, int column)
+dfm_forward_columns (struct file_handle *h, size_t columns)
{
- struct dfm_fhuser_ext *ext = h->ext;
+ struct dfm_reader_ext *ext = get_reader (h);
+ dfm_reread_record (h, (ext->pos + 1) + columns);
+}
- assert (h->class == &dfm_r_class);
- ext->advance = 0;
- ext->ptr = ext->line + min ((int) ext->len + 1, column) - 1;
+/* Returns the 1-based column to which the line pointer in HANDLE
+ is set. Unless dfm_reread_record() or dfm_forward_columns()
+ have been called, this is 1. */
+size_t
+dfm_column_start (struct file_handle *h)
+{
+ struct dfm_reader_ext *ext = get_reader (h);
+ return ext->pos + 1;
}
-/* Sets the current line in HANDLE to NEW_LINE, which must point
- somewhere in the line last returned by dfm_get_record(). Used by
- DATA LIST FREE to strip the leading portion off the current line. */
+/* Pushes the filename and line number on the fn/ln stack. */
void
-dfm_set_record (struct file_handle *h, char *new_line)
+dfm_push (struct file_handle *h)
{
- struct dfm_fhuser_ext *ext = h->ext;
+ struct dfm_reader_ext *ext = get_reader (h);
+ if (h != inline_file)
+ err_push_file_locator (&ext->where);
+}
- assert (h->class == &dfm_r_class);
- ext->ptr = new_line;
+/* Pops the filename and line number from the fn/ln stack. */
+void
+dfm_pop (struct file_handle *h)
+{
+ struct dfm_reader_ext *ext = get_reader (h);
+ if (h != inline_file)
+ err_pop_file_locator (&ext->where);
}
-/* Returns the 0-based current column to which the line pointer in
- HANDLE is set. Unless dfm_set_record() or dfm_bkwd_record() have
- been called, this is 0. */
+/* DFM reader class. */
+static struct fh_ext_class dfm_r_class =
+{
+ 1,
+ N_("reading as a data file"),
+ close_reader,
+};
+\f
+/* file_handle extension structure. */
+struct dfm_writer_ext
+ {
+ struct file_ext file; /* Associated file. */
+ struct file_locator where; /* Current location in data file. */
+ char *bounce; /* Bounce buffer for fixed-size fields. */
+ };
+
+static struct fh_ext_class dfm_w_class;
+
+/* Opens a file handle for writing as a data file. */
int
-dfm_get_cur_col (struct file_handle *h)
+dfm_open_for_writing (struct file_handle *h)
{
- struct dfm_fhuser_ext *ext = h->ext;
+ struct dfm_writer_ext *ext;
+
+ if (h->class != NULL)
+ {
+ if (h->class == &dfm_w_class)
+ return 1;
+ else
+ {
+ msg (ME, _("Cannot write to file %s already opened for %s."),
+ handle_get_name (h), gettext (h->class->name));
+ err_cond_fail ();
+ return 0;
+ }
+ }
- assert (h->class == &dfm_r_class);
- return ext->ptr - ext->line;
+ ext = xmalloc (sizeof *ext);
+ ext->where.filename = handle_get_filename (h);
+ ext->where.line_number = 0;
+ ext->file.file = NULL;
+ ext->bounce = NULL;
+
+ msg (VM (1), _("%s: Opening data-file handle %s for writing."),
+ handle_get_filename (h), handle_get_name (h));
+
+ assert (h != NULL);
+ if (h == inline_file)
+ {
+ msg (ME, _("Cannot open the inline file for writing."));
+ goto error;
+ }
+
+ ext->file.filename = xstrdup (handle_get_filename (h));
+ ext->file.mode = "wb";
+ ext->file.file = NULL;
+ ext->file.sequence_no = NULL;
+ ext->file.param = NULL;
+ ext->file.postopen = NULL;
+ ext->file.preclose = NULL;
+
+ if (!fn_open_ext (&ext->file))
+ {
+ msg (ME, _("An error occurred while opening \"%s\" for writing "
+ "as a data file: %s."),
+ handle_get_filename (h), strerror (errno));
+ goto error;
+ }
+
+ h->class = &dfm_w_class;
+ h->ext = ext;
+ return 1;
+
+ error:
+ free (ext);
+ err_cond_fail ();
+ return 0;
}
/* Writes record REC having length LEN to the file corresponding to
int
dfm_put_record (struct file_handle *h, const char *rec, size_t len)
{
- char *ptr;
- size_t amt;
-
- if (h->class == NULL)
- {
- if (!open_file_w (h))
- return 0;
- }
- else if (h->class != &dfm_w_class)
- {
- msg (ME, _("Cannot write to file %s already opened for %s."),
- fh_handle_name (h), gettext (h->class->name));
- err_cond_fail ();
- return 0;
- }
+ struct dfm_writer_ext *ext;
- if (h->recform == FH_RF_FIXED && len < h->lrecl)
- {
- int ch;
+ assert (h != NULL);
+ assert (h->class == &dfm_w_class);
+ assert (h->ext != NULL);
- amt = h->lrecl;
- ptr = local_alloc (amt);
- memcpy (ptr, rec, len);
- ch = h->mode == FH_MD_CHARACTER ? ' ' : 0;
- memset (&ptr[len], ch, amt - len);
- }
- else
+ ext = h->ext;
+ if (handle_get_mode (h) == MODE_BINARY && len < handle_get_record_width (h))
{
- ptr = (char *) rec;
- amt = len;
+ size_t rec_width = handle_get_record_width (h);
+ if (ext->bounce == NULL)
+ ext->bounce = xmalloc (rec_width);
+ memcpy (ext->bounce, rec, len);
+ memset (&ext->bounce[len], 0, rec_width - len);
+ rec = ext->bounce;
+ len = rec_width;
}
- if (1 != fwrite (ptr, amt, 1, ((struct dfm_fhuser_ext *) h->ext)->file.file))
+ if (fwrite (rec, len, 1, ext->file.file) != 1)
{
- msg (ME, _("Error writing file %s: %s."), fh_handle_name (h),
- strerror (errno));
+ msg (ME, _("Error writing file %s: %s."),
+ handle_get_name (h), strerror (errno));
err_cond_fail ();
return 0;
}
- if (ptr != rec)
- local_free (ptr);
-
return 1;
}
-/* Pushes the filename and line number on the fn/ln stack. */
-void
-dfm_push (struct file_handle *h)
+/* Closes file handle H opened by dfm_open_for_writing(). */
+static void
+close_writer (struct file_handle *h)
{
- if (h != inline_file)
- err_push_file_locator (&h->where);
+ struct dfm_writer_ext *ext;
+
+ assert (h->class == &dfm_w_class);
+ ext = h->ext;
+
+ msg (VM (2), _("%s: Closing data-file handle %s."),
+ handle_get_filename (h), handle_get_name (h));
+ if (ext->file.file)
+ {
+ fn_close_ext (&ext->file);
+ free (ext->file.filename);
+ ext->file.filename = NULL;
+ }
+ free (ext->bounce);
+ free (ext);
}
-/* Pops the filename and line number from the fn/ln stack. */
-void
-dfm_pop (struct file_handle *h)
+/* DFM writer class. */
+static struct fh_ext_class dfm_w_class =
{
- if (h != inline_file)
- err_pop_file_locator (&h->where);
-}
+ 2,
+ N_("writing as a data file"),
+ close_writer,
+};
\f
/* BEGIN DATA...END DATA procedure. */
int
cmd_begin_data (void)
{
- struct dfm_fhuser_ext *ext;
+ struct dfm_reader_ext *ext;
/* FIXME: figure out the *exact* conditions, not these really
lenient conditions. */
if (vfm_source == NULL
- || vfm_source == &vfm_memory_stream
- || vfm_source == &vfm_disk_stream
- || vfm_source == &sort_stream)
+ || case_source_is_class (vfm_source, &storage_source_class))
{
msg (SE, _("This command is not valid here since the current "
- "input program does not access the inline file."));
+ "input program does not access the inline file."));
err_cond_fail ();
return CMD_FAILURE;
}
/* Initialize inline_file. */
msg (VM (1), _("inline file: Opening for reading."));
- inline_file->class = &dfm_r_class;
- inline_file->ext = xmalloc (sizeof (struct dfm_fhuser_ext));
- open_inline_file (inline_file->ext);
+ dfm_open_for_reading (inline_file);
+ ext = inline_file->ext;
+ ext->flags |= DFM_SAW_BEGIN_DATA;
/* We don't actually read from the inline file. The input procedure
is what reads from it. */
getl_prompt = GETL_PRPT_DATA;
- procedure (NULL, NULL, NULL);
-
+ procedure (NULL, NULL);
+
ext = inline_file->ext;
-
- if (ext && ext->line)
+ if (ext && (ext->flags & DFM_EOF) == 0)
{
msg (MW, _("Skipping remaining inline data."));
- for (read_record (inline_file); ext->line; read_record (inline_file))
- ;
+ while ((ext->flags & DFM_EOF) == 0)
+ read_record (inline_file);
}
assert (inline_file->ext == NULL);
return CMD_SUCCESS;
}
-
-static struct fh_ext_class dfm_r_class =
-{
- 1,
- N_("reading as a data file"),
- dfm_close,
-};
-
-static struct fh_ext_class dfm_w_class =
-{
- 2,
- N_("writing as a data file"),
- dfm_close,
-};