-/* 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. The caller must not
- or destroy this constant string.
-
- After parsing the field, sets the current position in the
- record to just past the field and any trailing delimiter.
- Returns 0 on failure or a 1-based column number indicating the
- beginning of the field on success. */
-static bool
-cut_field (const struct data_list_pgm *dls, struct substring *field)
-{
- struct substring line, p;
-
- if (dfm_eof (dls->reader))
- return false;
- if (ds_is_empty (&dls->delims))
- dfm_expand_tabs (dls->reader);
- line = p = dfm_get_record (dls->reader);
-
- if (ds_is_empty (&dls->delims))
- {
- bool missing_quote = false;
-
- /* Skip leading whitespace. */
- ss_ltrim (&p, ss_cstr (CC_SPACES));
- if (ss_is_empty (p))
- return false;
-
- /* Handle actual data, whether quoted or unquoted. */
- if (ss_match_char (&p, '\''))
- missing_quote = !ss_get_until (&p, '\'', field);
- else if (ss_match_char (&p, '"'))
- missing_quote = !ss_get_until (&p, '"', field);
- else
- ss_get_chars (&p, ss_cspan (p, ss_cstr ("," CC_SPACES)), field);
- if (missing_quote)
- msg (SW, _("Quoted string extends beyond end of line."));
-
- /* Skip trailing whitespace and a single comma if present. */
- ss_ltrim (&p, ss_cstr (CC_SPACES));
- ss_match_char (&p, ',');
-
- dfm_forward_columns (dls->reader, ss_length (line) - ss_length (p));
- }
- else
- {
- if (!ss_is_empty (p))
- ss_get_chars (&p, ss_cspan (p, ds_ss (&dls->delims)), field);
- else if (dfm_columns_past_end (dls->reader) == 0)
- {
- /* A blank line or a line that ends in a delimiter has a
- trailing blank field. */
- *field = p;
- }
- else
- return false;
-
- /* Advance past the field.
-
- Also advance past a trailing delimiter, regardless of
- whether one actually existed. If we "skip" a delimiter
- that was not actually there, then we will return
- end-of-line on our next call, which is what we want. */
- dfm_forward_columns (dls->reader, ss_length (line) - ss_length (p) + 1);
- }
- return true;
-}
-
-static bool read_from_data_list_fixed (const struct data_list_pgm *,
- struct ccase *);
-static bool read_from_data_list_free (const struct data_list_pgm *,
- struct ccase *);
-static bool read_from_data_list_list (const struct data_list_pgm *,
- struct ccase *);
-
-/* Reads a case from DLS into C.
- Returns true if successful, false at end of file or on I/O error. */
-static bool
-read_from_data_list (const struct data_list_pgm *dls, struct ccase *c)
-{
- bool retval;
-
- dfm_push (dls->reader);
- switch (dls->type)
- {
- case DLS_FIXED:
- retval = read_from_data_list_fixed (dls, c);
- break;
- case DLS_FREE:
- retval = read_from_data_list_free (dls, c);
- break;
- case DLS_LIST:
- retval = read_from_data_list_list (dls, c);
- break;
- default:
- NOT_REACHED ();
- }
- dfm_pop (dls->reader);
-
- return retval;
-}
-
-/* Reads a case from the data file into C, parsing it according
- to fixed-format syntax rules in DLS.
- Returns true if successful, false at end of file or on I/O error. */
-static bool
-read_from_data_list_fixed (const struct data_list_pgm *dls, struct ccase *c)
-{
- enum legacy_encoding encoding = dfm_reader_get_legacy_encoding (dls->reader);
- struct dls_var_spec *spec;
- int row;
-
- if (dfm_eof (dls->reader))
- return false;
-
- spec = ll_to_dls_var_spec (ll_head (&dls->specs));
- for (row = 1; row <= dls->record_cnt; row++)
- {
- struct substring line;
-
- if (dfm_eof (dls->reader))
- {
- msg (SW, _("Partial case of %d of %d records discarded."),
- row - 1, dls->record_cnt);
- return false;
- }
- dfm_expand_tabs (dls->reader);
- line = dfm_get_record (dls->reader);
-
- ll_for_each_continue (spec, struct dls_var_spec, ll, &dls->specs)
- {
- if (row < spec->record)
- break;
-
- data_in (ss_substr (line, spec->first_column - 1,
- spec->input.w),
- encoding, spec->input.type, spec->input.d,
- spec->first_column, case_data_rw_idx (c, spec->fv),
- fmt_var_width (&spec->input));
- }
-
- dfm_forward_record (dls->reader);
- }
-
- return true;
-}
-
-/* Reads a case from the data file into C, parsing it according
- to free-format syntax rules in DLS.
- Returns true if successful, false at end of file or on I/O error. */
-static bool
-read_from_data_list_free (const struct data_list_pgm *dls, struct ccase *c)
-{
- enum legacy_encoding encoding = dfm_reader_get_legacy_encoding (dls->reader);
- struct dls_var_spec *spec;
-
- ll_for_each (spec, struct dls_var_spec, ll, &dls->specs)
- {
- struct substring field;
-
- /* Cut out a field and read in a new record if necessary. */
- while (!cut_field (dls, &field))
- {
- if (!dfm_eof (dls->reader))
- dfm_forward_record (dls->reader);
- if (dfm_eof (dls->reader))
- {
- if (&spec->ll != ll_head (&dls->specs))
- msg (SW, _("Partial case discarded. The first variable "
- "missing was %s."), spec->name);
- return false;
- }
- }
-
- data_in (field, encoding, spec->input.type, 0,
- dfm_get_column (dls->reader, ss_data (field)),
- case_data_rw_idx (c, spec->fv), fmt_var_width (&spec->input));
- }
- return true;
-}
-
-/* Reads a case from the data file and parses it according to
- list-format syntax rules.
- Returns true if successful, false at end of file or on I/O error. */
-static bool
-read_from_data_list_list (const struct data_list_pgm *dls, struct ccase *c)
-{
- enum legacy_encoding encoding = dfm_reader_get_legacy_encoding (dls->reader);
- struct dls_var_spec *spec;
-
- if (dfm_eof (dls->reader))
- return false;
-
- ll_for_each (spec, struct dls_var_spec, ll, &dls->specs)
- {
- struct substring field;
-
- if (!cut_field (dls, &field))
- {
- 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."),
- spec->name);
- ll_for_each_continue (spec, struct dls_var_spec, ll, &dls->specs)
- {
- int width = fmt_var_width (&spec->input);
- if (width == 0)
- case_data_rw_idx (c, spec->fv)->f = SYSMIS;
- else
- memset (case_data_rw_idx (c, spec->fv)->s, ' ', width);
- }
- break;
- }
-
- data_in (field, encoding, spec->input.type, 0,
- dfm_get_column (dls->reader, ss_data (field)),
- case_data_rw_idx (c, spec->fv), fmt_var_width (&spec->input));
- }
-
- dfm_forward_record (dls->reader);
- return true;
-}
-
-/* Destroys DATA LIST transformation DLS.