#include <errno.h>
#include <float.h>
#include <c-ctype.h>
+#include <minmax.h>
#include <libpspp/alloc.h>
+#include <libpspp/assertion.h>
#include <libpspp/message.h>
#include <libpspp/compiler.h>
#include <libpspp/magic.h>
#include <libpspp/misc.h>
#include <libpspp/str.h>
#include <libpspp/hash.h>
+#include <libpspp/array.h>
#include "sys-file-reader.h"
#include "sfm-private.h"
FILE *file; /* File stream. */
int reverse_endian; /* 1=file has endianness opposite us. */
- int fix_specials; /* 1=SYSMIS/HIGHEST/LOWEST differs from us. */
int value_cnt; /* Number of `union values's per case. */
long case_cnt; /* Number of cases, -1 if unknown. */
int compressed; /* 1=compressed, 0=not compressed. */
- double bias; /* Compression bias, usually 100.0. */
+ double bias; /* Compression bias, usually 100.0. */
int weight_idx; /* 0-based index of weighting variable, or -1. */
bool ok; /* False after an I/O error or corrupt data. */
+ bool has_vls; /* True if the file has one or more Very Long Strings*/
/* Variables. */
- struct sfm_var *vars; /* Variables. */
+ struct sfm_var *vars;
+ size_t var_cnt;
/* File's special constants. */
flt64 sysmis;
va_list args;
struct string text;
- ds_create (&text, _("corrupt system file: "));
+ ds_init_cstr (&text, _("corrupt system file: "));
va_start (args, format);
- ds_vprintf (&text, format, args);
+ ds_put_vformat (&text, format, args);
va_end (args);
m.category = msg_class_to_category (class);
m.severity = msg_class_to_severity (class);
m.where.file_name = NULL;
m.where.line_number = 0;
- m.text = ds_c_str (&text);
+ m.text = ds_cstr (&text);
msg_emit (&m);
}
};
static int
-pair_sn_compare(const void *_p1, const void *_p2, void *aux UNUSED)
+pair_sn_compare(const void *_p1, const void *_p2, const void *aux UNUSED)
{
int i;
}
static unsigned int
-pair_sn_hash(const void *_p, void *aux UNUSED)
+pair_sn_hash(const void *_p, const void *aux UNUSED)
{
int i;
const struct name_pair *p = _p;
}
static void
-pair_sn_free(void *p, void *aux UNUSED)
+pair_sn_free(void *p, const void *aux UNUSED)
{
free(p);
}
+
/* Opens the system file designated by file handle FH for
reading. Reads the system file's dictionary into *DICT.
If INFO is non-null, then it receives additional info about the
/* A hash table of long variable names indexed by short name */
struct hsh_table *short_to_long = NULL;
-
*dict = dict_create ();
if (!fh_open (fh, FH_REF_FILE, "system file", "rs"))
goto error;
r->file = fn_open (fh_get_file_name (fh), "rb");
r->reverse_endian = 0;
- r->fix_specials = 0;
r->value_cnt = 0;
r->case_cnt = 0;
r->compressed = 0;
r->bias = 100.0;
r->weight_idx = -1;
r->ok = true;
+ r->has_vls = false;
r->vars = NULL;
{
struct
{
- int32_t subtype P;
- int32_t size P;
- int32_t count P;
- }
+ int32_t subtype ;
+ int32_t size ;
+ int32_t count ;
+ } ATTRIBUTE((packed))
data;
unsigned long bytes;
{
struct
{
- int32_t measure P;
- int32_t width P;
- int32_t align P;
- }
+ int32_t measure ;
+ int32_t width ;
+ int32_t align ;
+ } ATTRIBUTE((packed))
params;
struct variable *v;
assertive_buf_read (r, ¶ms, sizeof(params), 0);
+ if ( ! measure_is_valid(params.measure)
+ ||
+ ! alignment_is_valid(params.align))
+ {
+ msg(MW,
+ _("%s: Invalid variable display parameters. Default parameters substituted."),
+ fh_get_file_name(r->fh));
+ continue;
+ }
+
v = dict_get_var(*dict, i);
v->measure = params.measure;
case 13: /* SPSS 12.0 Long variable name map */
{
- char *short_name, *save_ptr;
+ char *short_name;
+ char *save_ptr = NULL;
int idx;
/* Read data. */
records have been processed. --- JMD 27 April 2006
*/
- /* For compatability, make sure dictionary
+ /* For compatibility, make sure dictionary
is in long variable name map order. In
the common case, this has no effect,
because the dictionary and the long
}
buffer[bytes] = '\0';
+ r->has_vls = true;
/* Note: SPSS v13 terminates this record with 00,
whereas SPSS v14 terminates it with 00 09. We must
goto error;
}
-
+
l = length;
if ( v->width > EFFECTIVE_LONG_STRING_LENGTH )
l -= EFFECTIVE_LONG_STRING_LENGTH;
dict_delete_var(*dict, v_next);
}
-
+
+ assert ( length > MAX_LONG_STRING );
+
v->width = length;
v->print.w = v->width;
v->write.w = v->width;
+ v->nv = DIV_RND_UP (length, MAX_SHORT_STRING);
}
eq_seen = false;
memset(name, 0, SHORT_NAME_LEN+1);
}
}
free(buffer);
+ dict_compact_values(*dict);
}
break;
success:
/* Come here on successful completion. */
+ /* Create an index of dictionary variable widths for
+ sfm_read_case to use. We cannot use the `struct variables'
+ from the dictionary we created, because the caller owns the
+ dictionary and may destroy or modify its variables. */
+ {
+ size_t i;
+
+ r->var_cnt = dict_get_var_cnt (*dict);
+ r->vars = xnmalloc (r->var_cnt, sizeof *r->vars);
+ for (i = 0; i < r->var_cnt; i++)
+ {
+ struct variable *v = dict_get_var (*dict, i);
+ struct sfm_var *sv = &r->vars[i];
+ sv->width = v->width;
+ sv->fv = v->fv;
+ }
+ }
free (var_by_idx);
hsh_destroy(short_to_long);
*var_by_idx = 0;
- /* Pre-allocate variables. */
- if (r->value_cnt != -1)
- {
- *var_by_idx = xnmalloc (r->value_cnt, sizeof **var_by_idx);
- r->vars = xnmalloc (r->value_cnt, sizeof *r->vars);
- }
-
/* Read in the entry for each variable and use the info to
initialize the dictionary. */
}
*var_by_idx = xnrealloc (*var_by_idx, i + 1, sizeof **var_by_idx);
- r->vars = xnrealloc (r->vars, i + 1, sizeof *r->vars);
/* If there was a long string previously, make sure that the
continuations are present; otherwise make sure there aren't
fh_get_file_name (r->fh), i));
- r->vars[i].width = -1;
(*var_by_idx)[i] = NULL;
long_string_count--;
continue;
if (!parse_format_spec (r, sv.print, &vv->print, vv)
|| !parse_format_spec (r, sv.write, &vv->write, vv))
goto error;
-
- r->vars[i].width = vv->width;
- r->vars[i].fv = vv->fv;
-
}
/* Some consistency checks. */
parse_format_spec (struct sfm_reader *r, int32_t s,
struct fmt_spec *f, const struct variable *v)
{
- f->type = translate_fmt ((s >> 16) & 0xff);
- if (f->type == -1)
+ bool ok;
+
+ if (!fmt_from_io ((s >> 16) & 0xff, &f->type))
lose ((ME, _("%s: Bad format specifier byte (%d)."),
fh_get_file_name (r->fh), (s >> 16) & 0xff));
f->w = (s >> 8) & 0xff;
f->d = s & 0xff;
- if ((v->type == ALPHA) ^ ((formats[f->type].cat & FCAT_STRING) != 0))
+ if ((v->type == ALPHA) ^ (fmt_is_string (f->type) != 0))
lose ((ME, _("%s: %s variable %s has %s format specifier %s."),
fh_get_file_name (r->fh),
v->type == ALPHA ? _("String") : _("Numeric"),
v->name,
- formats[f->type].cat & FCAT_STRING ? _("string") : _("numeric"),
- formats[f->type].name));
+ fmt_is_string (f->type) ? _("string") : _("numeric"),
+ fmt_name (f->type)));
- if (!check_output_specifier (f, false)
- || !check_specifier_width (f, v->width, false))
+ msg_disable ();
+ ok = fmt_check_output (f) && fmt_check_width_compat (f, v->width);
+ msg_enable ();
+
+ if (!ok)
{
+ char fmt_string[FMT_STRING_LEN_MAX + 1];
msg (ME, _("%s variable %s has invalid format specifier %s."),
v->type == NUMERIC ? _("Numeric") : _("String"),
- v->name, fmt_to_string (f));
- *f = v->type == NUMERIC ? f8_2 : make_output_format (FMT_A, v->width, 0);
+ v->name, fmt_to_string (f, fmt_string));
+ *f = (v->type == NUMERIC
+ ? fmt_for_output (FMT_F, 8, 2)
+ : fmt_for_output (FMT_A, v->width, 0));
}
return 1;
p = r->x;
}
- abort ();
+ NOT_REACHED ();
success:
/* We have filled up an entire record. Update state and return
{
if (!r->ok)
return 0;
-
- if (!r->compressed && sizeof (flt64) == sizeof (double))
+
+ if (!r->compressed && sizeof (flt64) == sizeof (double) && ! r->has_vls)
{
/* Fast path: external and internal representations are the
same, except possibly for endianness or SYSMIS. Read
{
int i;
- for (i = 0; i < r->value_cnt; i++)
+ for (i = 0; i < r->var_cnt; i++)
if (r->vars[i].width == 0)
bswap_flt64 (&case_data_rw (c, r->vars[i].fv)->f);
}
{
int i;
- for (i = 0; i < r->value_cnt; i++)
+ for (i = 0; i < r->var_cnt; i++)
if (r->vars[i].width == 0 && case_num (c, i) == r->sysmis)
case_data_rw (c, r->vars[i].fv)->f = SYSMIS;
}
bounce_size = sizeof *bounce * r->value_cnt;
bounce = bounce_cur = local_alloc (bounce_size);
+ memset(bounce, 0, bounce_size);
+
if (!r->compressed)
read_ok = fread_ok (r, bounce, bounce_size);
else
return 0;
}
- for (i = 0; i < r->value_cnt; i++)
+ for (i = 0; i < r->var_cnt; i++)
{
- struct sfm_var *v = &r->vars[i];
+ struct sfm_var *sv = &r->vars[i];
- if (v->width == 0)
+ if (sv->width == 0)
{
flt64 f = *bounce_cur++;
if (r->reverse_endian)
bswap_flt64 (&f);
- case_data_rw (c, v->fv)->f = f == r->sysmis ? SYSMIS : f;
+ case_data_rw (c, sv->fv)->f = f == r->sysmis ? SYSMIS : f;
}
- else if (v->width != -1)
+ else
{
- memcpy (case_data_rw (c, v->fv)->s, bounce_cur, v->width);
- bounce_cur += DIV_RND_UP (v->width, sizeof (flt64));
+ flt64 *bc_start = bounce_cur;
+ int ofs = 0;
+ while (ofs < sv->width )
+ {
+ const int chunk = MIN (MAX_LONG_STRING, sv->width - ofs);
+ memcpy (case_data_rw (c, sv->fv)->s + ofs, bounce_cur, chunk);
+
+ bounce_cur += DIV_RND_UP (chunk, sizeof (flt64));
+
+ ofs += chunk;
+ }
+ bounce_cur = bc_start + width_to_bytes(sv->width) / sizeof(flt64);
}
}