implementation that was already there.
Update our string ADTs, struct string and struct len_string. Get rid
of pool support, which was largely unused. Rename lots of functions
to have more obvious or consistent names.
Fix a few miscellaneous bugs.
+Sun May 30 18:19:03 2004 Ben Pfaff <blp@gnu.org>
+
+ * config.ac: Check for valgrind/valgrind.h.
+
Mon Mar 29 15:22:48 2004 Ben Pfaff <blp@gnu.org>
* TODO: Updated.
-Time-stamp: <2004-04-24 22:23:04 blp>
+Time-stamp: <2004-05-30 18:09:06 blp>
What Ben's working on now.
--------------------------
+Does SET work correctly?
+
Update q2c input format description.
Rewrite output subsystem, break into multiple processes.
dnl Checks for header files.
AC_CHECK_HEADERS([limits.h memory.h sys/stat.h sys/time.h sys/types.h \
- fpu_control.h sys/mman.h sys/wait.h ieeefp.h fenv.h])
+ fpu_control.h sys/mman.h sys/wait.h ieeefp.h fenv.h \
+ valgrind/valgrind.h])
AC_HEADER_STAT
AC_HEADER_STDC
AC_HEADER_TIME
+Sun May 30 22:44:25 2004 Ben Pfaff <blp@gnu.org>
+
+ * pspp.texi: Update FILE HANDLE, DATA LIST FREE, DATA LIST LIST
+ documentation to reflect latest changes.
+
Mon Apr 19 22:46:37 2004 Ben Pfaff <blp@gnu.org>
* pspp.texi: Minor updates to data file and portable file
@display
DATA LIST FREE
+ [(@{TAB,'c'@}, @dots{})]
[@{NOTABLE,TABLE@}]
FILE='filename'
END=end_var
var_list *
@end display
-In free format, the input data is structured as a series of comma- or
-whitespace-delimited fields (end of line is one form of whitespace; it
-is not treated specially). Field contents may be surrounded by matched
-pairs of apostrophes (@samp{'}) or quotes (@samp{"}), or they may be
-unenclosed. For any type of field leading white space (up to the
-apostrophe or quote, if any) is not included in the field.
-
-Multiple consecutive delimiters are equivalent to a single delimiter.
-To specify an empty field, write an empty set of single or double
-quotes; for instance, @samp{""}.
+In free format, the input data is, by default, structured as a series
+of fields separated by spaces, tabs, commas, or line breaks. Each
+field's content may be unquoted, or it may be quoted with a pairs of
+apostrophes (@samp{'}) or double quotes (@samp{"}). Unquoted white
+space separates fields but is not part of any field. Any mix of
+spaces, tabs, and line breaks is equivalent to a single space for the
+purpose of separating fields, but consecutive commas will skip a
+field.
+
+Alternatively, delimiters can be specified explicitly, as a
+parenthesized, comma-separated list of single-character strings
+immediately following FREE. The word TAB may also be used to specify
+a tab character as a delimiter. When delimiters are specified
+explicitly, only the given characters, plus line breaks, separate
+fields. Furthermore, leading spaces at the beginnings of fields are
+not trimmed, consecutive delimiters define empty fields, and no form
+of quoting is allowed.
The NOTABLE and TABLE subcommands are as in @cmd{DATA LIST FIXED} above.
NOTABLE is the default.
@display
DATA LIST LIST
+ [(@{TAB,'c'@}, @dots{})]
[@{NOTABLE,TABLE@}]
FILE='filename'
END=end_var
@display
FILE HANDLE handle_name
/NAME='filename'
- /RECFORM=@{VARIABLE,FIXED,SPANNED@}
+ /MODE=@{CHARACTER,IMAGE@}
/LRECL=rec_len
- /MODE=@{CHARACTER,IMAGE,BINARY,MULTIPUNCH,360@}
+ /TABWIDTH=tab_width
@end display
-Use @cmd{FILE HANDLE} to define the attributes of a file that does
-not use conventional variable-length records terminated by new-line
-characters.
+Use @cmd{FILE HANDLE} to associate a file handle name with a file and
+its attributes, so that later commands can refer to the file by its
+handle name. Because names of text files can be specified directly on
+commands that access files, @cmd{FILE HANDLE} is only needed when a
+file is not an ordinary file containing lines of text. However,
+@cmd{FILE HANDLE} may be used even for text files, and it may be
+easier to specify a file's name once and later refer to it by an
+abstract handle.
Specify the file handle name as an identifier. Any given identifier may
only appear once in a PSPP run. File handles may not be reassigned to a
The NAME subcommand specifies the name of the file associated with the
handle. It is the only required subcommand.
-The RECFORM subcommand specifies how the file is laid out. VARIABLE
-specifies variable-length lines terminated with new-lines, and it is the
-default. FIXED specifies fixed-length records. SPANNED is not
-supported.
-
-LRECL specifies the length of fixed-length records. It is required if
-@code{/RECFORM FIXED} is specified.
+MODE specifies a file mode. In CHARACTER mode, the default, the data
+file is opened in ANSI C text mode, so that local end of line
+conventions are followed, and each text line is read as one record.
+In CHARACTER mode, most input programs will expand tabs to spaces
+(@cmd{DATA LIST FREE} with explicitly specified delimiters is an
+exception). By default, each tab is 4 characters wide, but an
+alternate width may be specified on TABWIDTH. A tab width of 0
+suppresses tab expansion entirely.
-MODE specifies a file mode. CHARACTER, the default, causes the data
-file to be opened in ANSI C text mode. BINARY causes the data file to
-be opened in ANSI C binary mode. The other possibilities are not
-supported.
+By contrast, in BINARY mode, the data file is opened in ANSI C binary
+mode and records are a fixed length. In BINARY mode, LRECL specifies
+the record length in bytes, with a default of 1024. Tab characters
+are never expanded to spaces in binary mode.
@node INPUT PROGRAM, LIST, FILE HANDLE, Data Input and Output
@section INPUT PROGRAM
The aggregation functions listed above exclude all user-missing values
from calculations. To include user-missing values, insert a period
(@samp{.}) between the function name and left parenthesis
-(e.g.~@samp{SUM.}).
+(e.g.@: @samp{SUM.}).
Normally, only a single case (for SD and SD., two cases) need be
non-missing in each group for the aggregate variable to be
The 200-byte segment is divided into five 40-byte sections, each of
which represents the string @code{@var{charset} SPSS PORT FILE} in a
different character set encoding, where @var{charset} is the name of
-the character set used in the file, e.g. @code{ASCII} or
+the character set used in the file, e.g.@: @code{ASCII} or
@code{EBCDIC}. Each string is padded on the right with spaces in its
respective character set.
+Sun May 30 18:35:19 2004 Ben Pfaff <blp@gnu.org>
+
+ Fully implement arbitrary delimiters on DATA LIST, extending the
+ half implementation that was already there.
+
+ * data-list.c: (struct data_list_pgm) Remove `delim', add
+ `delims', `delim_cnt'.
+ (cmd_data_list) Initialize new members. Parse delimiters and
+ clean up code a bit.
+ (cut_field) Extract fields with arbitrary delimiters. Also, fix
+ handling of leading commas.
+ (read_from_data_list_fixed) Expand tabs. Adapt to new DFM
+ interfaces.
+ (read_from_data_list_free) Adapt to new DFM interfaces.
+ (read_from_data_list_list) Ditto.
+ (repeating_data_trns_proc) Ditto.
+
+ * dfm.c: Split up reader and writer into separate code, because
+ they do different things. Use struct string instead of explicit
+ allocation code, for clarity.
+ (enum dfm_reader_flags) New enum.
+ (struct dfm_fhuser_ext) Removed.
+ (struct dfm_reader_ext) New.
+ (get_reader) New function, used by just about all the reader
+ functions.
+ (dfm_close) Removed.
+ (close_reader) New function.
+ (dfm_open_for_reading) Rewrite initialization of dfm_fhuser_ext.
+ (dfm_open_for_writing) Ditto.
+ (macro force_line_buffer_expansion) Removed.
+ (count_tabs) Removed.
+ (tabs_to_spaces) Removed.
+ (read_record) Deal with new dfm_reader_ext. Use struct string
+ functions. Don't convert tabs to spaces.
+ (dfm_eof) New function.
+ (dfm_get_record) Changed interface, rewrote.
+ (dfm_expand_tabs) New function.
+ (dfm_fwd_record) Renamed dfm_forward_record(), updated to new
+ dfm_reader_ext, rewritten.
+ (dfm_bkwd_record) Renamed dfm_reread_record(), updated to new
+ dfm_reader_ext, rewritten.
+ (dfm_set_record) Removed in favor of dfm_forward_columns().
+ (dfm_forward_columns) New function.
+ (dfm_get_cur_col) Renamed dfm_column_start, updated to new
+ dfm_reader_ext, rewritten.
+ (static var dfm_r_class) Use close_reader for the destructor.
+ (struct dfm_writer_ext) New.
+ (dfm_put_record) Updated to new dfm_writer_ext, rewritten. Uses
+ bounce buffer now instead of local allocation.
+ (close_writer) New function.
+ (static var dfm_writer_ext) Use close_writer for destructor.
+ (cmd_begin_data) Adapt to new dfm_reader_ext.
+
+ * file-handle.q: Add support for per-file tab width.
+ (struct private_file_handle) Add tab_width member.
+ (q2c specifications) Add tabwidth subcommand.
+ (cmd_file_handle) Put parsed tab width into private_file_handle.
+ (create_file_handle) Set default tab width.
+ (handle_get_tab_width) New function.
+
+ * file-type.c: (file_type_source_read) Adapt to new DFM interface.
+
+ * inpt-pgm.c: (reread_trns_proc) Ditto.
+
+ * matrix-data.c: (context) Ditto.
+ (another_token) Ditto.
+ (mget_token) Ditto.
+ (force_eol) Ditto.
+
+Sun May 30 18:33:59 2004 Ben Pfaff <blp@gnu.org>
+
+ * casefile.c: (casefile_destroy) Fix memory leak by freeing
+ cf->filename.
+ (casereader_destroy) Don't close file descriptor -1.
+
+ * recode.c: (cmd_recode) Fix memory leak.
+
+ * set.q: (q2c specifications) Fix typo in user message.
+
+ * str.c: (st_bare_pad_len_copy) Change memcpy to memmove to avoid
+ undefined behavior for overlapping arguments.
+
+Sun May 30 18:31:48 2004 Ben Pfaff <blp@gnu.org>
+
+ * casefile.c: valgrind doesn't implement posix_fadvise() yet, so
+ don't call it when we're running under valgrind.
+ (call_posix_fadvise) New function.
+ (casefile_to_disk) Use call_posix_fadvise().
+ (reader_open_file) Ditto.
+
+Sun May 30 18:20:12 2004 Ben Pfaff <blp@gnu.org>
+
+ Update our string ADTs, struct string and struct len_string. Get
+ rid of pool support, which was largely unused. Rename lots of
+ functions to have more obvious or consistent names.
+
+ * ascii.c: Get rid of ascii_pool. It was only used for string
+ allocations.
+ (ascii_open_global) Don't create ascii_pool.
+ (ascii_close_driver) Don't destroy ascii_pool.
+ (ascii_postopen_driver) Don't use pool.
+ (ascii_close_driver) Destroy strings manually.
+
+ * str.c: (ds_create) Remove pool argument, all references updated.
+ (ds_init) Ditto.
+ (ds_replace) Remove pool support, make more efficient when we
+ don't need to reallocate.
+ (ds_destroy) Remove pool support.
+ (ds_rpad) New function.
+ (ds_size) Renamed ds_capacity(), all references updated.
+ (ds_value) Renamed ds_c_str(), all references updated.
+ (ds_concat) Renamed ds_puts(), all references updated.
+ (ds_concat_buffer) Renamed ds_concat(), all references updated.
+ (ds_putchar) Renamed ds_putc(), all references updated.
+ (ds_getline) Renamed ds_gets(), all references updated.
+ (ls_create) Remove pool argument, all references updated.
+ (ls_create_buffer) Ditto.
+ (ls_destroy) Removed pool support.
+ (ls_value) Renamed ls_c_str(), all references updated.
+
+ * str.h: (ls_length) [__GNUC__] Add inline version.
+ (ls_c_str) [__GNUC__] Add inline version.
+ (ls_end) [__GNUC__] Add inline version.
+ (struct string) Remove pool member. Rename `size' to `capacity',
+ all references updated.
+
+ * tab.c: (text_format) Instead of using pool argument to
+ ls_create_buffer(), call pool_register() on allocated data.
+
Mon Apr 26 22:40:07 2004 Ben Pfaff <blp@gnu.org>
We're abusing the current ASCII driver by telling it to allocate a
if (token == T_STRING)
{
ds_truncate (&tokstr, 255);
- dest_label[n_dest - 1] = xstrdup (ds_value (&tokstr));
+ dest_label[n_dest - 1] = xstrdup (ds_c_str (&tokstr));
lex_get ();
}
}
lex_match (',');
if (token == T_STRING)
{
- arg[i].c = xstrdup (ds_value (&tokstr));
+ arg[i].c = xstrdup (ds_c_str (&tokstr));
type = ALPHA;
}
else if (token == T_NUM)
#endif
};
-static struct pool *ascii_pool;
-
static int postopen (struct file_ext *);
static int preclose (struct file_ext *);
static int
ascii_open_global (struct outp_class *this UNUSED)
{
- ascii_pool = pool_create ();
return 1;
}
+
static int
ascii_close_global (struct outp_class *this UNUSED)
{
- pool_destroy (ascii_pool);
return 1;
}
this->length = x->l * this->vert;
if (ls_null_p (&x->ops[OPS_FORMFEED]))
- ls_create (ascii_pool, &x->ops[OPS_FORMFEED], "\f");
+ ls_create (&x->ops[OPS_FORMFEED], "\f");
if (ls_null_p (&x->ops[OPS_NEWLINE])
- || !strcmp (ls_value (&x->ops[OPS_NEWLINE]), "default"))
+ || !strcmp (ls_c_str (&x->ops[OPS_NEWLINE]), "default"))
{
- ls_create (ascii_pool, &x->ops[OPS_NEWLINE], "\n");
+ ls_create (&x->ops[OPS_NEWLINE], "\n");
x->file.mode = "wt";
}
c[0] = '+';
break;
}
- ls_create (ascii_pool, &x->box[i], c);
+ ls_create (&x->box[i], c);
}
}
ascii_close_driver (struct outp_driver *this)
{
struct ascii_driver_ext *x = this->ext;
+ int i;
assert (this->driver_open == 1);
msg (VM (2), _("%s: Beginning closing..."), this->name);
x = this->ext;
+ for (i = 0; i < OPS_COUNT; i++)
+ ls_destroy (&x->ops[i]);
+ for (i = 0; i < LNS_COUNT; i++)
+ ls_destroy (&x->box[i]);
+ for (i = 0; i < FSTY_COUNT; i++)
+ ls_destroy (&x->fonts[i]);
if (x->lines != NULL)
{
int line;
int cat, subcat;
const char *value;
- value = ds_value (val);
+ value = ds_c_str (val);
if (!strncmp (key, "box[", 4))
{
char *tail;
}
if (!ls_null_p (&x->box[indx]))
msg (SW, _("Duplicate value for key `%s'."), key);
- ls_create (ascii_pool, &x->box[indx], value);
+ ls_create (&x->box[indx], value);
return;
}
assert (0);
abort ();
}
- ls_create (ascii_pool, s, value);
+ ls_create (s, value);
}
break;
case font_string_arg:
{
if (!strcmp (value, "overstrike"))
{
- ls_destroy (ascii_pool, &x->fonts[subcat]);
+ ls_destroy (&x->fonts[subcat]);
return;
}
- ls_create (ascii_pool, &x->fonts[subcat], value);
+ ls_create (&x->fonts[subcat], value);
}
break;
case boolean_arg:
struct ascii_driver_ext *x = f->param;
struct len_string *s = &x->ops[OPS_INIT];
- if (!ls_empty_p (s) && fwrite (ls_value (s), ls_length (s), 1, f->file) < 1)
+ if (!ls_empty_p (s) && fwrite (ls_c_str (s), ls_length (s), 1, f->file) < 1)
{
msg (ME, _("ASCII output driver: %s: %s"),
f->filename, strerror (errno));
struct ascii_driver_ext *x = f->param;
struct len_string *d = &x->ops[OPS_DONE];
- if (!ls_empty_p (d) && fwrite (ls_value (d), ls_length (d), 1, f->file) < 1)
+ if (!ls_empty_p (d) && fwrite (ls_c_str (d), ls_length (d), 1, f->file) < 1)
{
msg (ME, _("ASCII output driver: %s: %s"),
f->filename, strerror (errno));
int max_y;
/* Current position in string, character following end of string. */
- const char *s = ls_value (&t->s);
+ const char *s = ls_c_str (&t->s);
const char *end = ls_end (&t->s);
/* Temporary struct outp_text to pass to low-level function. */
int x = t->x;
int y = t->y;
- char *s = ls_value (&t->s);
+ char *s = ls_c_str (&t->s);
/* Expand the line with the assumption that S takes up LEN character
spaces (sometimes it takes up less). */
if (remaining >= len)
{
- memcpy (line_p, ls_value (box), len);
+ memcpy (line_p, ls_c_str (box), len);
line_p += len;
remaining -= len;
}
{
if (!commit_line_buf (this))
return 0;
- output_string (this, ls_value (box), ls_end (box));
+ output_string (this, ls_c_str (box), ls_end (box));
remaining = LINE_BUF_SIZE - (line_p - line_buf);
}
}
abort ();
}
if (off)
- output_string (this, ls_value (off), ls_end (off));
+ output_string (this, ls_c_str (off), ls_end (off));
}
/* Turn on new font. */
abort ();
}
if (on)
- output_string (this, ls_value (on), ls_end (on));
+ output_string (this, ls_c_str (on), ls_end (on));
}
ep = bp + 1;
}
}
- output_string (this, ls_value (newline), ls_end (newline));
+ output_string (this, ls_c_str (newline), ls_end (newline));
}
}
}
for (cp = s, i = 0; i < x->top_margin; i++)
{
- memcpy (cp, ls_value (&x->ops[OPS_NEWLINE]), nl_len);
+ memcpy (cp, ls_c_str (&x->ops[OPS_NEWLINE]), nl_len);
cp += nl_len;
}
output_string (this, s, &s[total_len]);
len = min ((int) strlen (outp_title), x->w);
memcpy (s, outp_title, len);
}
- memcpy (&s[x->w], ls_value (&x->ops[OPS_NEWLINE]), nl_len);
+ memcpy (&s[x->w], ls_c_str (&x->ops[OPS_NEWLINE]), nl_len);
output_string (this, s, &s[total_len]);
memset (s, ' ', x->w);
len = min ((int) strlen (string), x->w);
memcpy (s, string, len);
}
- memcpy (&s[x->w], ls_value (&x->ops[OPS_NEWLINE]), nl_len);
+ memcpy (&s[x->w], ls_c_str (&x->ops[OPS_NEWLINE]), nl_len);
output_string (this, s, &s[total_len]);
output_string (this, &s[x->w], &s[total_len]);
}
s = xrealloc (s, total_len);
for (cp = s, i = 0; i < x->bottom_margin; i++)
{
- memcpy (cp, ls_value (&x->ops[OPS_NEWLINE]), nl_len);
+ memcpy (cp, ls_c_str (&x->ops[OPS_NEWLINE]), nl_len);
cp += nl_len;
}
- memcpy (cp, ls_value (&x->ops[OPS_FORMFEED]), ff_len);
+ memcpy (cp, ls_c_str (&x->ops[OPS_FORMFEED]), ff_len);
if ( x->paginate )
output_string (this, s, &s[total_len]);
if (line_p != line_buf && !commit_line_buf (this))
#include "var.h"
#include "workspace.h"
+#ifdef HAVE_VALGRIND_VALGRIND_H
+#include <valgrind/valgrind.h>
+#endif
+
#define IO_BUF_SIZE 8192
/* A casefile is a sequentially accessible array of immutable
if (cf->filename != NULL && remove (cf->filename) == -1)
msg (ME, _("%s: Removing temporary file: %s."),
cf->filename, strerror (errno));
+ free (cf->filename);
free (cf->buffer);
return 1;
}
+static void
+call_posix_fadvise (int fd UNUSED,
+ off_t offset UNUSED, off_t len UNUSED,
+ int advice UNUSED)
+{
+#ifdef HAVE_VALGRIND_VALGRIND_H
+ /* Valgrind doesn't know about posix_fadvise() as of this
+ writing. */
+ if (RUNNING_ON_VALGRIND)
+ return;
+#endif
+
+#ifdef HAVE_POSIX_FADVISE
+ posix_fadvise (fd, offset, len, advice);
+#endif
+}
+
/* If CF is currently stored in memory, writes it to disk. Readers, if any,
retain their current positions. */
void
cf->storage = DISK;
if (!make_temp_file (&cf->fd, &cf->filename))
err_failure ();
-#if HAVE_POSIX_FADVISE
- posix_fadvise (cf->fd, 0, 0, POSIX_FADV_SEQUENTIAL);
-#endif
+ call_posix_fadvise (cf->fd, 0, 0, POSIX_FADV_SEQUENTIAL);
cf->buffer = xmalloc (cf->buffer_size);
memset (cf->buffer, 0, cf->buffer_size);
}
else
file_ofs = 0;
-#if HAVE_POSIX_FADVISE
- posix_fadvise (reader->fd, file_ofs, 0, POSIX_FADV_SEQUENTIAL);
-#endif
+ call_posix_fadvise (reader->fd, file_ofs, 0, POSIX_FADV_SEQUENTIAL);
if (lseek (reader->fd, file_ofs, SEEK_SET) != file_ofs)
msg (FE, _("%s: Seeking temporary file: %s."),
reader->cf->filename, strerror (errno));
else
free (reader->buffer);
- if (reader->cf->fd == -1)
- reader->cf->fd = reader->fd;
- else
- safe_close (reader->fd);
-
+ if (reader->fd != -1)
+ {
+ if (reader->cf->fd == -1)
+ reader->cf->fd = reader->fd;
+ else
+ safe_close (reader->fd);
+ }
+
free (reader);
}
assert (word_cnt < sizeof words / sizeof *words);
if (token == T_ID)
- words[word_cnt++] = xstrdup (ds_value (&tokstr));
+ words[word_cnt++] = xstrdup (ds_c_str (&tokstr));
else
words[word_cnt++] = xstrdup ("-");
if (!lex_force_string ())
return CMD_FAILURE;
- if (remove (ds_value (&tokstr)) == -1)
+ if (remove (ds_c_str (&tokstr)) == -1)
{
msg (SW, _("Error removing `%s': %s."),
- ds_value (&tokstr), strerror (errno));
+ ds_c_str (&tokstr), strerror (errno));
return CMD_FAILURE;
}
lex_get ();
if (!lex_force_string ())
return CMD_FAILURE;
- cmd = ds_value (&tokstr);
+ cmd = ds_c_str (&tokstr);
string = 1;
}
else
cur = &c->crit.s[n++];
cur->type = CNT_SINGLE;
cur->s = malloc (len + 1);
- st_pad_copy (cur->s, ds_value (&tokstr), len + 1);
+ st_pad_copy (cur->s, ds_c_str (&tokstr), len + 1);
lex_get ();
lex_match (',');
struct error e;
struct string title;
- ds_init (NULL, &title, 64);
+ ds_init (&title, 64);
if (!getl_reading_script)
- ds_concat (&title, _("data-file error: "));
+ ds_puts (&title, _("data-file error: "));
if (i->f1 == i->f2)
ds_printf (&title, _("(column %d"), i->f1);
else
e.class = DE;
err_location (&e.where);
- e.title = ds_value (&title);
+ e.title = ds_c_str (&title);
e.text = buf;
err_vmsg (&e);
int eof; /* End of file encountered. */
int nrec; /* Number of records. */
size_t case_size; /* Case size in bytes. */
- int delim; /* Specified delimeter */
+ char *delims; /* Delimiters if any; not null-terminated. */
+ size_t delim_cnt; /* Number of delimiter, or 0 for spaces. */
};
static int parse_fixed (struct data_list_pgm *);
dls->end = NULL;
dls->eof = 0;
dls->nrec = 0;
- dls->delim=0;
+ dls->delims = NULL;
+ dls->delim_cnt = 0;
dls->first = dls->last = NULL;
while (token != '/')
}
else if (token == T_ID)
{
- /* Must match DLS_* constants. */
- static const char *id[] = {"FIXED", "FREE", "LIST", "NOTABLE",
- "TABLE", NULL};
- const char **p;
- int index;
-
- for (p = id; *p; p++)
- if (lex_id_match (*p, tokid))
- break;
- if (*p == NULL)
- {
- lex_error (NULL);
- goto error;
- }
-
- lex_get ();
+ if (lex_match_id ("NOTABLE"))
+ table = 0;
+ else if (lex_match_id ("TABLE"))
+ table = 1;
+ else
+ {
+ int type;
+ if (lex_match_id ("FIXED"))
+ type = DLS_FIXED;
+ else if (lex_match_id ("FREE"))
+ type = DLS_FREE;
+ else if (lex_match_id ("LIST"))
+ type = DLS_LIST;
+ else
+ {
+ lex_error (NULL);
+ goto error;
+ }
- index = p - id;
- if (index < 3)
- {
if (dls->type != -1)
{
msg (SE, _("Only one of FIXED, FREE, or LIST may "
- "be specified."));
+ "be specified."));
goto error;
}
-
- dls->type = index;
- }
- else
- table = index - 3;
- }
- else if (token=='(') {
- lex_get();
- if (lex_match_id ("TAB")) {
- dls->delim='\t';
+ dls->type = type;
+
+ if ((dls->type == DLS_FREE || dls->type == DLS_LIST)
+ && lex_match ('('))
+ {
+ while (!lex_match (')'))
+ {
+ int delim;
+
+ if (lex_match_id ("TAB"))
+ delim = '\t';
+ else if (token == T_STRING && tokstr.length == 1)
+ delim = tokstr.string[0];
+ else
+ {
+ lex_error (NULL);
+ goto error;
+ }
+
+ dls->delims = xrealloc (dls->delims, dls->delim_cnt + 1);
+ dls->delims[dls->delim_cnt++] = delim;
+
+ lex_match (',');
+ }
+ }
+ }
}
- lex_get();
- }
else
{
lex_error (NULL);
\f
/* Input procedure. */
-/* Extracts a field from the current position in the current record.
- Fields can be unquoted or quoted with single- or double-quote
- characters. *RET_LEN is set to the field length, *RET_CP is set to
- the field itself. After parsing the field, sets the current
- position in the record to just past the field. Returns 0 on
- failure or a 1-based column number indicating the beginning of the
- field on success. */
+/* Extracts a field from the current position in the current
+ record. Fields can be unquoted or quoted with single- or
+ double-quote characters. *FIELD is set to the field content.
+ After parsing the field, sets the current position in the
+ record to just past the field and any trailing delimiter.
+ END_BLANK is used internally; it should be initialized by the
+ caller to 0 and left alone afterward. Returns 0 on failure or
+ a 1-based column number indicating the beginning of the field
+ on success. */
static int
-cut_field (const struct data_list_pgm *dls, char **ret_cp, int *ret_len)
+cut_field (const struct data_list_pgm *dls, struct len_string *field,
+ int *end_blank)
{
- char *cp, *ep;
- int len;
-
- cp = dfm_get_record (dls->handle, &len);
- if (!cp)
- return 0;
-
- ep = cp + len;
- if (dls->delim != 0) {
- if (*cp==dls->delim) {
- cp++;
- }
- } else {
+ struct len_string line;
+ char *cp;
+ size_t column_start;
- /* Skip leading whitespace and commas. */
- while ((isspace ((unsigned char) *cp) || *cp == ',') && cp < ep)
- cp++;
- }
- if (cp >= ep)
+ if (dfm_eof (dls->handle))
return 0;
+ if (dls->delim_cnt == 0)
+ dfm_expand_tabs (dls->handle);
+ dfm_get_record (dls->handle, &line);
- /* Three types of fields: quoted with ', quoted with ", unquoted. */
- /* Quoting does not escape the effects of delimiters for explicitly */
- /* specified delims */
- /* (consistency with SPSS doco: */
- /* For data with explicitly specified value delimiters (for example, */
- /* DATA LIST FREE (","): */
- /* - Multiple delimiters without any intervening space can be used */
- /* to specify missing data. */
- /* - The specified delimiters cannot occur within a data value, even */
- /* if you enclose the value in quotation marks or apostrophes. */
- if (dls->delim==0 && (*cp == '\'' || *cp == '"'))
+ cp = ls_c_str (&line);
+ if (dls->delim_cnt == 0)
{
- int quote = *cp;
-
- *ret_cp = ++cp;
- while (cp < ep && *cp != quote)
- cp++;
- if (dls->delim!=0) {
- while(cp<ep && *cp!=dls->delim) {
- cp++;
+ /* Skip leading whitespace. */
+ while (cp < ls_end (&line) && isspace ((unsigned char) *cp))
+ cp++;
+ if (cp >= ls_end (&line))
+ return 0;
+
+ /* Handle actual data, whether quoted or unquoted. */
+ if (*cp == '\'' || *cp == '"')
+ {
+ int quote = *cp;
+
+ field->string = ++cp;
+ while (cp < ls_end (&line) && *cp != quote)
+ cp++;
+ field->length = cp - field->string;
+ if (cp < ls_end (&line))
+ cp++;
+ else
+ msg (SW, _("Quoted string missing terminating `%c'."), quote);
}
- }
- *ret_len = cp - *ret_cp;
- if (cp < ep)
- cp++;
else
- msg (SW, _("Scope of string exceeds line."));
+ {
+ field->string = cp;
+ while (cp < ls_end (&line)
+ && !isspace ((unsigned char) *cp) && *cp != ',')
+ cp++;
+ field->length = cp - field->string;
+ }
+
+ /* Skip trailing whitespace and a single comma if present. */
+ while (cp < ls_end (&line) && isspace ((unsigned char) *cp))
+ cp++;
+ if (cp < ls_end (&line) && *cp == ',')
+ cp++;
}
- else
+ else
{
- *ret_cp = cp;
- if (dls->delim!=0) {
- while(cp<ep && *cp!=dls->delim) {
- cp++;
+ if (cp >= ls_end (&line))
+ {
+ int column = dfm_column_start (dls->handle);
+ /* A blank line or a line that ends in \t has a
+ trailing blank field. */
+ if (column == 1 || (column > 1 && cp[-1] == '\t'))
+ {
+ if (*end_blank == 0)
+ {
+ *end_blank = 1;
+ field->string = ls_end (&line);
+ field->length = 0;
+ dfm_forward_record (dls->handle);
+ return column;
+ }
+ else
+ {
+ *end_blank = 0;
+ return 0;
+ }
+ }
+ else
+ return 0;
+ }
+ else
+ {
+ field->string = cp;
+ while (cp < ls_end (&line)
+ && memchr (dls->delims, *cp, dls->delim_cnt) == NULL)
+ cp++;
+ field->length = cp - field->string;
+ if (cp < ls_end (&line))
+ cp++;
}
- } else {
-
- while (cp < ep && !isspace ((unsigned char) *cp) && *cp != ',')
- cp++;
- }
- *ret_len = cp - *ret_cp;
}
-
- {
- int beginning_column;
-
- dfm_set_record (dls->handle, *ret_cp);
- beginning_column = dfm_get_cur_col (dls->handle) + 1;
+
+ dfm_forward_columns (dls->handle, field->string - line.string);
+ column_start = dfm_column_start (dls->handle);
- dfm_set_record (dls->handle, cp);
+ dfm_forward_columns (dls->handle, cp - field->string);
- return beginning_column;
- }
+ return column_start;
}
typedef int data_list_read_func (const struct data_list_pgm *, struct ccase *);
struct dls_var_spec *var_spec = dls->first;
int i;
- if (!dfm_get_record (dls->handle, NULL))
+ if (dfm_eof (dls->handle))
return -2;
for (i = 1; i <= dls->nrec; i++)
{
- int len;
- char *line = dfm_get_record (dls->handle, &len);
+ struct len_string line;
- if (!line)
+ if (dfm_eof (dls->handle))
{
/* Note that this can't occur on the first record. */
msg (SW, _("Partial case of %d of %d records discarded."),
i - 1, dls->nrec);
return -2;
}
+ dfm_expand_tabs (dls->handle);
+ dfm_get_record (dls->handle, &line);
for (; var_spec && i == var_spec->rec; var_spec = var_spec->next)
{
struct data_in di;
- data_in_finite_line (&di, line, len, var_spec->fc, var_spec->lc);
+ data_in_finite_line (&di, ls_c_str (&line), ls_length (&line),
+ var_spec->fc, var_spec->lc);
di.v = &c->data[var_spec->fv];
di.flags = 0;
di.f1 = var_spec->fc;
data_in (&di);
}
- dfm_fwd_record (dls->handle);
+ dfm_forward_record (dls->handle);
}
return -1;
struct ccase *c)
{
struct dls_var_spec *var_spec;
- char *field;
- int len;
+ int end_blank = 0;
for (var_spec = dls->first; var_spec; var_spec = var_spec->next)
{
+ struct len_string field;
int column;
/* Cut out a field and read in a new record if necessary. */
for (;;)
{
- column = cut_field (dls, &field, &len);
+ column = cut_field (dls, &field, &end_blank);
if (column != 0)
break;
- if (dfm_get_record (dls->handle, NULL))
- dfm_fwd_record (dls->handle);
- if (!dfm_get_record (dls->handle, NULL))
+ if (!dfm_eof (dls->handle))
+ dfm_forward_record (dls->handle);
+ if (dfm_eof (dls->handle))
{
if (var_spec != dls->first)
msg (SW, _("Partial case discarded. The first variable "
- "missing was %s."), var_spec->name);
+ "missing was %s."), var_spec->name);
return -2;
}
}
{
struct data_in di;
- di.s = field;
- di.e = field + len;
+ di.s = ls_c_str (&field);
+ di.e = ls_end (&field);
di.v = &c->data[var_spec->fv];
di.flags = 0;
di.f1 = column;
struct ccase *c)
{
struct dls_var_spec *var_spec;
- char *field;
- int len;
+ int end_blank = 0;
- if (!dfm_get_record (dls->handle, NULL))
+ if (dfm_eof (dls->handle))
return -2;
for (var_spec = dls->first; var_spec; var_spec = var_spec->next)
{
+ struct len_string field;
+ int column;
+
/* Cut out a field and check for end-of-line. */
- int column = cut_field (dls, &field, &len);
-
+ column = cut_field (dls, &field, &end_blank);
if (column == 0)
{
- if (get_undefined() )
+ if (get_undefined ())
msg (SW, _("Missing value(s) for all variables from %s onward. "
- "These will be filled with the system-missing value "
- "or blanks, as appropriate."),
+ "These will be filled with the system-missing value "
+ "or blanks, as appropriate."),
var_spec->name);
- for (; var_spec; var_spec = var_spec->next)
+ for (; var_spec; var_spec = var_spec->next)
{
int width = get_format_var_width (&var_spec->input);
if (width == 0)
{
struct data_in di;
- di.s = field;
- di.e = field + len;
+ di.s = ls_c_str (&field);
+ di.e = ls_end (&field);
di.v = &c->data[var_spec->fv];
di.flags = 0;
di.f1 = column;
}
}
- dfm_fwd_record (dls->handle);
+ dfm_forward_record (dls->handle);
return -1;
}
{
struct repeating_data_trns *t = (struct repeating_data_trns *) trns;
- char *line; /* Current record. */
- int len; /* Length of current record. */
+ struct len_string line; /* Current record. */
int starts_beg; /* Starting column. */
int starts_end; /* Ending column. */
int occurs; /* Number of repetitions. */
int length; /* Length of each occurrence. */
- int cont_beg; /* Starting column for continuation lines. */
- int cont_end; /* Ending column for continuation lines. */
+ int cont_beg; /* Starting column for continuation lines. */
+ int cont_end; /* Ending column for continuation lines. */
int occurs_left; /* Number of occurrences remaining. */
dfm_push (t->handle);
/* Read the current record. */
- dfm_bkwd_record (t->handle, 1);
- line = dfm_get_record (t->handle, &len);
- if (line == NULL)
+ dfm_reread_record (t->handle, 1);
+ dfm_expand_tabs (t->handle);
+ if (dfm_eof (t->handle))
return -2;
- dfm_fwd_record (t->handle);
+ dfm_get_record (t->handle, &line);
+ dfm_forward_record (t->handle);
/* Calculate occurs, length. */
occurs_left = occurs = realize_value (&t->occurs, c);
{
struct rpd_parse_info info;
info.trns = t;
- info.line = line;
- info.len = len;
+ info.line = ls_c_str (&line);
+ info.len = ls_length (&line);
info.beg = starts_beg;
info.end = starts_end;
info.ofs = length;
assert (occurs_left >= 0);
/* Read in another record. */
- line = dfm_get_record (t->handle, &len);
- if (line == NULL)
+ if (dfm_eof (t->handle))
{
tmsg (SE, RPD_ERR,
_("Unexpected end of file with %d repetitions "
occurs_left, occurs);
return -2;
}
- dfm_fwd_record (t->handle);
+ dfm_expand_tabs (t->handle);
+ dfm_get_record (t->handle, &line);
+ dfm_forward_record (t->handle);
/* Parse this record. */
info.trns = t;
- info.line = line;
- info.len = len;
+ info.line = ls_c_str (&line);
+ info.len = ls_length (&line);
info.beg = cont_beg;
info.end = cont_end;
info.ofs = length;
break;
case 1:
free (x->file.filename);
- x->file.filename = xstrdup (ds_value (val));
+ x->file.filename = xstrdup (ds_c_str (val));
break;
default:
assert (0);
if (t->nr == 1 && t->nc == 1)
{
fputs ("p:", x->file.file);
- escape_string (x->file.file, ls_value (t->cc), ls_length (t->cc));
+ escape_string (x->file.file, ls_c_str (t->cc), ls_length (t->cc));
putc ('\n', x->file.file);
return;
if (!ls_empty_p (&t->title))
{
putc ('T', x->file.file);
- escape_string (x->file.file, ls_value (&t->title),
+ escape_string (x->file.file, ls_c_str (&t->title),
ls_length (&t->title));
putc ('\n', x->file.file);
}
cc = t->cc + c + r * t->nc;
if (*ct & TAB_JOIN)
{
- j = (struct tab_joined_cell *) ls_value (cc);
+ j = (struct tab_joined_cell *) ls_c_str (cc);
cc = &j->contents;
if (c != j->x1 || r != j->y1)
continue;
else
putc ('c', x->file.file);
putc ('t', x->file.file);
- escape_string (x->file.file, ls_value (cc), ls_length (cc));
+ escape_string (x->file.file, ls_c_str (cc), ls_length (cc));
putc ('\n', x->file.file);
}
}
#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. */
struct file_locator where; /* Current location in data file. */
- char *line; /* Current line, not null-terminated. */
- size_t size; /* Number of bytes allocated for line. */
- size_t len; /* Length of line. */
-
- char *ptr; /* Pointer into line that is returned by
- dfm_get_record(). */
- int advance; /* Nonzero=dfm_get_record() reads a new
- record; otherwise returns current record. */
- int saw_begin_data; /* For inline_file only, whether we've
- already read a BEGIN DATA line. */
+ 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."),
handle_get_filename (h), handle_get_name (h));
- assert (h->class == &dfm_r_class || h->class == &dfm_w_class);
+ 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);
}
int
dfm_open_for_reading (struct file_handle *h)
{
- struct dfm_fhuser_ext *ext;
+ struct dfm_reader_ext *ext;
if (h->class != NULL)
{
ext->where.filename = handle_get_filename (h);
ext->where.line_number = 0;
ext->file.file = NULL;
- ext->line = xmalloc (128);
- ext->len = 0;
- ext->ptr = NULL;
- ext->size = 128;
- ext->advance = 1;
- ext->saw_begin_data = 0;
+ 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."),
handle_get_filename (h), handle_get_name (h));
return 0;
}
-/* Opens a file handle for writing as a data file. */
-int
-dfm_open_for_writing (struct file_handle *h)
-{
- struct dfm_fhuser_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;
- }
- }
-
- ext = xmalloc (sizeof *ext);
- ext->where.filename = handle_get_filename (h);
- ext->where.line_number = 0;
- ext->file.file = NULL;
- ext->line = NULL;
- ext->len = 0;
- ext->ptr = NULL;
- ext->size = 0;
- ext->advance = 0;
-
- 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;
-}
-
-/* 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);
- }
-}
-
/* Reads a record from H->EXT->FILE into H->EXT->LINE, setting
H->EXT->PTR to H->EXT->LINE, and setting H->EXT-LEN to the length
of the line. The line is not null-terminated. If an error occurs
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->saw_begin_data)
+ if ((ext->flags & DFM_SAW_BEGIN_DATA) == 0)
{
char *s;
- ext->saw_begin_data = 1;
+ ext->flags |= DFM_SAW_BEGIN_DATA;
/* FIXME: WTF can't this just be done with tokens?
Is this really a special case? */
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);
+ /* 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++)
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 ();
}
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 (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."),
}
goto eof;
}
- ext->len = (size_t) read_len;
}
else if (handle_get_mode (h) == MODE_BINARY)
{
size_t record_width = handle_get_record_width (h);
size_t amt;
- if (ext->size < record_width)
- {
- ext->size = record_width;
- ext->line = xmalloc (ext->size);
- }
- amt = fread (ext->line, 1, record_width, ext->file.file);
+ 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))
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 (handle_get_mode (h) == MODE_TEXT)
- {
- /* while (ext->len && isspace ((unsigned char) ext->line[ext->len - 1]))
- ext->len--;*/
-
- /* Convert tabs to spaces. */
-
- 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;
+ 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)
-{
- struct dfm_fhuser_ext *ext;
-
- assert (h != NULL);
- assert (h->class == &dfm_r_class);
- assert (h->ext != NULL);
- ext = h->ext;
- if (ext->advance)
+/* 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)
+{
+ struct dfm_reader_ext *ext = get_reader (h);
+ if (ext->flags & DFM_ADVANCE)
{
- if (ext->line)
+ 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));
- goto lossage;
+ err_cond_fail ();
}
}
- ext->advance = 0;
- if (len)
- *len = ext->len - (ext->ptr - ext->line);
- return ext->ptr;
+ return (ext->flags & DFM_EOF) != 0;
+}
-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;
+}
+
+/* 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);
+ }
+ }
- return NULL;
+ /* 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)
{
- struct dfm_fhuser_ext *ext;
- char *ptr;
- size_t amt;
+ struct dfm_writer_ext *ext;
assert (h != NULL);
assert (h->class == &dfm_w_class);
ext = h->ext;
if (handle_get_mode (h) == MODE_BINARY && len < handle_get_record_width (h))
{
- amt = handle_get_record_width (h);
- ptr = local_alloc (amt);
- memcpy (ptr, rec, len);
- memset (&ptr[len], 0, amt - len);
- }
- else
- {
- 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, ext->file.file))
+ if (fwrite (rec, len, 1, ext->file.file) != 1)
{
msg (ME, _("Error writing file %s: %s."),
handle_get_name (h), strerror (errno));
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)
{
- struct dfm_fhuser_ext *ext = h->ext;
+ struct dfm_writer_ext *ext;
- assert (h->class == &dfm_r_class || h->class == &dfm_w_class);
- assert (ext != NULL);
- if (h != inline_file)
- err_push_file_locator (&ext->where);
+ 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 =
{
- struct dfm_fhuser_ext *ext = h->ext;
-
- assert (h->class == &dfm_r_class || h->class == &dfm_w_class);
- assert (ext != NULL);
- if (h != inline_file)
- err_pop_file_locator (&ext->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. */
|| case_source_is_class (vfm_source, &sort_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;
}
msg (VM (1), _("inline file: Opening for reading."));
dfm_open_for_reading (inline_file);
ext = inline_file->ext;
- ext->saw_begin_data = 1;
+ ext->flags |= DFM_SAW_BEGIN_DATA;
/* We don't actually read from the inline file. The input procedure
is what reads from it. */
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,
-};
#include <stddef.h>
-/* I/O utilities. */
struct file_handle;
-int dfm_open_for_reading (struct file_handle *handle);
-int dfm_open_for_writing (struct file_handle *handle);
-char *dfm_get_record (struct file_handle *handle, int *len);
-int dfm_put_record (struct file_handle *handle, const char *rec, size_t len);
-
-/* Motion control. */
-void dfm_fwd_record (struct file_handle *handle);
-void dfm_bkwd_record (struct file_handle *handle, int column);
-
-/* Weirdness. */
-void dfm_set_record (struct file_handle *handle, char *new_line);
-int dfm_get_cur_col (struct file_handle *handle);
-void dfm_push (struct file_handle *handle);
-void dfm_pop (struct file_handle *handle);
+struct len_string;
+
+/* Input. */
+int dfm_open_for_reading (struct file_handle *);
+int dfm_eof (struct file_handle *);
+void dfm_get_record (struct file_handle *, struct len_string *);
+void dfm_expand_tabs (struct file_handle *);
+
+void dfm_forward_record (struct file_handle *);
+void dfm_reread_record (struct file_handle *, size_t column);
+void dfm_forward_columns (struct file_handle *, size_t columns);
+size_t dfm_column_start (struct file_handle *);
+
+/* Output. */
+int dfm_open_for_writing (struct file_handle *);
+int dfm_put_record (struct file_handle *, const char *rec, size_t len);
+
+/* File stack. */
+void dfm_push (struct file_handle *);
+void dfm_pop (struct file_handle *);
#endif /* dfm_h */
{
struct string buf;
- ds_init (NULL, &buf, 1024);
+ ds_init (&buf, 1024);
/* Format the message into BUF. */
{
assert (class >= 0 && class < ERR_CLASS_COUNT);
assert (e->text != NULL);
- ds_init (NULL, &msg, 64);
+ ds_init (&msg, 64);
if (e->where.filename && (error_classes[class].flags & ERR_WITH_FILE))
{
ds_printf (&msg, "%s:", e->where.filename);
if (e->where.line_number != -1)
ds_printf (&msg, "%d:", e->where.line_number);
- ds_putchar (&msg, ' ');
+ ds_putc (&msg, ' ');
}
ds_printf (&msg, "%s: ", gettext (error_classes[class].banner));
ds_printf (&msg, "%s: ", cur_proc);
if (e->title)
- ds_concat (&msg, e->title);
+ ds_puts (&msg, e->title);
- ds_concat (&msg, e->text);
+ ds_puts (&msg, e->text);
/* FIXME: Check set_messages and set_errors to determine where to
send errors and messages.
Please note that this is not trivial. We have to avoid an
infinite loop in reporting errors that originate in the output
section. */
- dump_message (ds_value (&msg), 8, puts_stdout, get_viewwidth());
+ dump_message (ds_c_str (&msg), 8, puts_stdout, get_viewwidth());
ds_destroy (&msg);
case T_STRING:
{
- *n = allocate_str_con (ds_value (&tokstr), ds_length (&tokstr));
+ *n = allocate_str_con (ds_c_str (&tokstr), ds_length (&tokstr));
lex_get ();
return EXPR_STRING;
}
}
ds_truncate (&tokstr, 31);
- strcpy (fname, ds_value (&tokstr));
+ strcpy (fname, ds_c_str (&tokstr));
cp = strrchr (fname, '.');
if (cp && isdigit ((unsigned char) cp[1]))
{
const char *handle_get_filename (const struct file_handle *handle);
enum file_handle_mode handle_get_mode (const struct file_handle *);
size_t handle_get_record_width (const struct file_handle *);
+size_t handle_get_tab_width (const struct file_handle *);
#endif /* !file_handle.h */
struct file_locator where; /* Used for reporting error messages. */
enum file_handle_mode mode; /* File mode. */
size_t length; /* Length of fixed-format records. */
+ size_t tab_width; /* Tab width, 0=do not expand tabs. */
};
/* Linked list of file handles. */
/* (specification)
"FILE HANDLE" (fh_):
name=string;
- recform=recform:fixed/!variable/spanned;
lrecl=integer;
- mode=mode:!character/image/binary/multipunch/_360.
+ tabwidth=integer "x>=0" "%s must be nonnegative";
+ mode=mode:!character/image.
*/
/* (declarations) */
/* (functions) */
{
case FH_CHARACTER:
handle->private->mode = MODE_TEXT;
+ if (cmd.sbc_tabwidth)
+ handle->private->tab_width = cmd.n_tabwidth;
+ else
+ handle->private->tab_width = 4;
break;
case FH_IMAGE:
handle->private->mode = MODE_BINARY;
handle->private->where.line_number = 0;
handle->private->mode = MODE_TEXT;
handle->private->length = 1024;
+ handle->private->tab_width = 4;
handle->ext = NULL;
handle->class = NULL;
if (token == T_ID)
handle = get_handle_with_name (tokid);
if (handle == NULL)
- handle = get_handle_for_filename (ds_value (&tokstr));
+ handle = get_handle_for_filename (ds_c_str (&tokstr));
if (handle == NULL)
{
- char *filename = ds_value (&tokstr);
+ char *filename = ds_c_str (&tokstr);
char *handle_name = xmalloc (strlen (filename) + 3);
sprintf (handle_name, "\"%s\"", filename);
handle = create_file_handle (handle_name, filename);
return handle->private->mode;
}
-/* Returns the width of a logical record on HANDLE. */
+/* Returns the width of a logical record on HANDLE. Applicable
+ only to MODE_BINARY files. */
size_t
handle_get_record_width (const struct file_handle *handle)
{
return handle->private->length;
}
+/* Returns the number of characters per tab stop for HANDLE, or
+ zero if tabs are not to be expanded. Applicable only to
+ MODE_TEXT files. */
+size_t
+handle_get_tab_width (const struct file_handle *handle)
+{
+ assert (handle != NULL);
+ return handle->private->tab_width;
+}
+
/*
Local variables:
mode: c
if (!lex_force_string ())
goto error;
rct->v[rct->nv].c = xmalloc (fty->record.nc + 1);
- st_bare_pad_copy (rct->v[rct->nv].c, ds_value (&tokstr),
+ st_bare_pad_copy (rct->v[rct->nv].c, ds_c_str (&tokstr),
fty->record.nc + 1);
}
else
write_case_data wc_data UNUSED)
{
struct file_type_pgm *fty = source->aux;
- char *line;
- int len;
-
struct fmt_spec format;
dfm_push (fty->handle);
format.type = fty->record.fmt;
format.w = fty->record.nc;
format.d = 0;
- while (NULL != (line = dfm_get_record (fty->handle, &len)))
+ while (!dfm_eof (fty->handle))
{
+ struct len_string line;
struct record_type *iter;
union value v;
int i;
+ dfm_expand_tabs (fty->handle);
+ dfm_get_record (fty->handle, &line);
if (formats[fty->record.fmt].cat & FCAT_STRING)
{
struct data_in di;
v.c = c->data[fty->record.v->fv].s;
- data_in_finite_line (&di, line, len,
+ data_in_finite_line (&di, ls_c_str (&line), ls_length (&line),
fty->record.fc, fty->record.fc + fty->record.nc);
di.v = (union value *) v.c;
di.flags = 0;
{
struct data_in di;
- data_in_finite_line (&di, line, len,
+ data_in_finite_line (&di, ls_c_str (&line), ls_length (&line),
fty->record.fc, fty->record.fc + fty->record.nc);
di.v = &v;
di.flags = 0;
if (fty->wild)
msg (SW, _("Unknown record type %g."), v.f);
}
- dfm_fwd_record (fty->handle);
+ dfm_forward_record (fty->handle);
continue;
found:
/* Arrive here if there is a matching record_type, which is in
iter. */
- dfm_fwd_record (fty->handle);
+ dfm_forward_record (fty->handle);
}
/* switch(fty->type)
if (NULL == strchr (input, '$'))
return xstrdup (input);
- ds_init (NULL, &output, strlen (input));
+ ds_init (&output, strlen (input));
for (;;)
switch (*input)
{
case '\0':
- return ds_value (&output);
+ return ds_c_str (&output);
case '$':
input++;
if (*input == '$')
{
- ds_putchar (&output, '$');
+ ds_putc (&output, '$');
input++;
}
else
while (*input && *input != stop
&& (stop || isalpha ((unsigned char) *input)))
- ds_putchar (&output, *input++);
+ ds_putc (&output, *input++);
- value = getenv (ds_value (&output) + start);
+ value = getenv (ds_c_str (&output) + start);
ds_truncate (&output, start);
- ds_concat (&output, value);
+ ds_puts (&output, value);
if (stop && *input == stop)
input++;
}
default:
- ds_putchar (&output, *input++);
+ ds_putc (&output, *input++);
}
}
if (NULL == strchr (input, '~'))
return xstrdup (input);
- ds_init (NULL, &output, strlen (input));
+ ds_init (&output, strlen (input));
ip = input;
for (ip = input; *ip; )
if (*ip != '~' || (ip != input && ip[-1] != PATH_DELIMITER))
- ds_putchar (&output, *ip++);
+ ds_putc (&output, *ip++);
else
{
static const char stop_set[3] = {DIR_SEPARATOR, PATH_DELIMITER, 0};
pwd = getpwnam (username);
if (!pwd || !pwd->pw_dir)
- ds_putchar (&output, *ip++);
+ ds_putc (&output, *ip++);
else
- ds_concat (&output, pwd->pw_dir);
+ ds_puts (&output, pwd->pw_dir);
}
else
{
const char *home = fn_getenv ("HOME");
if (!home)
- ds_putchar (&output, *ip++);
+ ds_putc (&output, *ip++);
else
- ds_concat (&output, home);
+ ds_puts (&output, home);
}
ip = cp;
}
- return ds_value (&output);
+ return ds_c_str (&output);
}
#else /* !unix */
char *
}
msg (VM (4), _("Searching for `%s'..."), basename);
- ds_init (NULL, &filename, 64);
+ ds_init (&filename, 64);
for (;;)
{
ds_clear (&filename);
if (prepend && !fn_absolute_p (bp))
{
- ds_concat (&filename, prepend);
- ds_putchar (&filename, DIR_SEPARATOR);
+ ds_puts (&filename, prepend);
+ ds_putc (&filename, DIR_SEPARATOR);
}
- ds_concat_buffer (&filename, bp, ep - bp);
+ ds_concat (&filename, bp, ep - bp);
if (ep - bp
- && ds_value (&filename)[ds_length (&filename) - 1] != DIR_SEPARATOR)
- ds_putchar (&filename, DIR_SEPARATOR);
- ds_concat (&filename, basename);
+ && ds_c_str (&filename)[ds_length (&filename) - 1] != DIR_SEPARATOR)
+ ds_putc (&filename, DIR_SEPARATOR);
+ ds_puts (&filename, basename);
- msg (VM (5), " - %s", ds_value (&filename));
- if (fn_exists_p (ds_value (&filename)))
+ msg (VM (5), " - %s", ds_c_str (&filename));
+ if (fn_exists_p (ds_c_str (&filename)))
{
- msg (VM (4), _("Found `%s'."), ds_value (&filename));
+ msg (VM (4), _("Found `%s'."), ds_c_str (&filename));
free (subst_path);
- return ds_value (&filename);
+ return ds_c_str (&filename);
}
if (0 == *ep)
char *sp, *ep;
int idx;
- sp = ep = ds_value (&tokstr);
+ sp = ep = ds_c_str (&tokstr);
while (isalpha ((unsigned char) *ep))
ep++;
{
/* No match. */
msg (SE, _("%.*s is not a valid data format."),
- (int) (ep - sp), ds_value (&tokstr));
+ (int) (ep - sp), ds_c_str (&tokstr));
idx = -1;
}
}
if (cp2 == cp && type != FMT_X)
{
msg (SE, _("Data format %s does not specify a width."),
- ds_value (&tokstr));
+ ds_c_str (&tokstr));
return 0;
}
if (*cp)
{
- msg (SE, _("Data format %s is not valid."), ds_value (&tokstr));
+ msg (SE, _("Data format %s is not valid."), ds_c_str (&tokstr));
return 0;
}
lex_get ();
void
getl_initialize (void)
{
- ds_create (NULL, &getl_include_path,
+ ds_create (&getl_include_path,
fn_getenv_default ("STAT_INCLUDE_PATH", include_path));
- ds_init (NULL, &getl_buf, 256);
+ ds_init (&getl_buf, 256);
}
/* Close getline. */
getl_add_include_dir (const char *path)
{
if (ds_length (&getl_include_path))
- ds_putchar (&getl_include_path, PATH_DELIMITER);
+ ds_putc (&getl_include_path, PATH_DELIMITER);
- ds_concat (&getl_include_path, path);
+ ds_puts (&getl_include_path, path);
}
/* Adds FN to the tail end of the list of script files to execute.
{
char *cur_dir = getl_get_current_directory ();
- real_fn = fn_search_path (fn, ds_value (&getl_include_path), cur_dir);
+ real_fn = fn_search_path (fn, ds_c_str (&getl_include_path), cur_dir);
free (cur_dir);
}
}
while (s->cur_line == NULL);
- ds_concat_buffer (&getl_buf, s->cur_line->line, s->cur_line->len);
+ ds_concat (&getl_buf, s->cur_line->line, s->cur_line->len);
/* Advance pointers. */
s->cur_line = s->cur_line->next;
perform_DO_REPEAT_substitutions ();
if (getl_head->print)
tab_output_text (TAB_LEFT | TAT_FIX | TAT_PRINTF, "+%s",
- ds_value (&getl_buf));
+ ds_c_str (&getl_buf));
return 1;
}
}
}
- if (!ds_getline (&getl_buf, s->f))
+ if (!ds_gets (&getl_buf, s->f))
{
if (ferror (s->f))
msg (ME, _("Reading `%s': %s."), s->fn, strerror (errno));
ds_truncate (&getl_buf, ds_length (&getl_buf) - 1);
if (get_echo())
- tab_output_text (TAB_LEFT | TAT_FIX, ds_value (&getl_buf));
+ tab_output_text (TAB_LEFT | TAT_FIX, ds_c_str (&getl_buf));
getl_head->ln++;
/* Allows shebang invocation: `#! /usr/local/bin/pspp'. */
- if (ds_value (&getl_buf)[0] == '#'
- && ds_value (&getl_buf)[1] == '!')
+ if (ds_c_str (&getl_buf)[0] == '#'
+ && ds_c_str (&getl_buf)[1] == '!')
continue;
return 1;
#endif
ds_clear (&getl_buf);
- ds_concat (&getl_buf, line);
+ ds_puts (&getl_buf, line);
return 1;
}
fputs (getl_prompt ? get_cprompt() : get_prompt(), stdout);
ds_clear (&getl_buf);
- if (ds_getline (&getl_buf, stdin))
+ if (ds_gets (&getl_buf, stdin))
return 1;
if (ferror (stdin))
last_vfm_invocation = time (NULL);
/* lexer.h */
- ds_init (NULL, &tokstr, 64);
+ ds_init (&tokstr, 64);
/* common.h */
{
break;
case 1:
free (x->file.filename);
- x->file.filename = xstrdup (ds_value (val));
+ x->file.filename = xstrdup (ds_c_str (val));
break;
case string_arg:
{
}
if (*dest)
free (*dest);
- *dest = xstrdup (ds_value (val));
+ *dest = xstrdup (ds_c_str (val));
}
break;
default:
{
fputs ("<P>", x->file.file);
if (!ls_empty_p (t->cc))
- escape_string (x->file.file, ls_value (t->cc), ls_length (t->cc));
+ escape_string (x->file.file, ls_c_str (t->cc), ls_length (t->cc));
fputs ("</P>\n", x->file.file);
return;
if (!ls_empty_p (&t->title))
{
fprintf (x->file.file, " <TR>\n <TH COLSPAN=%d>", t->nc);
- escape_string (x->file.file, ls_value (&t->title),
+ escape_string (x->file.file, ls_c_str (&t->title),
ls_length (&t->title));
fputs ("</TH>\n </TR>\n", x->file.file);
}
cc = t->cc + c + r * t->nc;
if (*ct & TAB_JOIN)
{
- j = (struct tab_joined_cell *) ls_value (cc);
+ j = (struct tab_joined_cell *) ls_c_str (cc);
cc = &j->contents;
if (j->x1 != c || j->y1 != r)
continue;
if ( ! (*ct & TAB_EMPTY) )
{
- char *s = ls_value (cc);
+ char *s = ls_c_str (cc);
size_t l = ls_length (cc);
while (l && isspace ((unsigned char) *s))
lex_error (_("expecting filename"));
return CMD_FAILURE;
}
- getl_include (ds_value (&tokstr));
+ getl_include (ds_c_str (&tokstr));
lex_get ();
return lex_end_of_command ();
struct reread_trns *t = (struct reread_trns *) pt;
if (t->column == NULL)
- dfm_bkwd_record (t->handle, 1);
+ dfm_reread_record (t->handle, 1);
else
{
union value column;
{
msg (SE, _("REREAD: Column numbers must be positive finite "
"numbers. Column set to 1."));
- dfm_bkwd_record (t->handle, 1);
+ dfm_reread_record (t->handle, 1);
}
else
- dfm_bkwd_record (t->handle, column.f);
+ dfm_reread_record (t->handle, column.f);
}
return -1;
}
void
lex_init (void)
{
- ds_init (NULL, &put_tokstr, 64);
+ ds_init (&put_tokstr, 64);
if (!lex_get_line ())
unexpected_eof ();
}
{
assert (put_token != 0);
token = put_token;
- ds_replace (&tokstr, ds_value (&put_tokstr));
- strncpy (tokid, ds_value (&put_tokstr), 8);
+ ds_replace (&tokstr, ds_c_str (&put_tokstr));
+ strncpy (tokid, ds_c_str (&put_tokstr), 8);
tokid[8] = 0;
tokval = put_tokval;
put_token = 0;
save_token (void)
{
put_token = token;
- ds_replace (&put_tokstr, ds_value (&tokstr));
+ ds_replace (&put_tokstr, ds_c_str (&tokstr));
put_tokval = tokval;
}
negative numbers into two tokens. */
if (*cp == '-')
{
- ds_putchar (&tokstr, *prog++);
+ ds_putc (&tokstr, *prog++);
while (isspace ((unsigned char) *prog))
prog++;
/* Parse the number, copying it into tokstr. */
while (isdigit ((unsigned char) *prog))
- ds_putchar (&tokstr, *prog++);
+ ds_putc (&tokstr, *prog++);
if (*prog == '.')
{
- ds_putchar (&tokstr, *prog++);
+ ds_putc (&tokstr, *prog++);
while (isdigit ((unsigned char) *prog))
- ds_putchar (&tokstr, *prog++);
+ ds_putc (&tokstr, *prog++);
}
if (*prog == 'e' || *prog == 'E')
{
- ds_putchar (&tokstr, *prog++);
+ ds_putc (&tokstr, *prog++);
if (*prog == '+' || *prog == '-')
- ds_putchar (&tokstr, *prog++);
+ ds_putc (&tokstr, *prog++);
while (isdigit ((unsigned char) *prog))
- ds_putchar (&tokstr, *prog++);
+ ds_putc (&tokstr, *prog++);
}
/* Parse as floating point. */
- tokval = strtod (ds_value (&tokstr), &tail);
+ tokval = strtod (ds_c_str (&tokstr), &tail);
if (*tail)
{
msg (SE, _("%s does not form a valid number."),
- ds_value (&tokstr));
+ ds_c_str (&tokstr));
tokval = 0.0;
ds_clear (&tokstr);
- ds_putchar (&tokstr, '0');
+ ds_putc (&tokstr, '0');
}
token = T_NUM;
}
/* Copy id to tokstr. */
- ds_putchar (&tokstr, toupper ((unsigned char) *prog++));
+ ds_putc (&tokstr, toupper ((unsigned char) *prog++));
while (CHAR_IS_IDN (*prog))
- ds_putchar (&tokstr, toupper ((unsigned char) *prog++));
+ ds_putc (&tokstr, toupper ((unsigned char) *prog++));
/* Copy tokstr to tokid, truncating it to 8 characters. */
- strncpy (tokid, ds_value (&tokstr), 8);
+ strncpy (tokid, ds_c_str (&tokstr), 8);
tokid[8] = 0;
- token = check_id (ds_value (&tokstr), ds_length (&tokstr));
+ token = check_id (ds_c_str (&tokstr), ds_length (&tokstr));
break;
default:
save_token ();
token = T_ID;
ds_replace (&tokstr, id);
- strncpy (tokid, ds_value (&tokstr), 8);
+ strncpy (tokid, ds_c_str (&tokstr), 8);
tokid[8] = 0;
}
\f
const char *
lex_entire_line (void)
{
- return ds_value (&getl_buf);
+ return ds_c_str (&getl_buf);
}
/* As lex_entire_line(), but only returns the part of the current line
/* Remove C-style comments begun by slash-star and terminated by
star-slash or newline. */
quote = comment = 0;
- for (cp = ds_value (&getl_buf); *cp; )
+ for (cp = ds_c_str (&getl_buf); *cp; )
{
/* If we're not commented out, toggle quoting. */
if (!comment)
/* Strip trailing whitespace and terminal dot. */
{
size_t len = ds_length (&getl_buf);
- char *s = ds_value (&getl_buf);
+ char *s = ds_c_str (&getl_buf);
/* Strip trailing whitespace. */
while (len > 0 && isspace ((unsigned char) s[len - 1]))
as necessary. */
if (getl_interactive != 2 && getl_mode == GETL_MODE_BATCH)
{
- char *s = ds_value (&getl_buf);
+ char *s = ds_c_str (&getl_buf);
if (s[0] == '+' || s[0] == '-' || s[0] == '.')
s[0] = ' ';
put_token = '.';
}
- prog = ds_value (&getl_buf);
+ prog = ds_c_str (&getl_buf);
}
\f
/* Token names. */
{
case T_ID:
case T_NUM:
- return xstrdup (ds_value (&tokstr));
+ return xstrdup (ds_c_str (&tokstr));
break;
case T_STRING:
int hexstring = 0;
char *sp, *dp;
- for (sp = ds_value (&tokstr); sp < ds_end (&tokstr); sp++)
+ for (sp = ds_c_str (&tokstr); sp < ds_end (&tokstr); sp++)
if (!isprint ((unsigned char) *sp))
{
hexstring = 1;
*dp++ = '\'';
if (!hexstring)
- for (sp = ds_value (&tokstr); *sp; )
+ for (sp = ds_c_str (&tokstr); *sp; )
{
if (*sp == '\'')
*dp++ = '\'';
*dp++ = (unsigned char) *sp++;
}
else
- for (sp = ds_value (&tokstr); sp < ds_end (&tokstr); sp++)
+ for (sp = ds_c_str (&tokstr); sp < ds_end (&tokstr); sp++)
{
*dp++ = (((unsigned char) *sp) >> 4)["0123456789ABCDEF"];
*dp++ = (((unsigned char) *sp) & 15)["0123456789ABCDEF"];
{
token = T_NUM;
tokval = -tokval;
- ds_replace (&tokstr, ds_value (&tokstr) + 1);
+ ds_replace (&tokstr, ds_c_str (&tokstr) + 1);
save_token ();
token = '-';
}
"multiple of %d."),
gettext (base_name), ds_length (&tokstr), cpb);
- p = ds_value (&tokstr);
+ p = ds_c_str (&tokstr);
for (i = 0; i < nb; i++)
{
int value;
value = value * base + v;
}
- ds_value (&tokstr)[i] = (unsigned char) value;
+ ds_c_str (&tokstr)[i] = (unsigned char) value;
}
ds_truncate (&tokstr, nb);
break;
}
- ds_putchar (&tokstr, *prog++);
+ ds_putc (&tokstr, *prog++);
}
prog++;
int warned = 0;
for (i = 0; i < ds_length (&tokstr); i++)
- if (ds_value (&tokstr)[i] == 0)
+ if (ds_c_str (&tokstr)[i] == 0)
{
if (!warned)
{
"characters. Replacing with spaces."));
warned = 1;
}
- ds_value (&tokstr)[i] = ' ';
+ ds_c_str (&tokstr)[i] = ' ';
}
}
break;
case T_STRING:
- fprintf (stderr, "STRING\t\"%s\"\n", ds_value (&tokstr));
+ fprintf (stderr, "STRING\t\"%s\"\n", ds_c_str (&tokstr));
break;
case T_STOP:
context (struct file_handle *data_file)
{
static char buf[32];
- int len;
- char *p = dfm_get_record (data_file, &len);
-
- if (!p || !len)
- strcpy (buf, "at end of line");
- else
+
+ if (dfm_eof (data_file))
+ strcpy (buf, "at end of file");
+ else
{
- char *cp = buf;
- int n_copy = min (10, len);
- cp = stpcpy (buf, "before `");
- while (n_copy && isspace ((unsigned char) *p))
- p++, n_copy++;
- while (n_copy && !isspace ((unsigned char) *p))
- *cp++ = *p++, n_copy--;
- *cp++ = '\'';
- *cp = 0;
+ struct len_string line;
+ const char *sp;
+
+ dfm_get_record (data_file, &line);
+ sp = ls_c_str (&line);
+ while (sp < ls_end (&line) && isspace ((unsigned char) *sp))
+ sp++;
+ if (sp >= ls_end (&line))
+ strcpy (buf, "at end of line");
+ else
+ {
+ char *dp;
+ size_t copy_cnt = 0;
+
+ dp = stpcpy (buf, "before `");
+ while (sp < ls_end (&line) && !isspace ((unsigned char) *sp)
+ && copy_cnt < 10)
+ {
+ *dp++ = *sp++;
+ copy_cnt++;
+ }
+ strcpy (dp, "'");
+ }
}
return buf;
static int
another_token (struct file_handle *data_file)
{
- char *cp, *ep;
- int len;
-
for (;;)
{
- cp = dfm_get_record (data_file, &len);
- if (!cp)
- return 0;
+ struct len_string line;
+ const char *cp;
+
+ if (dfm_eof (data_file))
+ return 0;
+ dfm_get_record (data_file, &line);
- ep = cp + len;
- while (isspace ((unsigned char) *cp) && cp < ep)
+ cp = ls_c_str (&line);
+ while (isspace ((unsigned char) *cp) && cp < ls_end (&line))
cp++;
- if (cp < ep)
- break;
+ if (cp < ls_end (&line))
+ {
+ dfm_forward_columns (data_file, cp - ls_c_str (&line));
+ return 1;
+ }
- dfm_fwd_record (data_file);
+ dfm_forward_record (data_file);
}
-
- dfm_set_record (data_file, cp);
-
- return 1;
}
/* Parse a MATRIX DATA token from mx->data_file into TOKEN. */
static int
(mget_token) (struct matrix_token *token, struct file_handle *data_file)
{
- char *cp, *ep;
- int len;
+ struct len_string line;
int first_column;
-
- for (;;)
- {
- cp = dfm_get_record (data_file, &len);
- if (!cp)
- return 0;
+ char *cp;
- ep = cp + len;
- while (isspace ((unsigned char) *cp) && cp < ep)
- cp++;
+ if (!another_token (data_file))
+ return 0;
- if (cp < ep)
- break;
-
- dfm_fwd_record (data_file);
- }
-
- dfm_set_record (data_file, cp);
- first_column = dfm_get_cur_col (data_file) + 1;
+ dfm_get_record (data_file, &line);
+ first_column = dfm_column_start (data_file);
/* Three types of fields: quoted with ', quoted with ", unquoted. */
+ cp = ls_c_str (&line);
if (*cp == '\'' || *cp == '"')
{
int quote = *cp;
token->type = MSTR;
token->string = ++cp;
- while (cp < ep && *cp != quote)
+ while (cp < ls_end (&line) && *cp != quote)
cp++;
token->length = cp - token->string;
- if (cp < ep)
+ if (cp < ls_end (&line))
cp++;
else
msg (SW, _("Scope of string exceeds line."));
int is_num = isdigit ((unsigned char) *cp) || *cp == '.';
token->string = cp++;
- while (cp < ep && !isspace ((unsigned char) *cp) && *cp != ','
+ while (cp < ls_end (&line)
+ && !isspace ((unsigned char) *cp) && *cp != ','
&& *cp != '-' && *cp != '+')
{
if (isdigit ((unsigned char) *cp))
token->type = MSTR;
}
- dfm_set_record (data_file, cp);
+ dfm_forward_columns (data_file, cp - ls_c_str (&line));
return 1;
}
static int
force_eol (struct file_handle *data_file, const char *content)
{
- char *cp;
- int len;
-
- cp = dfm_get_record (data_file, &len);
- if (!cp)
+ struct len_string line;
+ const char *cp;
+
+ if (dfm_eof (data_file))
return 0;
- while (len && isspace (*cp))
- cp++, len--;
+ dfm_get_record (data_file, &line);
+
+ cp = ls_c_str (&line);
+ while (isspace ((unsigned char) *cp) && cp < ls_end (&line))
+ cp++;
- if (len)
+ if (cp < ls_end (&line))
{
msg (SE, _("End of line expected %s while reading %s."),
context (data_file), content);
return 0;
}
- dfm_fwd_record (data_file);
-
+ dfm_forward_record (data_file);
return 1;
}
\f
msg (SE, _("String is not of proper length."));
return 0;
}
- strncpy (missing[miss_type].s, ds_value (&tokstr), MAX_SHORT_STRING);
+ strncpy (missing[miss_type].s, ds_c_str (&tokstr), MAX_SHORT_STRING);
lex_get ();
lex_match (',');
}
where.line_number = 0;
err_push_file_locator (&where);
- ds_init (NULL, &line, 128);
+ ds_init (&line, 128);
if (init_fn == NULL)
{
msg (ME, _("Reading %s: %s."), init_fn, strerror (errno));
break;
}
- for (cp = ds_value (&line); isspace ((unsigned char) *cp); cp++);
+ for (cp = ds_c_str (&line); isspace ((unsigned char) *cp); cp++);
if (!strncmp ("define", cp, 6) && isspace ((unsigned char) cp[6]))
outp_configure_macro (&cp[7]);
else if (*cp)
while (*prog && *prog != quote)
{
if (*prog != '\\')
- ds_putchar (&op_tokstr, *prog++);
+ ds_putc (&op_tokstr, *prog++);
else
{
int c;
msg (IS, _("Syntax error in string constant."));
continue;
}
- ds_putchar (&op_tokstr, (unsigned char) c);
+ ds_putc (&op_tokstr, (unsigned char) c);
}
}
prog++;
}
else
while (*prog && !isspace ((unsigned char) *prog) && *prog != '=')
- ds_putchar (&op_tokstr, *prog++);
+ ds_putc (&op_tokstr, *prog++);
op_token = 'a';
}
prog = s;
op_token = -1;
- ds_init (NULL, &op_tokstr, 64);
+ ds_init (&op_tokstr, 64);
while (tokener ())
{
char key[65];
}
ds_truncate (&op_tokstr, 64);
- strcpy (key, ds_value (&op_tokstr));
+ strcpy (key, ds_c_str (&op_tokstr));
tokener ();
if (op_token != '=')
where.filename = pprsz_fn;
where.line_number = 0;
err_push_file_locator (&where);
- ds_init (NULL, &line, 128);
+ ds_init (&line, 128);
if (pprsz_fn == NULL)
{
msg (ME, _("Reading %s: %s."), pprsz_fn, strerror (errno));
break;
}
- for (cp = ds_value (&line); isspace ((unsigned char) *cp); cp++);
+ for (cp = ds_c_str (&line); isspace ((unsigned char) *cp); cp++);
if (*cp == 0)
continue;
if (*cp != '"')
{
struct ps_driver_ext *x = this->ext;
int cat, subcat;
- char *value = ds_value (val);
+ char *value = ds_c_str (val);
cat = outp_match_keyword (key, option_tab, &option_info, &subcat);
struct string line, buf;
- ds_init (NULL, &line, 128);
- ds_init (NULL, &buf, 128);
+ ds_init (&line, 128);
+ ds_init (&buf, 128);
for (pe = hsh_first (x->encodings, &iter); pe != NULL;
pe = hsh_next (x->encodings, &iter))
{
if (buf.length == 0)
continue;
- pschar = strtok_r (ds_value (&buf), " \t\r\n", &sp);
+ pschar = strtok_r (ds_c_str (&buf), " \t\r\n", &sp);
code = strtok_r (NULL, " \t\r\n", &sp);
if (*pschar == 0 || *code == 0)
continue;
if (ds_length (&line) + strlen (temp) > 70)
{
- ds_concat (&line, x->eol);
- fputs (ds_value (&line), x->file.file);
+ ds_puts (&line, x->eol);
+ fputs (ds_c_str (&line), x->file.file);
ds_clear (&line);
}
- ds_concat (&line, temp);
+ ds_puts (&line, temp);
}
- ds_concat (&line, x->eol);
- fputs (ds_value (&line), x->file.file);
+ ds_puts (&line, x->eol);
+ fputs (ds_c_str (&line), x->file.file);
if (fclose (f) == EOF)
msg (MW, _("PostScript driver: Error closing encoding file `%s'."),
where.line_number = 0;
err_push_file_locator (&where);
- ds_init (NULL, &line, 128);
+ ds_init (&line, 128);
for (;;)
{
buf_loc = buf;
assert (!ls_null_p (&t->s));
- cp = ls_value (&t->s);
+ cp = ls_c_str (&t->s);
end = ls_end (&t->s);
if (draw)
{
{
fx.spec.type = PRT_CONST;
fx.spec.fc = fx.sc - 1;
- fx.spec.u.c = xstrdup (ds_value (&tokstr));
+ fx.spec.u.c = xstrdup (ds_c_str (&tokstr));
lex_get ();
/* Parse the included column range. */
outdent ();
}
dump (0, "free(p->s_%s);", st_lower(sbc->name) );
- dump (0, "p->s_%s = xstrdup (ds_value (&tokstr));",
+ dump (0, "p->s_%s = xstrdup (ds_c_str (&tokstr));",
st_lower (sbc->name));
dump (0, "lex_get ();");
if (sbc->restriction)
}
+ free (v);
+ v = NULL;
+
if (!lex_match ('/'))
break;
while (rcd->next)
rcd = rcd->next;
rcd = rcd->next = xmalloc (sizeof *rcd);
-
- free (v);
- v = NULL;
}
if (token != '.')
if (toklen > max)
max = toklen;
v->c = xmalloc (max + 1);
- st_pad_copy (v->c, ds_value (&tokstr), max + 1);
+ st_pad_copy (v->c, ds_c_str (&tokstr), max + 1);
flags = RCD_DEST_STRING;
*max_dst_width = max;
lex_get ();
if (!lex_force_string ())
return 0;
c->f1.c = xmalloc (max_src_width + 1);
- st_pad_copy (c->f1.c, ds_value (&tokstr), max_src_width + 1);
+ st_pad_copy (c->f1.c, ds_c_str (&tokstr), max_src_width + 1);
lex_get ();
}
}
c->data[v->src->fv].s,
v->dest->width, v->src->width);
else
- memcpy (c->data[v->dest->fv].s, cp->t.c, v->dest->width);
+ memmove (c->data[v->dest->fv].s, cp->t.c, v->dest->width);
}
}
command names must appear on a single line--they can't be
spread out. */
{
- char *cp = ds_value (&getl_buf);
+ char *cp = ds_c_str (&getl_buf);
/* Skip leading indentors and any whitespace. */
if (*cp == '+' || *cp == '-' || *cp == '.')
line_buf_tail->len = ds_length (&getl_buf);
line_buf_tail->line = xmalloc (ds_length (&getl_buf) + 1);
memcpy (line_buf_tail->line,
- ds_value (&getl_buf), ds_length (&getl_buf) + 1);
+ ds_c_str (&getl_buf), ds_length (&getl_buf) + 1);
}
}
/* Terminal dot. */
int dot = 0;
- ds_init (NULL, &output, ds_size (&getl_buf));
+ ds_init (&output, ds_capacity (&getl_buf));
/* Strip trailing whitespace, check for & remove terminal dot. */
while (ds_length (&getl_buf) > 0
ds_truncate (&getl_buf, ds_length (&getl_buf) - 1);
}
- for (cp = ds_value (&getl_buf); cp < ds_end (&getl_buf); )
+ for (cp = ds_c_str (&getl_buf); cp < ds_end (&getl_buf); )
{
if (*cp == '\'' && !in_quote)
in_apos ^= 1;
if (in_quote || in_apos || !CHAR_IS_ID1 (*cp))
{
- ds_putchar (&output, *cp++);
+ ds_putc (&output, *cp++);
continue;
}
substitution = find_DO_REPEAT_substitution (name);
if (!substitution)
{
- ds_concat_buffer (&output, start, cp - start);
+ ds_concat (&output, start, cp - start);
continue;
}
/* Force output buffer size, copy substitution. */
- ds_concat (&output, substitution);
+ ds_puts (&output, substitution);
}
}
if (dot)
- ds_putchar (&output, get_endcmd() );
+ ds_putc (&output, get_endcmd() );
ds_destroy (&getl_buf);
getl_buf = output;
listing=custom;
log=custom;
lowres=lores:auto/on/off;
- lpi=integer "x>0" "% must be greater than 0";
+ lpi=integer "x>0" "%s must be greater than 0";
menus=menus:standard/extended;
messages=messages:on/off/terminal/listing/both/none;
mexpand=mexp:on/off;
return 0;
if (set_pager)
free (set_pager);
- set_pager = xstrdup (ds_value (&tokstr));
+ set_pager = xstrdup (ds_c_str (&tokstr));
lex_get ();
}
return 1;
set_journaling = 0;
if (token == T_STRING)
{
- set_journal = xstrdup (ds_value (&tokstr));
+ set_journal = xstrdup (ds_c_str (&tokstr));
lex_get ();
}
return 1;
st_bare_pad_len_copy (char *dest, const char *src, size_t n, size_t len)
{
if (len >= n)
- memcpy (dest, src, n);
+ memmove (dest, src, n);
else
{
- memcpy (dest, src, len);
+ memmove (dest, src, len);
memset (&dest[len], ' ', n - len);
}
}
}
}
\f
-/* Initializes ST inside pool POOL (which may be a null pointer) with
- initial contents S. */
+/* Initializes ST with initial contents S. */
void
-ds_create (struct pool *pool, struct string *st, const char *s)
+ds_create (struct string *st, const char *s)
{
- st->pool = pool;
st->length = strlen (s);
- st->size = 8 + st->length * 2;
- st->string = pool_malloc (pool, st->size + 1);
+ st->capacity = 8 + st->length * 2;
+ st->string = xmalloc (st->capacity + 1);
strcpy (st->string, s);
}
-/* Initializes ST inside POOL (which may be null), making room for at
- least SIZE characters. */
+/* Initializes ST, making room for at least CAPACITY characters. */
void
-ds_init (struct pool *pool, struct string *st, size_t size)
+ds_init (struct string *st, size_t capacity)
{
- st->pool = pool;
st->length = 0;
- if (size > 8)
- st->size = size;
+ if (capacity > 8)
+ st->capacity = capacity;
else
- st->size = 8;
- st->string = pool_malloc (pool, st->size + 1);
+ st->capacity = 8;
+ st->string = xmalloc (st->capacity + 1);
}
/* Replaces the contents of ST with STRING. STRING may overlap with
void
ds_replace (struct string *st, const char *string)
{
- char *s = st->string;
- st->string = NULL;
- ds_create (st->pool, st, string);
- pool_free (st->pool, s);
+ size_t new_length = strlen (string);
+ if (new_length > st->capacity)
+ {
+ /* The new length is longer than the allocated length, so
+ there can be no overlap. */
+ st->length = 0;
+ ds_concat (st, string, new_length);
+ }
+ else
+ {
+ /* Overlap is possible, but the new string will fit in the
+ allocated space, so we can just copy data. */
+ st->length = new_length;
+ memmove (st->string, string, st->length);
+ }
}
/* Frees ST. */
void
ds_destroy (struct string *st)
{
- pool_free (st->pool, st->string);
+ free (st->string);
}
/* Truncates ST to zero length. */
st->length = 0;
}
-/* Ensures that ST can hold at least MIN_SIZE characters plus a null
+/* Pad ST on the right with copies of PAD until ST is at least
+ LENGTH characters in size. If ST is initially LENGTH
+ characters or longer, this is a no-op. */
+void
+ds_rpad (struct string *st, size_t length, char pad)
+{
+ assert (st != NULL);
+ if (st->length < length)
+ {
+ if (st->capacity < length)
+ ds_extend (st, length);
+ memset (&st->string[st->length], pad, length - st->length);
+ st->length = length;
+ }
+}
+
+/* Ensures that ST can hold at least MIN_CAPACITY characters plus a null
terminator. */
void
-ds_extend (struct string *st, size_t min_size)
+ds_extend (struct string *st, size_t min_capacity)
{
- if (min_size > st->size)
+ if (min_capacity > st->capacity)
{
- st->size *= 2;
- if (st->size < min_size)
- st->size = min_size * 2;
+ st->capacity *= 2;
+ if (st->capacity < min_capacity)
+ st->capacity = min_capacity * 2;
- st->string = pool_realloc (st->pool, st->string, st->size + 1);
+ st->string = xrealloc (st->string, st->capacity + 1);
}
}
-/* Shrink ST to the minimum size need to contain its content. */
+/* Shrink ST to the minimum capacity need to contain its content. */
void
ds_shrink (struct string *st)
{
- if (st->size != st->length)
+ if (st->capacity != st->length)
{
- st->size = st->length;
- st->string = pool_realloc (st->pool, st->string, st->size + 1);
+ st->capacity = st->length;
+ st->string = xrealloc (st->string, st->capacity + 1);
}
}
/* Returns the allocation size of ST. */
size_t
-ds_size (const struct string *st)
+ds_capacity (const struct string *st)
{
- return st->size;
+ return st->capacity;
}
/* Returns the value of ST as a null-terminated string. */
char *
-ds_value (const struct string *st)
+ds_c_str (const struct string *st)
{
((char *) st->string)[st->length] = '\0';
return st->string;
}
/* Returns a pointer to the null terminator ST.
- This might not be an actual null character unless ds_value() has
+ This might not be an actual null character unless ds_c_str() has
been called since the last modification to ST. */
char *
ds_end (const struct string *st)
/* Concatenates S onto ST. */
void
-ds_concat (struct string *st, const char *s)
+ds_puts (struct string *st, const char *s)
{
size_t s_len;
/* Concatenates LEN characters from BUF onto ST. */
void
-ds_concat_buffer (struct string *st, const char *buf, size_t len)
+ds_concat (struct string *st, const char *buf, size_t len)
{
ds_extend (st, st->length + len);
memcpy (st->string + st->length, buf, len);
int avail, needed;
- avail = st->size - st->length + 1;
+ avail = st->capacity - st->length + 1;
needed = vsnprintf (st->string + st->length, avail, format, args);
else
while (needed == -1)
{
- ds_extend (st, (st->size + 1) * 2);
- avail = st->size - st->length + 1;
+ ds_extend (st, (st->capacity + 1) * 2);
+ avail = st->capacity - st->length + 1;
needed = vsnprintf (st->string + st->length, avail, format, args);
/* Appends character CH to ST. */
void
-ds_putchar (struct string *st, int ch)
+ds_putc (struct string *st, int ch)
{
- if (st->length == st->size)
+ if (st->length == st->capacity)
ds_extend (st, st->length + 1);
st->string[st->length++] = ch;
}
-/* Reads a newline-terminated line from STREAM into ST.
+/* Appends to ST a newline-terminated line read from STREAM.
Newline is the last character of ST on return, unless an I/O error
or end of file is encountered after reading some characters.
Returns 1 if a line is successfully read, or 0 if no characters at
all were read before an I/O error or end of file was
encountered. */
int
-ds_getline (struct string *st, FILE *stream)
+ds_gets (struct string *st, FILE *stream)
{
int c;
for (;;)
{
- ds_putchar (st, c);
+ ds_putc (st, c);
if (c == '\n')
return 1;
/* Read the first line. */
ds_clear (st);
where->line_number++;
- if (!ds_getline (st, stream))
+ if (!ds_gets (st, stream))
return 0;
/* Read additional lines, if any. */
{
/* Remove trailing whitespace. */
{
- char *s = ds_value (st);
+ char *s = ds_c_str (st);
size_t len = ds_length (st);
while (len > 0 && isspace ((unsigned char) s[len - 1]))
}
/* Check for trailing \. Remove if found, bail otherwise. */
- if (ds_length (st) == 0 || ds_value (st)[ds_length (st) - 1] != '\\')
+ if (ds_length (st) == 0 || ds_c_str (st)[ds_length (st) - 1] != '\\')
break;
ds_truncate (st, ds_length (st) - 1);
/* Append another line and go around again. */
{
- int success = ds_getline (st, stream);
+ int success = ds_gets (st, stream);
where->line_number++;
if (!success)
return 1;
char *cp;
int quote = 0;
- for (cp = ds_value (st); *cp; cp++)
+ for (cp = ds_c_str (st); *cp; cp++)
if (quote)
{
if (*cp == quote)
quote = *cp;
else if (*cp == '#')
{
- ds_truncate (st, cp - ds_value (st));
+ ds_truncate (st, cp - ds_c_str (st));
break;
}
}
\f
/* Lengthed strings. */
-/* Creates a new lengthed string LS in POOL with contents as a copy of
+/* Creates a new lengthed string LS with contents as a copy of
S. */
void
-ls_create (struct pool *pool, struct len_string *ls, const char *s)
+ls_create (struct len_string *ls, const char *s)
{
ls->length = strlen (s);
- ls->string = pool_alloc (pool, ls->length + 1);
+ ls->string = xmalloc (ls->length + 1);
memcpy (ls->string, s, ls->length + 1);
}
-/* Creates a new lengthed string LS in POOL with contents as a copy of
+/* Creates a new lengthed string LS with contents as a copy of
BUFFER with length LEN. */
void
-ls_create_buffer (struct pool *pool, struct len_string *ls,
+ls_create_buffer (struct len_string *ls,
const char *buffer, size_t len)
{
ls->length = len;
- ls->string = pool_malloc (pool, len + 1);
+ ls->string = xmalloc (len + 1);
memcpy (ls->string, buffer, len);
ls->string[len] = '\0';
}
*dst = *src;
}
-/* Frees the memory in POOL backing LS. */
+/* Frees the memory backing LS. */
void
-ls_destroy (struct pool *pool, struct len_string *ls)
+ls_destroy (struct len_string *ls)
{
- pool_free (pool, ls->string);
+ free (ls->string);
}
/* Sets LS to a null pointer value. */
/* Returns a pointer to the character string in LS. */
char *
-ls_value (const struct len_string *ls)
+ls_c_str (const struct len_string *ls)
{
return (char *) ls->string;
}
size_t length;
};
-struct pool;
-void ls_create (struct pool *, struct len_string *, const char *);
-void ls_create_buffer (struct pool *, struct len_string *,
+void ls_create (struct len_string *, const char *);
+void ls_create_buffer (struct len_string *,
const char *, size_t len);
void ls_init (struct len_string *, const char *, size_t);
void ls_shallow_copy (struct len_string *, const struct len_string *);
-void ls_destroy (struct pool *, struct len_string *);
+void ls_destroy (struct len_string *);
void ls_null (struct len_string *);
int ls_null_p (const struct len_string *);
int ls_empty_p (const struct len_string *);
size_t ls_length (const struct len_string *);
-char *ls_value (const struct len_string *);
+char *ls_c_str (const struct len_string *);
char *ls_end (const struct len_string *);
+
+#if __GNUC__ > 1
+extern inline size_t
+ls_length (const struct len_string *st)
+{
+ return st->length;
+}
+
+extern inline char *
+ls_c_str (const struct len_string *st)
+{
+ return st->string;
+}
+
+extern inline char *
+ls_end (const struct len_string *st)
+{
+ return st->string + st->length;
+}
+#endif
\f
/* Dynamic strings. */
struct string
{
- struct pool *pool;
- size_t length;
- size_t size;
- char *string;
+ size_t length; /* Length, not including a null terminator. */
+ size_t capacity; /* Allocated capacity, not including one
+ extra byte allocated for null terminator. */
+ char *string; /* String data, not necessarily null
+ terminated. */
};
-void ds_create (struct pool *, struct string *, const char *);
-void ds_init (struct pool *, struct string *, size_t size);
-void ds_replace (struct string *, const char *);
+/* Constructors, destructors. */
+void ds_create (struct string *, const char *);
+void ds_init (struct string *, size_t);
void ds_destroy (struct string *);
+
+/* Copy, shrink, extend. */
+void ds_replace (struct string *, const char *);
void ds_clear (struct string *);
-void ds_extend (struct string *, size_t min_size);
+void ds_extend (struct string *, size_t);
void ds_shrink (struct string *);
-void ds_truncate (struct string *, size_t length);
+void ds_truncate (struct string *, size_t);
+void ds_rpad (struct string *, size_t length, char pad);
+/* Inspectors. */
size_t ds_length (const struct string *);
-char *ds_value (const struct string *);
+char *ds_c_str (const struct string *);
+char *ds_data (const struct string *);
char *ds_end (const struct string *);
-size_t ds_size (const struct string *);
+size_t ds_capacity (const struct string *);
+/* File input. */
struct file_locator;
-int ds_getline (struct string *st, FILE *stream);
+int ds_gets (struct string *, FILE *);
int ds_get_config_line (FILE *, struct string *, struct file_locator *);
-void ds_putchar (struct string *, int ch);
-void ds_concat (struct string *, const char *);
-void ds_concat_buffer (struct string *, const char *buf, size_t len);
-void ds_vprintf (struct string *st, const char *format, va_list args);
+
+/* Append. */
+void ds_putc (struct string *, int ch);
+void ds_puts (struct string *, const char *);
+void ds_concat (struct string *, const char *, size_t);
+void ds_vprintf (struct string *st, const char *, va_list);
void ds_printf (struct string *, const char *, ...)
PRINTF_FORMAT (2, 3);
#if __GNUC__ > 1
extern inline void
-ds_putchar (struct string *st, int ch)
+ds_putc (struct string *st, int ch)
{
- if (st->length == st->size)
+ if (st->length == st->capacity)
ds_extend (st, st->length + 1);
st->string[st->length++] = ch;
}
}
extern inline char *
-ds_value (const struct string *st)
+ds_c_str (const struct string *st)
{
((char *) st->string)[st->length] = '\0';
return st->string;
}
+extern inline char *
+ds_data (const struct string *st)
+{
+ return st->string;
+}
+
extern inline char *
ds_end (const struct string *st)
{
{
if (!lex_force_string ())
return 0;
- strncpy (v->s, ds_value (&tokstr), ds_length (&tokstr));
+ strncpy (v->s, ds_c_str (&tokstr), ds_length (&tokstr));
}
lex_get ();
else
len = strlen (text);
- ls_create_buffer (table->container, s, text, len);
+ ls_create_buffer (s, text, len);
+ pool_register (table->container, free, s->string);
if (opt & TAT_PRINTF)
local_free (text);
cp = stpcpy (cp, ". ");
if (!ls_empty_p (&t->title))
{
- memcpy (cp, ls_value (&t->title), ls_length (&t->title));
+ memcpy (cp, ls_c_str (&t->title), ls_length (&t->title));
cp += ls_length (&t->title);
}
*cp = 0;
}
} else {
struct tab_joined_cell *j =
- (struct tab_joined_cell *) ls_value (&t->cc[index]);
+ (struct tab_joined_cell *) ls_c_str (&t->cc[index]);
if (j->hit != tab_hit)
{
return CMD_FAILURE;
if (*title)
free (*title);
- *title = xstrdup (ds_value (&tokstr));
+ *title = xstrdup (ds_c_str (&tokstr));
lex_get ();
if (token != '.')
{
lex_error (_("expecting string"));
return 0;
}
- st_bare_pad_copy (value.s, ds_value (&tokstr), MAX_SHORT_STRING);
+ st_bare_pad_copy (value.s, ds_c_str (&tokstr), MAX_SHORT_STRING);
}
else
{
msg (SW, _("Truncating value label to 60 characters."));
ds_truncate (&tokstr, 60);
}
- label = ds_value (&tokstr);
+ label = ds_c_str (&tokstr);
for (i = 0; i < var_cnt; i++)
val_labs_replace (vars[i]->val_labs, value, label);
{
if (v[i]->label)
free (v[i]->label);
- v[i]->label = xstrdup (ds_value (&tokstr));
+ v[i]->label = xstrdup (ds_c_str (&tokstr));
}
lex_get ();
+Sun May 30 19:18:26 2004 Ben Pfaff <blp@gnu.org>
+
+ * command/tabs.sh: Default tab width is now 4.
+
+ * command/data-list.sh: New test.
+
+ * Makefile.am: (TESTS) Add command/data-list.sh.
+
Sun Apr 11 14:21:16 2004 Ben Pfaff <blp@gnu.org>
* stats/moments.sh: Now that our one-pass moments algorithm is
command/beg-data.sh \
command/bignum.sh \
command/count.sh \
+ command/data-list.sh \
command/erase.sh \
command/file-label.sh \
command/filter.sh \
--- /dev/null
+#!/bin/sh
+
+# This program tests the DATA LIST input program.
+
+TEMPDIR=/tmp/pspp-tst-$$
+
+here=`pwd`;
+
+# ensure that top_srcdir is absolute
+cd $top_srcdir; top_srcdir=`pwd`
+
+export STAT_CONFIG_PATH=$top_srcdir/config
+
+
+cleanup()
+{
+ rm -rf $TEMPDIR
+}
+
+
+fail()
+{
+ echo $activity
+ echo FAILED
+ cleanup;
+ exit 1;
+}
+
+
+no_result()
+{
+ echo $activity
+ echo NO RESULT;
+ cleanup;
+ exit 2;
+}
+
+pass()
+{
+ cleanup;
+ exit 0;
+}
+
+mkdir -p $TEMPDIR
+
+cd $TEMPDIR
+
+# Create command file.
+activity="create program"
+cat > $TEMPDIR/data-list.stat << EOF
+data list free/A B C D.
+begin data.
+,1,2,3
+,4,,5
+6
+7,
+8 9
+0,1,,,
+,,,,
+2
+
+3
+4
+5
+end data.
+list.
+
+data list free (tab)/A B C D.
+begin data.
+1 2 3 4
+1 2 3
+1 2 4
+1 2
+1 3 4
+1 3
+1 4
+1
+ 2 3 4
+ 2 3
+ 2 4
+ 2
+ 3 4
+ 3
+ 4
+
+end data.
+list.
+EOF
+if [ $? -ne 0 ] ; then no_result ; fi
+
+
+activity="run program"
+$SUPERVISOR $here/../src/pspp --testing-mode -o raw-ascii --testing-mode $TEMPDIR/data-list.stat # > $TEMPDIR/errs
+if [ $? -ne 0 ] ; then fail ; fi
+
+activity="compare output"
+diff -b -B $TEMPDIR/pspp.list - << EOF
+ A B C D
+-------- -------- -------- --------
+ . 1.00 2.00 3.00
+ . 4.00 . 5.00
+ 6.00 7.00 8.00 9.00
+ .00 1.00 . .
+ . . . .
+ 2.00 3.00 4.00 5.00
+
+ A B C D
+-------- -------- -------- --------
+ 1.00 2.00 3.00 4.00
+ 1.00 2.00 3.00 .
+ 1.00 2.00 . 4.00
+ 1.00 2.00 . .
+ 1.00 . 3.00 4.00
+ 1.00 . 3.00 .
+ 1.00 . . 4.00
+ 1.00 . . .
+ . 2.00 3.00 4.00
+ . 2.00 3.00 .
+ . 2.00 . 4.00
+ . 2.00 . .
+ . . 3.00 4.00
+ . . 3.00 .
+ . . . 4.00
+ . . . .
+EOF
+if [ $? -ne 0 ] ; then fail ; fi
+
+pass;
if [ $? -ne 0 ] ; then no_result ; fi
activity="create program 2"
-printf "\t1\t12\t123\t1234\t12345\t123456\t\t1234567\t12345678\tasdf\tjkl\n" >> $TEMPDIR/tabs.stat
+printf "\t1\t12\t123\t1234\t12345\n" >> $TEMPDIR/tabs.stat
if [ $? -ne 0 ] ; then no_result ; fi
#========#======#=======#======#
|X | 1| 1- 80|A80 |
+--------+------+-------+------+
- 1 12 123 1234 12345 123456 1234567 12345678
+ 1 12 123 1234 12345
EOF
if [ $? -ne 0 ] ; then fail ; fi