work
authorBen Pfaff <blp@cs.stanford.edu>
Mon, 11 Aug 2025 20:20:26 +0000 (13:20 -0700)
committerBen Pfaff <blp@cs.stanford.edu>
Mon, 11 Aug 2025 20:20:26 +0000 (13:20 -0700)
98 files changed:
rust/Cargo.lock
rust/pspp/Cargo.toml
rust/pspp/src/dictionary.rs
rust/pspp/src/main.rs
rust/pspp/src/output/cairo/driver.rs
rust/pspp/src/output/csv.rs
rust/pspp/src/output/driver.rs
rust/pspp/src/output/page.rs
rust/pspp/src/output/pivot/test.rs
rust/pspp/src/output/spv.rs
rust/pspp/src/output/text.rs
rust/pspp/src/sys/cooked.rs
rust/pspp/src/sys/test.rs
rust/pspp/src/sys/testdata/attributes.expected
rust/pspp/src/sys/testdata/bad_machine_float_info_size.expected
rust/pspp/src/sys/testdata/bad_machine_integer_info_count.expected
rust/pspp/src/sys/testdata/bad_machine_integer_info_endianness.expected
rust/pspp/src/sys/testdata/bad_machine_integer_info_float_format.expected
rust/pspp/src/sys/testdata/bad_variable_name_in_long_string_value_label.expected
rust/pspp/src/sys/testdata/bad_variable_name_in_variable_value_pair.expected
rust/pspp/src/sys/testdata/bad_very_long_string_length.expected
rust/pspp/src/sys/testdata/bad_very_long_string_segment_width.expected
rust/pspp/src/sys/testdata/compressed_data.expected
rust/pspp/src/sys/testdata/compressed_data_other_bias.expected
rust/pspp/src/sys/testdata/compressed_data_zero_bias.expected
rust/pspp/src/sys/testdata/documents.expected
rust/pspp/src/sys/testdata/duplicate_attribute_name.expected
rust/pspp/src/sys/testdata/duplicate_long_variable_name.expected
rust/pspp/src/sys/testdata/duplicate_value_labels_type.expected
rust/pspp/src/sys/testdata/duplicate_variable_name.expected
rust/pspp/src/sys/testdata/empty_document_record.expected
rust/pspp/src/sys/testdata/extra_product_info.expected
rust/pspp/src/sys/testdata/fewer_data_records_than_indicated_by_file_header.expected
rust/pspp/src/sys/testdata/integer_overflows_in_long_string_missing_values.expected
rust/pspp/src/sys/testdata/invalid_long_string_missing_values.expected
rust/pspp/src/sys/testdata/invalid_variable_format.expected
rust/pspp/src/sys/testdata/invalid_variable_name.expected
rust/pspp/src/sys/testdata/long_variable_names.expected
rust/pspp/src/sys/testdata/missing_attribute_value.expected
rust/pspp/src/sys/testdata/missing_newline_after_variable_name_in_mrsets.expected
rust/pspp/src/sys/testdata/missing_string_continuation.expected
rust/pspp/src/sys/testdata/mixed_variable_types_in_mrsets.expected
rust/pspp/src/sys/testdata/more_data_records_than_indicated_by_file_header.expected
rust/pspp/src/sys/testdata/multiple_documents_records.expected
rust/pspp/src/sys/testdata/multiple_response_sets.expected
rust/pspp/src/sys/testdata/multiple_response_sets_bad_counted_string.expected
rust/pspp/src/sys/testdata/multiple_response_sets_bad_name.expected
rust/pspp/src/sys/testdata/multiple_response_sets_counted_string_bad_length.expected
rust/pspp/src/sys/testdata/multiple_response_sets_counted_string_missing_space.expected
rust/pspp/src/sys/testdata/multiple_response_sets_duplicate_variable_name.expected
rust/pspp/src/sys/testdata/multiple_response_sets_missing_label_source.expected
rust/pspp/src/sys/testdata/multiple_response_sets_missing_newline_after_variable_name.expected
rust/pspp/src/sys/testdata/multiple_response_sets_missing_space_after_c.expected
rust/pspp/src/sys/testdata/multiple_response_sets_missing_space_after_counted_string.expected
rust/pspp/src/sys/testdata/multiple_response_sets_missing_space_after_e.expected
rust/pspp/src/sys/testdata/multiple_response_sets_unexpected_label_source.expected
rust/pspp/src/sys/testdata/no_variables.expected
rust/pspp/src/sys/testdata/null_dereference_skipping_bad_extension_record_18.expected
rust/pspp/src/sys/testdata/partial_compressed_data_record.expected
rust/pspp/src/sys/testdata/partial_data_record_between_variables.expected
rust/pspp/src/sys/testdata/partial_data_record_within_long_string.expected
rust/pspp/src/sys/testdata/test-encrypted.expected
rust/pspp/src/sys/testdata/type_4_record_names_long_string_variable.expected
rust/pspp/src/sys/testdata/unknown_encoding.expected
rust/pspp/src/sys/testdata/unknown_extension_record.expected
rust/pspp/src/sys/testdata/unquoted_attribute_value.expected
rust/pspp/src/sys/testdata/unspecified_number_of_variable_positions.expected
rust/pspp/src/sys/testdata/value_label_variable_indexes_must_be_in_correct_range.expected
rust/pspp/src/sys/testdata/value_label_variable_indexes_must_not_be_long_string_continuation.expected
rust/pspp/src/sys/testdata/value_label_with_no_associated_variables.expected
rust/pspp/src/sys/testdata/value_labels.expected
rust/pspp/src/sys/testdata/variable_display_with_width.expected
rust/pspp/src/sys/testdata/variable_display_without_width.expected
rust/pspp/src/sys/testdata/variable_labels_and_missing_values.expected
rust/pspp/src/sys/testdata/variable_roles.expected
rust/pspp/src/sys/testdata/variable_sets.expected
rust/pspp/src/sys/testdata/variable_sets_unknown_variable.expected
rust/pspp/src/sys/testdata/variables_for_value_label_must_all_be_same_type.expected
rust/pspp/src/sys/testdata/very_long_strings.expected
rust/pspp/src/sys/testdata/weight_must_be_numeric.expected
rust/pspp/src/sys/testdata/weight_variable_bad_index.expected
rust/pspp/src/sys/testdata/weight_variable_continuation.expected
rust/pspp/src/sys/testdata/write-numeric-simple.expected
rust/pspp/src/sys/testdata/write-numeric-uncompressed.expected
rust/pspp/src/sys/testdata/write-numeric-zlib.expected
rust/pspp/src/sys/testdata/write-string-simple.expected
rust/pspp/src/sys/testdata/write-string-uncompressed.expected
rust/pspp/src/sys/testdata/write-string-zlib.expected
rust/pspp/src/sys/testdata/wrong_display_alignment.expected
rust/pspp/src/sys/testdata/wrong_display_measurement_level.expected
rust/pspp/src/sys/testdata/wrong_display_parameter_count.expected
rust/pspp/src/sys/testdata/wrong_display_parameter_size.expected
rust/pspp/src/sys/testdata/wrong_special_floats.expected
rust/pspp/src/sys/testdata/wrong_variable_positions.expected
rust/pspp/src/sys/testdata/wrong_variable_positions_but_v13.expected
rust/pspp/src/sys/testdata/zcompressed_data.expected
rust/pspp/src/sys/testdata/zcompressed_data_uncompressed_size_block_size.expected
rust/pspp/src/sys/testdata/zero_or_one_variable_in_mrset.expected

index f07ce38b345aa1b23f95e44d3bc735bdfa9505d3..169d491b73cf3419f6b28e695414f2b558da5aea 100644 (file)
@@ -1637,6 +1637,7 @@ dependencies = [
  "smallstr",
  "smallvec",
  "thiserror",
+ "toml 0.9.5",
  "unicase",
  "unicode-linebreak",
  "unicode-properties",
@@ -1866,6 +1867,15 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "serde_spanned"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83"
+dependencies = [
+ "serde",
+]
+
 [[package]]
 name = "sha1"
 version = "0.10.6"
@@ -1992,7 +2002,7 @@ dependencies = [
  "cfg-expr",
  "heck",
  "pkg-config",
- "toml",
+ "toml 0.8.22",
  "version-compare",
 ]
 
@@ -2119,11 +2129,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae"
 dependencies = [
  "serde",
- "serde_spanned",
- "toml_datetime",
+ "serde_spanned 0.6.8",
+ "toml_datetime 0.6.9",
  "toml_edit",
 ]
 
+[[package]]
+name = "toml"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75129e1dc5000bfbaa9fee9d1b21f974f9fbad9daec557a521ee6e080825f6e8"
+dependencies = [
+ "indexmap",
+ "serde",
+ "serde_spanned 1.0.0",
+ "toml_datetime 0.7.0",
+ "toml_parser",
+ "toml_writer",
+ "winnow",
+]
+
 [[package]]
 name = "toml_datetime"
 version = "0.6.9"
@@ -2133,6 +2158,15 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "toml_datetime"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3"
+dependencies = [
+ "serde",
+]
+
 [[package]]
 name = "toml_edit"
 version = "0.22.26"
@@ -2141,11 +2175,26 @@ checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e"
 dependencies = [
  "indexmap",
  "serde",
- "serde_spanned",
- "toml_datetime",
+ "serde_spanned 0.6.8",
+ "toml_datetime 0.6.9",
+ "winnow",
+]
+
+[[package]]
+name = "toml_parser"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b551886f449aa90d4fe2bdaa9f4a2577ad2dde302c61ecf262d80b116db95c10"
+dependencies = [
  "winnow",
 ]
 
+[[package]]
+name = "toml_writer"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fcc842091f2def52017664b53082ecbbeb5c7731092bad69d2c63050401dfd64"
+
 [[package]]
 name = "tower"
 version = "0.4.13"
index b26173eb5ddb3b5d75f3d23f2a3e032b0d931f02..a6c91db72858fe0e5080c5347fdc2fe0ea2f41ba 100644 (file)
@@ -51,6 +51,7 @@ zeroize = "1.8.1"
 unicode-properties = "0.1.3"
 unicode-segmentation = "1.12.0"
 serde_json = "1.0.141"
+toml = "0.9.5"
 
 [target.'cfg(windows)'.dependencies]
 windows-sys = { version = "0.48.0", features = ["Win32_Globalization"] }
index d7db1173199d805120f484678a3e790ad88be80d..e01e8af5b563bbc58d00f4b6d94b46967a6c9847 100644 (file)
@@ -519,6 +519,27 @@ impl Dictionary {
         (group, values)
     }
 
+    pub fn to_pivot_table(&self) -> PivotTable {
+        let (group, data) = self.to_pivot_rows();
+        PivotTable::new([(Axis3::Y, Dimension::new(group))]).with_data(
+            data.into_iter()
+                .enumerate()
+                .filter(|(_row, value)| !value.is_empty())
+                .map(|(row, value)| ([row], value)),
+        )
+    }
+
+    pub fn all_pivot_tables(&self) -> Vec<PivotTable> {
+        let mut pivot_tables = Vec::new();
+        pivot_tables.push(self.to_pivot_table());
+        pivot_tables.push(self.output_variables().to_pivot_table());
+        pivot_tables.extend(self.output_value_labels().to_pivot_table());
+        pivot_tables.extend(self.output_mrsets().to_pivot_table());
+        pivot_tables.extend(self.output_attributes().to_pivot_table());
+        pivot_tables.extend(self.output_variable_sets().to_pivot_table());
+        pivot_tables
+    }
+
     pub fn short_names(&self) -> Vec<SmallVec<[Identifier; 1]>> {
         struct PickShortName<'a> {
             variable_name: &'a Identifier,
@@ -717,6 +738,7 @@ impl<'a> OutputVariables<'a> {
             (Axis3::Y, Dimension::new(names)),
             (Axis3::X, Dimension::new(attributes)),
         ])
+        .with_title("Variables")
         .with_show_empty();
         for (var_index, variable) in self.dictionary.variables.iter().enumerate() {
             for (field, field_index) in &columns {
index 77c63ba1f5aa31aeedafaca6e00c00c72bdbab78..2c7287e95d2264851a2b9cb6df2ad9362be8568a 100644 (file)
@@ -19,6 +19,10 @@ use clap::{Args, Parser, Subcommand, ValueEnum};
 use encoding_rs::Encoding;
 use pspp::{
     crypto::EncryptedFile,
+    output::{
+        driver::{Config, Driver, DriverType},
+        Details, Item, Text,
+    },
     sys::{
         self,
         raw::{infer_encoding, records::Compression, Decoder, Magic, Reader, Record},
@@ -27,10 +31,15 @@ use pspp::{
 };
 use serde::Serialize;
 use std::{
+    cell::RefCell,
+    ffi::OsStr,
+    fmt::{Display, Write as _},
     fs::File,
     io::{stdout, BufReader, Write},
     path::{Path, PathBuf},
-    str,
+    rc::Rc,
+    str::{self, FromStr},
+    sync::Arc,
 };
 use thiserror::Error as ThisError;
 use zeroize::Zeroizing;
@@ -226,38 +235,153 @@ impl Decrypt {
 /// Show SPSS system file dictionary and data.
 #[derive(Args, Clone, Debug)]
 struct Show {
+    /// File to show.
+    #[arg(required = true)]
+    input_file: PathBuf,
+
+    /// Output file name.  If omitted, output is written to stdout.
+    output_file: Option<PathBuf>,
+
+    /// Output driver configuration options.
+    #[arg(short = 'o')]
+    output_options: Vec<String>,
+
     /// Maximum number of cases to print.
     #[arg(long = "data", default_value_t = 0)]
     max_cases: u64,
 
-    /// Files to show.
-    #[arg(required = true)]
-    files: Vec<PathBuf>,
-
     /// What to show.
     #[arg(short, long, value_enum, default_value_t)]
     mode: Mode,
 
     /// Output format.
-    #[arg(long, value_enum, default_value_t)]
-    format: ShowFormat,
+    #[arg(long, value_parser = ShowFormat::from_str)]
+    format: Option<ShowFormat>,
 
     /// The encoding to use.
     #[arg(long, value_parser = parse_encoding)]
     encoding: Option<&'static Encoding>,
 }
 
-impl Show {
-    fn run(self) -> Result<()> {
-        for file in &self.files {
-            self.show(file)?;
+enum Output {
+    Driver {
+        driver: Rc<RefCell<Box<dyn Driver>>>,
+        mode: Mode,
+    },
+    Json {
+        writer: Rc<RefCell<Box<dyn Write>>>,
+        pretty: bool,
+    },
+}
+
+impl Output {
+    fn show_json<T>(&self, value: &T) -> Result<()>
+    where
+        T: Serialize,
+    {
+        match self {
+            Self::Driver { mode, driver: _ } => {
+                Err(anyhow!("Mode '{mode}' only supports output as JSON."))
+            }
+            Self::Json { writer, pretty } => {
+                let mut writer = writer.borrow_mut();
+                match pretty {
+                    true => serde_json::to_writer_pretty(&mut *writer, value)?,
+                    false => serde_json::to_writer(&mut *writer, value)?,
+                };
+                writeln!(writer)?;
+                Ok(())
+            }
+        }
+    }
+
+    fn warn(&self, warning: &impl Display) {
+        match self {
+            Output::Driver { driver, .. } => {
+                driver
+                    .borrow_mut()
+                    .write(&Arc::new(Item::from(Text::new_log(warning.to_string()))));
+            }
+            Output::Json { .. } => {
+                #[derive(Serialize)]
+                struct Warning {
+                    warning: String,
+                }
+                let warning = Warning {
+                    warning: warning.to_string(),
+                };
+                let _ = self.show_json(&warning);
+            }
         }
-        Ok(())
     }
-    fn show(&self, file_name: &Path) -> Result<()> {
-        let reader = File::open(file_name)?;
+}
+
+impl Show {
+    fn run(self) -> Result<()> {
+        let format = if let Some(format) = self.format {
+            format
+        } else if let Some(output_file) = &self.output_file {
+            ShowFormat::from_str(
+                output_file
+                    .extension()
+                    .unwrap_or(OsStr::new(""))
+                    .to_str()
+                    .unwrap_or(""),
+            )
+            .map_err(|_| {
+                anyhow!(
+                    "{}: no default output format for file name",
+                    output_file.display()
+                )
+            })?
+        } else {
+            ShowFormat::Json
+        };
+
+        let output = if let ShowFormat::Output(driver) = format {
+            let mut config = String::new();
+
+            #[derive(Serialize)]
+            struct DriverConfig {
+                driver: DriverType,
+            }
+            config.push_str(&toml::to_string_pretty(&DriverConfig { driver }).unwrap());
+
+            if let Some(file) = &self.output_file {
+                #[derive(Serialize)]
+                struct File<'a> {
+                    file: &'a Path,
+                }
+                let file = File {
+                    file: file.as_path(),
+                };
+                let toml_file = toml::to_string_pretty(&file).unwrap();
+                config.push_str(&toml_file);
+
+                for option in &self.output_options {
+                    writeln!(&mut config, "{option}").unwrap();
+                }
+            }
+
+            let config: Config = toml::from_str(&config)?;
+            Output::Driver {
+                mode: self.mode,
+                driver: Rc::new(RefCell::new(Box::new(<dyn Driver>::new(&config)?))),
+            }
+        } else {
+            Output::Json {
+                pretty: format == ShowFormat::Json,
+                writer: if let Some(output_file) = &self.output_file {
+                    Rc::new(RefCell::new(Box::new(File::create(output_file)?)))
+                } else {
+                    Rc::new(RefCell::new(Box::new(stdout())))
+                },
+            }
+        };
+
+        let reader = File::open(&self.input_file)?;
         let reader = BufReader::new(reader);
-        let mut reader = Reader::new(reader, Box::new(|warning| println!("{warning}")))?;
+        let mut reader = Reader::new(reader, Box::new(|warning| output.warn(&warning)))?;
 
         match self.mode {
             Mode::Identify => {
@@ -269,62 +393,69 @@ impl Show {
                 return Ok(());
             }
             Mode::Raw => {
-                self.show_json(reader.header())?;
+                output.show_json(reader.header())?;
                 for record in reader.records() {
-                    self.show_json(&record?)?;
+                    output.show_json(&record?)?;
                 }
                 for (_index, case) in (0..self.max_cases).zip(reader.cases()) {
-                    self.show_json(&case?)?;
+                    output.show_json(&case?)?;
                 }
             }
             Mode::Decoded => {
                 let records: Vec<Record> = reader.records().collect::<Result<Vec<_>, _>>()?;
                 let encoding = match self.encoding {
                     Some(encoding) => encoding,
-                    None => infer_encoding(&records, &mut |e| eprintln!("{e}"))?,
+                    None => infer_encoding(&records, &mut |e| output.warn(&e))?,
                 };
-                let mut decoder = Decoder::new(encoding, |e| eprintln!("{e}"));
+                let mut decoder = Decoder::new(encoding, |e| output.warn(&e));
                 for record in records {
-                    self.show_json(&record.decode(&mut decoder))?;
+                    output.show_json(&record.decode(&mut decoder))?;
                 }
             }
             Mode::Parsed => {
                 let records: Vec<Record> = reader.records().collect::<Result<Vec<_>, _>>()?;
                 let encoding = match self.encoding {
                     Some(encoding) => encoding,
-                    None => infer_encoding(&records, &mut |e| eprintln!("{e}"))?,
+                    None => infer_encoding(&records, &mut |e| output.warn(&e))?,
                 };
-                let mut decoder = Decoder::new(encoding, |e| eprintln!("{e}"));
+                let mut decoder = Decoder::new(encoding, |e| output.warn(&e));
                 let records = Records::from_raw(records, &mut decoder);
                 let (dictionary, metadata, cases) = records
                     .decode(
                         reader.header().clone().decode(&mut decoder),
                         reader.cases(),
                         encoding,
-                        |e| eprintln!("{e}"),
+                        |e| output.warn(&e),
                     )
                     .into_parts();
-                self.show_json(&dictionary)?;
-                self.show_json(&metadata)?;
-                for (_index, case) in (0..self.max_cases).zip(cases) {
-                    self.show_json(&case?)?;
+                match &output {
+                    Output::Driver { driver, mode: _ } => {
+                        driver
+                            .borrow_mut()
+                            .write(&Arc::new(Item::new(metadata.to_pivot_table())));
+                        driver
+                            .borrow_mut()
+                            .write(&Arc::new(Item::new(Details::Group(
+                                dictionary
+                                    .all_pivot_tables()
+                                    .into_iter()
+                                    .map(|pivot_table| Arc::new(Item::new(pivot_table)))
+                                    .collect(),
+                            ))));
+                    }
+                    Output::Json { .. } => {
+                        output.show_json(&dictionary)?;
+                        output.show_json(&metadata)?;
+                        for (_index, case) in (0..self.max_cases).zip(cases) {
+                            output.show_json(&case?)?;
+                        }
+                    }
                 }
             }
         }
 
         Ok(())
     }
-    fn show_json<T>(&self, value: &T) -> Result<()>
-    where
-        T: Serialize,
-    {
-        match self.format {
-            ShowFormat::Json => serde_json::to_writer_pretty(stdout(), value)?,
-            ShowFormat::Ndjson => serde_json::to_writer(stdout(), value)?,
-        };
-        println!();
-        Ok(())
-    }
 }
 
 #[derive(Subcommand, Clone, Debug)]
@@ -364,13 +495,52 @@ enum Mode {
     Parsed,
 }
 
-#[derive(Clone, Copy, Debug, Default, ValueEnum)]
+impl Mode {
+    fn as_str(&self) -> &'static str {
+        match self {
+            Mode::Identify => "identify",
+            Mode::Raw => "raw",
+            Mode::Decoded => "decoded",
+            Mode::Parsed => "parsed",
+        }
+    }
+}
+
+impl Display for Mode {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "{}", self.as_str())
+    }
+}
+
+#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize)]
+#[serde(rename_all = "snake_case")]
 enum ShowFormat {
     /// Pretty-printed JSON.
     #[default]
     Json,
     /// Newline-delimited JSON.
     Ndjson,
+    Output(DriverType),
+}
+
+#[derive(ThisError, Debug)]
+#[error("{0}: unknown format")]
+struct UnknownFormat(String);
+
+impl FromStr for ShowFormat {
+    type Err = UnknownFormat;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        if s.eq_ignore_ascii_case("json") {
+            Ok(Self::Json)
+        } else if s.eq_ignore_ascii_case("ndjson") {
+            Ok(Self::Ndjson)
+        } else if let Ok(driver_type) = DriverType::from_str(s, true) {
+            Ok(Self::Output(driver_type))
+        } else {
+            Err(UnknownFormat(String::from(s)))
+        }
+    }
 }
 
 fn main() -> Result<()> {
index c0931408eb52da46965845f9237976ac467afc7b..fbcabc06c0b8fb3de55c17aa2ed5ce116ce5b001 100644 (file)
@@ -31,7 +31,7 @@ use crate::output::{
         pager::{CairoPageStyle, CairoPager},
     },
     driver::Driver,
-    page::Setup,
+    page::PageSetup,
     pivot::{Color, Coord2, FontStyle},
     Item,
 };
@@ -41,18 +41,17 @@ use crate::output::pivot::Axis2;
 #[derive(Clone, Debug, Deserialize, Serialize)]
 pub struct CairoConfig {
     /// Output file name.
-    pub path: PathBuf,
+    pub file: PathBuf,
 
     /// Page setup.
-    #[serde(flatten)]
-    pub setup: Setup,
+    pub page_setup: Option<PageSetup>,
 }
 
 impl CairoConfig {
     pub fn new(path: impl AsRef<Path>) -> Self {
         Self {
-            path: path.as_ref().to_path_buf(),
-            setup: Setup::default(),
+            file: path.as_ref().to_path_buf(),
+            page_setup: None,
         }
     }
 }
@@ -70,16 +69,24 @@ impl CairoDriver {
             (inches * 72.0 * SCALE as f64).max(0.0).round() as usize
         }
 
-        let printable = config.setup.printable_size();
+        let default_page_setup;
+        let page_setup = match &config.page_setup {
+            Some(page_setup) => page_setup,
+            None => {
+                default_page_setup = PageSetup::default();
+                &default_page_setup
+            }
+        };
+        let printable = page_setup.printable_size();
         let page_style = CairoPageStyle {
             margins: EnumMap::from_fn(|axis| {
                 [
-                    scale(config.setup.margins[axis][0]),
-                    scale(config.setup.margins[axis][1]),
+                    scale(page_setup.margins[axis][0]),
+                    scale(page_setup.margins[axis][1]),
                 ]
             }),
-            headings: config.setup.headings.clone(),
-            initial_page_number: config.setup.initial_page_number,
+            headings: page_setup.headings.clone(),
+            initial_page_number: page_setup.initial_page_number,
         };
         let size = Coord2::new(scale(printable[Axis2::X]), scale(printable[Axis2::Y]));
         let font = FontStyle {
@@ -102,13 +109,13 @@ impl CairoDriver {
             font,
             fg: Color::BLACK,
             use_system_colors: false,
-            object_spacing: scale(config.setup.object_spacing),
+            object_spacing: scale(page_setup.object_spacing),
             font_resolution: 72.0,
         };
         let surface = PdfSurface::new(
-            config.setup.paper[Axis2::X] * 72.0,
-            config.setup.paper[Axis2::Y] * 72.0,
-            &config.path,
+            page_setup.paper[Axis2::X] * 72.0,
+            page_setup.paper[Axis2::Y] * 72.0,
+            &config.file,
         )?;
         Ok(Self {
             fsm_style: Arc::new(fsm_style),
index dba4cd2cf83e8755d792f01c7def45ec56fcf976..5690f1b711cd557e44620d84ea537cd6f3abb9ce 100644 (file)
@@ -31,7 +31,7 @@ use super::{driver::Driver, pivot::PivotTable, table::Table, Details, Item, Text
 
 #[derive(Clone, Debug, Serialize, Deserialize)]
 pub struct CsvConfig {
-    path: PathBuf,
+    file: PathBuf,
     #[serde(flatten)]
     options: CsvOptions,
 }
@@ -101,7 +101,7 @@ impl Display for CsvField<'_> {
 impl CsvDriver {
     pub fn new(config: &CsvConfig) -> std::io::Result<Self> {
         Ok(Self {
-            file: File::create(&config.path)?,
+            file: File::create(&config.file)?,
             options: config.options.clone(),
             n_items: 0,
         })
index eeddcfd89e1ecda1656790f8c2d37f8e4d39d839..9b272f6a05ceb5bdca383ca43ea19594b304131a 100644 (file)
 
 use std::{borrow::Cow, sync::Arc};
 
+use clap::ValueEnum;
 use serde::{Deserialize, Serialize};
 
 use crate::output::{
     cairo::{CairoConfig, CairoDriver},
     csv::{CsvConfig, CsvDriver},
     html::{HtmlConfig, HtmlDriver},
+    spv::{SpvConfig, SpvDriver},
     text::{TextConfig, TextDriver},
 };
 
-use super::{page::Setup, Item};
-
-#[derive(Clone, Debug, Serialize, Deserialize)]
-#[serde(tag = "driver", rename_all = "snake_case")]
-pub enum Config {
-    Text(TextConfig),
-    Pdf(CairoConfig),
-    Html(HtmlConfig),
-    Csv(CsvConfig),
-}
-
-impl dyn Driver {
-    fn new(config: &Config) -> anyhow::Result<Box<Self>> {
-        match config {
-            Config::Text(text_config) => Ok(Box::new(TextDriver::new(text_config)?)),
-            Config::Pdf(cairo_config) => Ok(Box::new(CairoDriver::new(cairo_config)?)),
-            Config::Html(html_config) => Ok(Box::new(HtmlDriver::new(html_config)?)),
-            Config::Csv(csv_config) => Ok(Box::new(CsvDriver::new(csv_config)?)),
-        }
-    }
-}
+use super::{page::PageSetup, Item};
 
 // An output driver.
 pub trait Driver {
@@ -54,7 +36,7 @@ pub trait Driver {
     fn write(&mut self, item: &Arc<Item>);
 
     /// Returns false if the driver doesn't support page setup.
-    fn setup(&mut self, page_setup: &Setup) -> bool {
+    fn setup(&mut self, page_setup: &PageSetup) -> bool {
         let _ = page_setup;
         false
     }
@@ -82,29 +64,85 @@ pub trait Driver {
     }
 }
 
-/*
-/// An abstract way for the output subsystem to create an output driver.
-trait DriverFactory {
-    /// The file extension, without the leading dot, e.g. "pdf".
-    fn extension(&self) ->  OsString;
+impl Driver for Box<dyn Driver> {
+    fn name(&self) -> Cow<'static, str> {
+        (&**self).name()
+    }
 
-    /// The default file name, including extension.
-    ///
-    /// If this is `-`, that implies that by default output will be directed to
-    /// stdout.
-    fn default_file_name(&self) -> PathBuf;
+    fn write(&mut self, item: &Arc<Item>) {
+        (&mut **self).write(item);
+    }
 
-    /// Creates a new output driver of this class.  `name` and `type` should be
-    /// passed directly to output_driver_init.
-    ///
-    /// It is up to the driver class to decide how to interpret `options`.  The
-    /// create function should delete pairs that it understands from `options`,
-    /// because the caller may issue errors about unknown options for any pairs
-    /// that remain.
-    fn create(&self, file_handle: (),
+    fn setup(&mut self, page_setup: &PageSetup) -> bool {
+        (&mut **self).setup(page_setup)
+    }
+
+    fn flush(&mut self) {
+        (&mut **self).flush();
+    }
+
+    fn handles_show(&self) -> bool {
+        (&**self).handles_show()
+    }
 
-                                     enum settings_output_devices type,
-                                     struct driver_options *);
+    fn handles_groups(&self) -> bool {
+        (&**self).handles_groups()
+    }
+}
 
+#[derive(Clone, Debug, Serialize, Deserialize)]
+#[serde(tag = "driver", rename_all = "snake_case")]
+pub enum Config {
+    Text(TextConfig),
+    Pdf(CairoConfig),
+    Html(HtmlConfig),
+    Csv(CsvConfig),
+    Spv(SpvConfig),
+}
+
+#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, ValueEnum)]
+#[serde(rename_all = "snake_case")]
+pub enum DriverType {
+    Text,
+    Pdf,
+    Html,
+    Csv,
+    Spv,
+}
+
+impl dyn Driver {
+    pub fn new(config: &Config) -> anyhow::Result<Box<Self>> {
+        match config {
+            Config::Text(text_config) => Ok(Box::new(TextDriver::new(text_config)?)),
+            Config::Pdf(cairo_config) => Ok(Box::new(CairoDriver::new(cairo_config)?)),
+            Config::Html(html_config) => Ok(Box::new(HtmlDriver::new(html_config)?)),
+            Config::Csv(csv_config) => Ok(Box::new(CsvDriver::new(csv_config)?)),
+            Config::Spv(spv_config) => Ok(Box::new(SpvDriver::new(spv_config)?)),
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use serde::Serialize;
+
+    use crate::output::driver::Config;
+
+    #[test]
+    fn toml() {
+        let config = r#"driver = "text"
+file = "filename.text"
+"#;
+        let toml: Config = toml::from_str(config).unwrap();
+        println!("{}", toml::to_string_pretty(&toml).unwrap());
+
+        #[derive(Serialize)]
+        struct Map<'a> {
+            file: &'a str,
+        }
+        println!(
+            "{}",
+            toml::to_string_pretty(&Map { file: "filename" }).unwrap()
+        );
+    }
 }
-*/
index 87015f6a7dd3039ba581b75dfa7c13ad57dd2ba8..4240b8d9f57a582d856d53b047a33cae487fdde6 100644 (file)
@@ -27,13 +27,21 @@ pub enum Orientation {
     Landscape,
 }
 
+/// Chart size.
 #[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "snake_case")]
 pub enum ChartSize {
+    /// Size specified in the chart itself.
     #[default]
     AsIs,
+
+    /// Full page.
     FullHeight,
+
+    /// Half-page.
     HalfHeight,
+
+    /// Quarter-page.
     QuarterHeight,
 }
 
@@ -56,7 +64,9 @@ impl Default for Paragraph {
 pub struct Heading(pub Vec<Paragraph>);
 
 #[derive(Clone, Debug, Deserialize, Serialize)]
-pub struct Setup {
+#[serde(default)]
+pub struct PageSetup {
+    /// Page number of first page.
     pub initial_page_number: i32,
 
     /// Paper size in inches.
@@ -65,18 +75,20 @@ pub struct Setup {
     /// Margin width in inches.
     pub margins: EnumMap<Axis2, [f64; 2]>,
 
+    /// Portrait or landscape.
     pub orientation: Orientation,
 
     /// Space between objects, in inches.
     pub object_spacing: f64,
 
+    /// Size of charts.
     pub chart_size: ChartSize,
 
     /// Header and footer.
     pub headings: [Heading; 2],
 }
 
-impl Default for Setup {
+impl Default for PageSetup {
     fn default() -> Self {
         Self {
             initial_page_number: 1,
@@ -90,7 +102,7 @@ impl Default for Setup {
     }
 }
 
-impl Setup {
+impl PageSetup {
     pub fn printable_size(&self) -> EnumMap<Axis2, f64> {
         EnumMap::from_fn(|axis| self.paper[axis] - self.margins[axis][0] - self.margins[axis][1])
     }
index e66ee0f1c0c595c792cfc47f12779ffb1f05562b..a69f82153205d79fba6c35e2b89964e95aaed1f7 100644 (file)
@@ -186,7 +186,7 @@ pub fn assert_rendering(name: &str, pivot_table: &PivotTable, expected: &str) {
 
     if let Some(dir) = std::env::var_os("PSPP_TEST_SPV_DIR") {
         let writer = File::create(Path::new(&dir).join(name).with_extension("spv")).unwrap();
-        SpvDriver::new(writer).write(&item);
+        SpvDriver::for_writer(writer).write(&item);
     }
 }
 
index 378a598e44ef6b35af5cf8cd8d9ddb3c90874366..325595345303d305a1dfd5aa952f6e286815956d 100644 (file)
@@ -18,8 +18,10 @@ use core::f64;
 use std::{
     borrow::Cow,
     fmt::Write as _,
+    fs::File,
     io::{Cursor, Seek, Write},
     iter::{repeat, repeat_n},
+    path::PathBuf,
     sync::Arc,
 };
 
@@ -31,6 +33,7 @@ use quick_xml::{
     writer::Writer as XmlWriter,
     ElementWriter,
 };
+use serde::{Deserialize, Serialize};
 use smallstr::SmallString;
 use zip::{result::ZipResult, write::SimpleFileOptions, ZipWriter};
 
@@ -38,6 +41,7 @@ use crate::{
     format::{Format, Type},
     output::{
         driver::Driver,
+        page::{Heading, PageSetup},
         pivot::{
             Area, AreaStyle, Axis2, Axis3, Border, BorderStyle, BoxBorder, Category, CellStyle,
             Color, Dimension, FontStyle, Footnote, FootnoteMarkerPosition, FootnoteMarkerType,
@@ -60,6 +64,15 @@ fn output_viewer_name(heading_id: u64, is_heading: bool) -> String {
     )
 }
 
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct SpvConfig {
+    /// Output file name.
+    pub file: PathBuf,
+
+    /// Page setup.
+    pub page_setup: Option<PageSetup>,
+}
+
 pub struct SpvDriver<W>
 where
     W: Write + Seek,
@@ -68,13 +81,24 @@ where
     needs_page_break: bool,
     next_table_id: u64,
     next_heading_id: u64,
+    page_setup: Option<PageSetup>,
+}
+
+impl SpvDriver<File> {
+    pub fn new(config: &SpvConfig) -> std::io::Result<Self> {
+        let mut driver = Self::for_writer(File::create(&config.file)?);
+        if let Some(page_setup) = &config.page_setup {
+            driver = driver.with_page_setup(page_setup.clone());
+        }
+        Ok(driver)
+    }
 }
 
 impl<W> SpvDriver<W>
 where
     W: Write + Seek,
 {
-    pub fn new(writer: W) -> Self {
+    pub fn for_writer(writer: W) -> Self {
         let mut writer = ZipWriter::new(writer);
         writer
             .start_file("META-INF/MANIFEST.MF", SimpleFileOptions::default())
@@ -85,6 +109,14 @@ where
             needs_page_break: false,
             next_table_id: 1,
             next_heading_id: 1,
+            page_setup: None,
+        }
+    }
+
+    pub fn with_page_setup(self, page_setup: PageSetup) -> Self {
+        Self {
+            page_setup: Some(page_setup),
+            ..self
         }
     }
 
@@ -533,7 +565,9 @@ where
             .write_inner_content(|w| {
                 w.create_element("label")
                     .write_text_content(BytesText::new("Output"))?;
-                // XXX page setup
+                if let Some(page_setup) = self.page_setup.take() {
+                    write_page_setup(&page_setup, w)?;
+                }
                 self.write_item(item, w);
                 Ok(())
             })
@@ -552,6 +586,80 @@ where
     }
 }
 
+fn write_page_setup<X>(page_setup: &PageSetup, writer: &mut XmlWriter<X>) -> std::io::Result<()>
+where
+    X: Write,
+{
+    fn inches<'a>(x: f64) -> Cow<'a, str> {
+        Cow::from(format!("{:.2}in", x))
+    }
+
+    writer
+        .create_element("vps:pageSetup")
+        .with_attribute((
+            "initial-page-number",
+            Cow::from(format!("{}", page_setup.initial_page_number)),
+        ))
+        .with_attribute((
+            "chart-size",
+            match page_setup.chart_size {
+                super::page::ChartSize::AsIs => "as-is",
+                super::page::ChartSize::FullHeight => "full-height",
+                super::page::ChartSize::HalfHeight => "half-height",
+                super::page::ChartSize::QuarterHeight => "quarter-height",
+            },
+        ))
+        .with_attribute(("margin-left", inches(page_setup.margins[Axis2::X][0])))
+        .with_attribute(("margin-right", inches(page_setup.margins[Axis2::X][1])))
+        .with_attribute(("margin-top", inches(page_setup.margins[Axis2::Y][0])))
+        .with_attribute(("margin-bottom", inches(page_setup.margins[Axis2::Y][1])))
+        .with_attribute(("paper-height", inches(page_setup.paper[Axis2::Y])))
+        .with_attribute(("paper-width", inches(page_setup.paper[Axis2::X])))
+        .with_attribute((
+            "reference-orientation",
+            match page_setup.orientation {
+                crate::output::page::Orientation::Portrait => "portrait",
+                crate::output::page::Orientation::Landscape => "landscape",
+            },
+        ))
+        .with_attribute((
+            "space-after",
+            Cow::from(format!("{:.1}pt", page_setup.object_spacing * 72.0)),
+        ))
+        .write_inner_content(|w| {
+            write_page_heading(&page_setup.headings[0], "vps:pageHeader", w)?;
+            write_page_heading(&page_setup.headings[1], "vps:pageFooter", w)?;
+            Ok(())
+        })?;
+    Ok(())
+}
+
+fn write_page_heading<X>(
+    heading: &Heading,
+    name: &str,
+    writer: &mut XmlWriter<X>,
+) -> std::io::Result<()>
+where
+    X: Write,
+{
+    let element = writer.create_element(name);
+    if !heading.0.is_empty() {
+        element.write_inner_content(|w| {
+            w.create_element("vps:pageParagraph")
+                .write_inner_content(|w| {
+                    for paragraph in &heading.0 {
+                        w.create_element("vtx:text")
+                            .with_attribute(("text", "title"))
+                            .write_text_content(BytesText::new(&paragraph.markup))?;
+                    }
+                    Ok(())
+                })?;
+            Ok(())
+        })?;
+    }
+    Ok(())
+}
+
 fn maybe_with_attribute<'a, 'b, W, I>(
     element: ElementWriter<'a, W>,
     attr: Option<I>,
index 5ef2f670908fc3dea7952f5575705cbf60ce09b5..61b49a357c507b793544a4f1d1bf731f3a17b232 100644 (file)
@@ -67,28 +67,19 @@ pub struct TextConfig {
     options: TextRendererOptions,
 }
 
-#[derive(Clone, Debug, Deserialize, Serialize)]
+#[derive(Clone, Debug, Default, Deserialize, Serialize)]
+#[serde(default)]
 pub struct TextRendererOptions {
     /// Enable bold and underline in output?
     pub emphasis: bool,
 
     /// Page width.
-    pub width: usize,
+    pub width: Option<usize>,
 
     /// ASCII or Unicode
     pub boxes: Boxes,
 }
 
-impl Default for TextRendererOptions {
-    fn default() -> Self {
-        Self {
-            emphasis: false,
-            width: usize::MAX,
-            boxes: Boxes::default(),
-        }
-    }
-}
-
 pub struct TextRenderer {
     /// Enable bold and underline in output?
     emphasis: bool,
@@ -114,14 +105,15 @@ impl Default for TextRenderer {
 
 impl TextRenderer {
     pub fn new(config: &TextRendererOptions) -> Self {
+        let width = config.width.unwrap_or(usize::MAX);
         Self {
             emphasis: config.emphasis,
-            width: config.width,
+            width,
             min_hbreak: 20,
             box_chars: config.boxes.box_chars(),
             n_objects: 0,
             params: Params {
-                size: Coord2::new(config.width, usize::MAX),
+                size: Coord2::new(width, usize::MAX),
                 font_size: EnumMap::from_fn(|_| 1),
                 line_widths: EnumMap::from_fn(|stroke| if stroke == Stroke::None { 0 } else { 1 }),
                 px_size: None,
index 7764b21c995b000bfe3c8e3c52b3dcec5643f9c6..0e7f87c5fb0d9eca7f005c11aca61799f4f95c58 100644 (file)
@@ -34,7 +34,7 @@ use crate::{
     format::{Error as FormatError, Format, UncheckedFormat},
     hexfloat::HexFloat,
     identifier::{Error as IdError, Identifier},
-    output::pivot::{Group, Value},
+    output::pivot::{Axis3, Dimension, Group, PivotTable, Value},
     sys::{
         raw::{
             self, infer_encoding,
@@ -1471,6 +1471,16 @@ impl Metadata {
         (group, values)
     }
 
+    pub fn to_pivot_table(&self) -> PivotTable {
+        let (group, data) = self.to_pivot_rows();
+        PivotTable::new([(Axis3::Y, Dimension::new(group))]).with_data(
+            data.into_iter()
+                .enumerate()
+                .filter(|(_row, value)| !value.is_empty())
+                .map(|(row, value)| ([row], value)),
+        )
+    }
+
     fn decode(header: &FileHeader<String>, headers: &Records, mut warn: impl FnMut(Error)) -> Self {
         let header = &header;
         let creation_date = NaiveDate::parse_from_str(&header.creation_date, "%e %b %y")
index 5ddf745f1790425e7f3d8b26ebc44d563a53b749..dc19b6499fc82e9ca6502ffd365b1f0bb183e66e 100644 (file)
@@ -23,6 +23,7 @@ use std::{
 
 use binrw::Endian;
 use encoding_rs::UTF_8;
+use itertools::Itertools;
 
 use crate::{
     crypto::EncryptedFile,
@@ -573,102 +574,104 @@ fn encrypted_file_without_password() {
 
 /// Tests the most basic kind of writing a system file, just writing a few
 /// numeric variables and cases.
-#[test]
-fn write_numeric() {
-    for (compression, compression_string) in [
-        (None, "uncompressed"),
-        (Some(Compression::Simple), "simple"),
-        (Some(Compression::ZLib), "zlib"),
+fn write_numeric(compression: Option<Compression>, compression_string: &str) {
+    let mut dictionary = Dictionary::new(UTF_8);
+    for i in 0..4 {
+        let name = Identifier::new(format!("variable{i}")).unwrap();
+        dictionary
+            .add_var(Variable::new(name, VarWidth::Numeric, UTF_8))
+            .unwrap();
+    }
+    let mut cases = WriteOptions::reproducible(compression)
+        .write_writer(&dictionary, Cursor::new(Vec::new()))
+        .unwrap();
+    for case in [
+        [1, 1, 1, 2],
+        [1, 1, 2, 30],
+        [1, 2, 1, 8],
+        [1, 2, 2, 20],
+        [2, 1, 1, 2],
+        [2, 1, 2, 22],
+        [2, 2, 1, 1],
+        [2, 2, 2, 3],
     ] {
-        let mut dictionary = Dictionary::new(UTF_8);
-        for i in 0..4 {
-            let name = Identifier::new(format!("variable{i}")).unwrap();
-            dictionary
-                .add_var(Variable::new(name, VarWidth::Numeric, UTF_8))
-                .unwrap();
-        }
-        let mut cases = WriteOptions::reproducible(compression)
-            .write_writer(&dictionary, Cursor::new(Vec::new()))
+        cases
+            .write_case(
+                case.into_iter()
+                    .map(|number| Datum::<&str>::Number(Some(number as f64))),
+            )
             .unwrap();
-        for case in [
-            [1, 1, 1, 2],
-            [1, 1, 2, 30],
-            [1, 2, 1, 8],
-            [1, 2, 2, 20],
-            [2, 1, 1, 2],
-            [2, 1, 2, 22],
-            [2, 2, 1, 1],
-            [2, 2, 2, 3],
-        ] {
-            cases
-                .write_case(
-                    case.into_iter()
-                        .map(|number| Datum::<&str>::Number(Some(number as f64))),
-                )
-                .unwrap();
-        }
-        let sysfile = cases.finish().unwrap().unwrap().into_inner();
-        let expected_filename = PathBuf::from(&format!(
-            "src/sys/testdata/write-numeric-{compression_string}.expected"
-        ));
-        let expected = String::from_utf8(std::fs::read(&expected_filename).unwrap()).unwrap();
-        test_sysfile(Cursor::new(sysfile), &expected, &expected_filename);
     }
+    let sysfile = cases.finish().unwrap().unwrap().into_inner();
+    let expected_filename = PathBuf::from(&format!(
+        "src/sys/testdata/write-numeric-{compression_string}.expected"
+    ));
+    let expected = String::from_utf8(std::fs::read(&expected_filename).unwrap()).unwrap();
+    test_sysfile(Cursor::new(sysfile), &expected, &expected_filename);
 }
 
-/// Tests writing string data.
 #[test]
-fn write_string_data() {
-    for (compression, compression_string) in [
-        (None, "uncompressed"),
-        (Some(Compression::Simple), "simple"),
-        (Some(Compression::ZLib), "zlib"),
-    ] {
-        let mut dictionary = Dictionary::new(UTF_8);
-        dictionary
-            .add_var(Variable::new(
-                Identifier::new("s1").unwrap(),
-                VarWidth::String(1),
-                UTF_8,
-            ))
-            .unwrap();
+fn write_numeric_uncompressed() {
+    write_numeric(None, "uncompressed");
+}
 
-        dictionary
-            .add_var(Variable::new(
-                Identifier::new("s2").unwrap(),
-                VarWidth::String(2),
-                UTF_8,
-            ))
-            .unwrap();
+#[test]
+fn write_numeric_simple() {
+    write_numeric(Some(Compression::Simple), "simple");
+}
 
-        dictionary
-            .add_var(Variable::new(
-                Identifier::new("s3").unwrap(),
-                VarWidth::String(3),
-                UTF_8,
-            ))
-            .unwrap();
+#[test]
+fn write_numeric_zlib() {
+    write_numeric(Some(Compression::ZLib), "zlib");
+}
 
-        dictionary
-            .add_var(Variable::new(
-                Identifier::new("s4").unwrap(),
-                VarWidth::String(9),
-                UTF_8,
-            ))
-            .unwrap();
+/// Tests writing string data.
+fn write_string(compression: Option<Compression>, compression_string: &str) {
+    let mut dictionary = Dictionary::new(UTF_8);
+    dictionary
+        .add_var(Variable::new(
+            Identifier::new("s1").unwrap(),
+            VarWidth::String(1),
+            UTF_8,
+        ))
+        .unwrap();
 
-        dictionary
-            .add_var(Variable::new(
-                Identifier::new("s566").unwrap(),
-                VarWidth::String(566),
-                UTF_8,
-            ))
-            .unwrap();
+    dictionary
+        .add_var(Variable::new(
+            Identifier::new("s2").unwrap(),
+            VarWidth::String(2),
+            UTF_8,
+        ))
+        .unwrap();
 
-        let mut cases = WriteOptions::reproducible(compression)
-            .write_writer(&dictionary, Cursor::new(Vec::new()))
-            .unwrap();
-        for case in [
+    dictionary
+        .add_var(Variable::new(
+            Identifier::new("s3").unwrap(),
+            VarWidth::String(3),
+            UTF_8,
+        ))
+        .unwrap();
+
+    dictionary
+        .add_var(Variable::new(
+            Identifier::new("s4").unwrap(),
+            VarWidth::String(9),
+            UTF_8,
+        ))
+        .unwrap();
+
+    dictionary
+        .add_var(Variable::new(
+            Identifier::new("s566").unwrap(),
+            VarWidth::String(566),
+            UTF_8,
+        ))
+        .unwrap();
+
+    let mut cases = WriteOptions::reproducible(compression)
+        .write_writer(&dictionary, Cursor::new(Vec::new()))
+        .unwrap();
+    for case in [
             ["1", "1", "1", "xyzzyquux", "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\nabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\nabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\nabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\nabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\nabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\nabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\nabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\nabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\n"],
             ["1", "2", "1", "8", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"],
         ] {
@@ -676,13 +679,27 @@ fn write_string_data() {
                 .write_case(case.into_iter().map(|s| Datum::String(s)))
                 .unwrap();
         }
-        let sysfile = cases.finish().unwrap().unwrap().into_inner();
-        let expected_filename = PathBuf::from(&format!(
-            "src/sys/testdata/write-string-{compression_string}.expected"
-        ));
-        let expected = String::from_utf8(std::fs::read(&expected_filename).unwrap()).unwrap();
-        test_sysfile(Cursor::new(sysfile), &expected, &expected_filename);
-    }
+    let sysfile = cases.finish().unwrap().unwrap().into_inner();
+    let expected_filename = PathBuf::from(&format!(
+        "src/sys/testdata/write-string-{compression_string}.expected"
+    ));
+    let expected = String::from_utf8(std::fs::read(&expected_filename).unwrap()).unwrap();
+    test_sysfile(Cursor::new(sysfile), &expected, &expected_filename);
+}
+
+#[test]
+fn write_string_uncompressed() {
+    write_string(None, "uncompressed");
+}
+
+#[test]
+fn write_string_simple() {
+    write_string(Some(Compression::Simple), "simple");
+}
+
+#[test]
+fn write_string_zlib() {
+    write_string(Some(Compression::ZLib), "zlib");
 }
 
 fn test_raw_sysfile(name: &str) {
@@ -736,43 +753,15 @@ where
     let output = match ReadOptions::new(|warning| warnings.push(warning)).open_reader(sysfile) {
         Ok(system_file) => {
             let (dictionary, metadata, cases) = system_file.into_parts();
-            let (group, data) = metadata.to_pivot_rows();
-            let metadata_table = PivotTable::new([(Axis3::Y, Dimension::new(group))]).with_data(
-                data.into_iter()
-                    .enumerate()
-                    .filter(|(_row, value)| !value.is_empty())
-                    .map(|(row, value)| ([row], value)),
-            );
-            let (group, data) = dictionary.to_pivot_rows();
-            let dictionary_table = PivotTable::new([(Axis3::Y, Dimension::new(group))]).with_data(
-                data.into_iter()
-                    .enumerate()
-                    .filter(|(_row, value)| !value.is_empty())
-                    .map(|(row, value)| ([row], value)),
-            );
+
             let mut output = Vec::new();
             output.extend(
                 warnings
                     .into_iter()
-                    .map(|warning| Arc::new(Item::from(Text::new_log(warning.to_string())))),
+                    .map(|warning| Item::from(Text::new_log(warning.to_string()))),
             );
-            output.push(Arc::new(metadata_table.into()));
-            output.push(Arc::new(dictionary_table.into()));
-            output.push(Arc::new(
-                dictionary.output_variables().to_pivot_table().into(),
-            ));
-            if let Some(pt) = dictionary.output_value_labels().to_pivot_table() {
-                output.push(Arc::new(pt.into()));
-            }
-            if let Some(pt) = dictionary.output_mrsets().to_pivot_table() {
-                output.push(Arc::new(pt.into()));
-            }
-            if let Some(pt) = dictionary.output_attributes().to_pivot_table() {
-                output.push(Arc::new(pt.into()));
-            }
-            if let Some(pt) = dictionary.output_variable_sets().to_pivot_table() {
-                output.push(Arc::new(pt.into()));
-            }
+            output.push(metadata.to_pivot_table().into());
+            output.extend(dictionary.all_pivot_tables().into_iter().map_into());
             let variables =
                 Group::new("Variable").with_multiple(dictionary.variables.iter().map(|var| &**var));
             let mut case_numbers = Group::new("Case").with_label_shown();
@@ -789,7 +778,7 @@ where
                         );
                     }
                     Err(error) => {
-                        output.push(Arc::new(Item::from(Text::new_log(error.to_string()))));
+                        output.push(Item::from(Text::new_log(error.to_string())));
                     }
                 }
             }
@@ -803,9 +792,9 @@ where
                         pt.insert(&[column_number, row_number], datum);
                     }
                 }
-                output.push(Arc::new(pt.into()));
+                output.push(pt.into());
             }
-            Item::new(Details::Group(output))
+            Item::new(Details::Group(output.into_iter().map(Arc::new).collect()))
         }
         Err(error) => Item::new(Details::Text(Box::new(Text::new_log(error.to_string())))),
     };
index f0daafed95ae08d56427d859132c80feb6dcd025..542ee2461f6152d49ea6a033ea784cdc6db74c75 100644 (file)
@@ -13,6 +13,7 @@
 │Variables│                       3│
 ╰─────────┴────────────────────────╯
 
+                                                    Variables
 ╭──────────────┬────────┬─────┬─────────────────┬──────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │              │Position│Label│Measurement Level│ Role │Width│Alignment│Print Format│Write Format│Missing Values│
 ├──────────────┼────────┼─────┼─────────────────┼──────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index d2542a1194fb406f8362657f8b4c2edc91876ac4..b09ef9aa98954baaf51502402f48716822e4ebc8 100644 (file)
@@ -13,6 +13,7 @@ Warning at file offsets 0xe0 to 0x100: In extension record: floating point recor
 │Variables│1│
 ╰─────────┴─╯
 
+                                               Variables
 ╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │    │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 0587a131f8d6098eb12e54a3157d4dac1a80ab75..c6e81da6886691222d0f37896466552322c0565e 100644 (file)
@@ -13,6 +13,7 @@ Warning at file offsets 0xe0 to 0x104: In extension record: integer record has b
 │Variables│1│
 ╰─────────┴─╯
 
+                                               Variables
 ╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │    │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index e66463ad126a12a3b6c6709ed04fd2496b99bb99..ca0229447c4a1710b29252c91d2a188cc38f4d45 100644 (file)
@@ -14,6 +14,7 @@ Integer format indicated by system file (3) differs from expected ({endian}).
 │Variables│1│
 ╰─────────┴─╯
 
+                                               Variables
 ╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │    │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index d25902e58460ff60ec88e2a5e1048e1f193f0800..f04025570532f9a830be688a15816436e0a8730c 100644 (file)
@@ -14,6 +14,7 @@ Floating-point representation indicated by system file (2) differs from expected
 │Variables│1│
 ╰─────────┴─╯
 
+                                               Variables
 ╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │    │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 0f9a086b6ce55705046dbf9a58d2e3f96cd62093..d276b3a58c88b4b4ccc5cd501e91161900df2fb3 100644 (file)
@@ -15,6 +15,7 @@ Ignoring long string value label for numeric variable NUM1.
 │Variables│2│
 ╰─────────┴─╯
 
+                                               Variables
 ╭─────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │     │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├─────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 13d3fb715e0da9080c0a21c604dcda454dca6522..673e178210a6abd50991d590e278eaaabf25625f 100644 (file)
@@ -13,6 +13,7 @@ Warning at file offsets 0xe0 to 0xe5: In long variable name record: Missing `=`
 │Variables│1│
 ╰─────────┴─╯
 
+                                                 Variables
 ╭────────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │        │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 31dafee3ba844c4cb4289371ea84b53f0cfecdb7..7d391c03235ab1402d05c2656025800d2df8d9b3 100644 (file)
@@ -17,6 +17,7 @@ Variable with short name NUM1 listed in very long string record with width 256 r
 │Variables│1│
 ╰─────────┴─╯
 
+                                               Variables
 ╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │    │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 75d706ab957fe742c2b92d69eb7db7f7fa2e4031..6ba24735bd7410e09deb534e80a9b058682f3540 100644 (file)
@@ -15,6 +15,7 @@ Variable with short name STR1 listed in very long string record with width 256 h
 │Variables│2│
 ╰─────────┴─╯
 
+                                                Variables
 ╭──────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │      │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├──────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 624955c00dfdefd5158f358735970ea1f434acd2..1cf5d48e0fb75d77d59cf33650102bfa9c52485a 100644 (file)
@@ -12,6 +12,7 @@
 │Variables│                       5│
 ╰─────────┴────────────────────────╯
 
+                                               Variables
 ╭─────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │     │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├─────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 21cd5347021fe6e7c67ea85b7d05038289aa3631..372a7f965447bb59addf2e756431c408244d0c8b 100644 (file)
@@ -14,6 +14,7 @@ Warning at file offsets 0x54 to 0x5c: In file header: Compression bias is 50 ins
 │Variables│                       5│
 ╰─────────┴────────────────────────╯
 
+                                               Variables
 ╭─────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │     │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├─────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 96d6e5ac76dc198b37f84a170f9d93c3e4693da1..9d6989cdf77b9adaae594535c3a3fd250bc5e4a0 100644 (file)
@@ -12,6 +12,7 @@
 │Variables│                       5│
 ╰─────────┴────────────────────────╯
 
+                                               Variables
 ╭─────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │     │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├─────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 42cc6e4c04b7da8f0312791b00bac8d569e65af6..762e20406943a7fb017b65701be94dc5618e6af2 100644 (file)
@@ -17,6 +17,7 @@
 │         │Last line of documents                                 │
 ╰─────────┴───────────────────────────────────────────────────────╯
 
+                                               Variables
 ╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │    │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 3a70ef81caf256ac50f07e0f992109d53aae349c..05fe2754acfdada0fe84925ddb448e54da23a70b 100644 (file)
@@ -15,6 +15,7 @@ Warning at file offsets 0x10e to 0x12d: In file or variable attribute record: Du
 │Variables│1│
 ╰─────────┴─╯
 
+                                                 Variables
 ╭────────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │        │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 6f17a31f67543b1061e813fa9e7f97f6802403ac..a169e66ead137341b1c770e707c13510adb979a2 100644 (file)
@@ -19,6 +19,7 @@ Duplicate long variable name LONGVARIABLENAME.
 │Variables│4│
 ╰─────────┴─╯
 
+                                                     Variables
 ╭────────────────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │                │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────────────────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index e6416d44f3a5f90bba8580dcd447098fa91fc28b..5552bcc73c189560fd8578e278bc08313747cdfd 100644 (file)
@@ -15,6 +15,7 @@ NUM1 has duplicate value labels for the following value(s): 1
 │Variables│2│
 ╰─────────┴─╯
 
+                                               Variables
 ╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │    │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index ac64a478c389aec5bc7a7aadd9ae197314ad3851..581bcf61d9e4c2d886b268f6840134480a53c398 100644 (file)
@@ -13,6 +13,7 @@ Renaming variable with duplicate name VAR1 to VAR001.
 │Variables│2│
 ╰─────────┴─╯
 
+                                                Variables
 ╭──────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │      │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├──────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 47d2fac4a43f7362d0fadb63008aa8799a8bd66a..0bf233ca67a84146a3c9e8682de67579ee5c03dc 100644 (file)
@@ -13,6 +13,7 @@
 │Variables│                       1│
 ╰─────────┴────────────────────────╯
 
+                                               Variables
 ╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │    │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 63ef54fde924242c6fde3e55e22d5dc4c4bde96c..f5d25a70ddb18c6bd1762eeb67ba41d63a6329f9 100644 (file)
@@ -16,6 +16,7 @@
 │Variables│             4│
 ╰─────────┴──────────────╯
 
+                                             Variables
 ╭─┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├─┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 41eca0a2663c3807765aef485e5579154eacd29f..70c4d64598815702848bfb5ac6d50b99f8720c2c 100644 (file)
@@ -11,6 +11,7 @@
 │Variables│2│
 ╰─────────┴─╯
 
+                                               Variables
 ╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │    │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index ea2d86612b86828fe190ce6f8fe08102fa1904bc..8b1faeb1aaeda98a84e36b887c6ce84e3a33230c 100644 (file)
@@ -18,6 +18,7 @@ This system file does not indicate its own character encoding.  For best results
 │Variables│4│
 ╰─────────┴─╯
 
+                                                 Variables
 ╭────────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │        │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 654448b13180e8d35decd970880fc6fe3954bc8b..086d49847e99097e4733002af5dfcf6f456c3ba5 100644 (file)
@@ -25,6 +25,7 @@ Invalid long string missing value for 7-byte string variable STR4.
 │Variables│                             5│
 ╰─────────┴──────────────────────────────╯
 
+                                                         Variables
 ╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────────────────────────╮
 │    │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│          Missing Values          │
 ├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────────────────────────┤
index 9969410ab101b562e3c15f3d543e76a52b73916f..05d55df674b9bc8c1271979fa82c167c33f09bbf 100644 (file)
@@ -27,6 +27,7 @@ Substituting A4 for invalid write format on variable STR2.  String variable with
 │Variables│4│
 ╰─────────┴─╯
 
+                                               Variables
 ╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │    │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 9ac792f6ba37e5e4091a9cde2472bf63a36595b8..ee97fa67064089c9ca66b123bd3163af5c3abcdf 100644 (file)
@@ -19,6 +19,7 @@
 │Variables│4│
 ╰─────────┴─╯
 
+                                                Variables
 ╭──────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │      │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├──────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 23b123429aef79e3d11327ae8cef74a91da131c8..88dde68c5235909750f82f1b0f214b2e8e77d8dc 100644 (file)
@@ -13,6 +13,7 @@
 │Variables│                       7│
 ╰─────────┴────────────────────────╯
 
+                                                     Variables
 ╭─────────────────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │                 │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├─────────────────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 141a5a0bc73fa00ddfc0467a1d09c3b075312ccd..90adcda9974495222add83432c6bdb5b1c502df8 100644 (file)
@@ -15,6 +15,7 @@ Warning at file offsets 0xf6 to 0x109: In file or variable attribute record: Att
 │Variables│1│
 ╰─────────┴─╯
 
+                                                 Variables
 ╭────────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │        │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index ca1145046a6f1372a4ece39d86c1b5fb8e32050f..7aef1fdfece7f31dafc99257c83e20eec2feb579 100644 (file)
@@ -13,6 +13,7 @@ Multiple response set $a has only one variable.
 │Variables│1│
 ╰─────────┴─╯
 
+                                               Variables
 ╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │    │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 3bb98b0f40806e3ae10322ad722444b931ac5f25..6630473a187a61ffbac9b916ce61964d05a7e29e 100644 (file)
@@ -13,6 +13,7 @@ Variable index 0 is a 10-byte string that should be followed by long string cont
 │Variables│2│
 ╰─────────┴─╯
 
+                                               Variables
 ╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │    │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index b9a1e3a4c326eff4ebd772de15dc3b3ba14b0686..4c86e6f7caf31b21f18ebe09feb9147ad8f98ed9 100644 (file)
@@ -13,6 +13,7 @@ Multiple response set $a contains both string and numeric variables.
 │Variables│2│
 ╰─────────┴─╯
 
+                                               Variables
 ╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │    │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index cbe0398bf4d1909205e6754e1083216db4f2a3da..16d81d3cc7243ab93ea5e00363299b04d027e9b8 100644 (file)
@@ -11,6 +11,7 @@
 │Variables│2│
 ╰─────────┴─╯
 
+                                               Variables
 ╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │    │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 414d01908c1c7c72b3ec6f9ced8e51c67f0865e9..2f4533163742e843bf4ef14d3a5a021ba33d8526 100644 (file)
@@ -13,6 +13,7 @@
 │         │One line of documents│
 ╰─────────┴─────────────────────╯
 
+                                               Variables
 ╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │    │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 47c8a9b46cd9cb3360a031d4a4446bd451cac4a7..07b7cf28395cddefadee4bb346a39a73f7e55fcf 100644 (file)
@@ -13,6 +13,7 @@
 │Variables│                      16│
 ╰─────────┴────────────────────────╯
 
+                                              Variables
 ╭──┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │  │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├──┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 9b849999e5b411702ff7b99627bac051db98d339..e418bef4cc992a9af42c9997b6febc8a1a319ebd 100644 (file)
@@ -13,6 +13,7 @@ Warning at file offsets 0xe0 to 0xe5: In multiple response set record: Syntax er
 │Variables│1│
 ╰─────────┴─╯
 
+                                               Variables
 ╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │    │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 6793e1b229cab6bfbfc7bce2d7470f6abcdd21a6..9635997a71bfc0374b3c78c3efef769c8964f353 100644 (file)
@@ -17,6 +17,7 @@ Invalid multiple response set name.  Multiple response set name "e" does not beg
 │Variables│                      16│
 ╰─────────┴────────────────────────╯
 
+                                              Variables
 ╭──┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │  │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├──┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 8fb97dbb0c2860892ff36e497c0e1a3376db4ac7..9d0a063f518274f3b906c1919948c94e38385bf7 100644 (file)
@@ -13,6 +13,7 @@ Warning at file offsets 0xe0 to 0xe9: In multiple response set record: Syntax er
 │Variables│1│
 ╰─────────┴─╯
 
+                                               Variables
 ╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │    │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index f6e6e03234ce94db4829dac38dad51a50011c494..8cbd13cc55ae1108d888bc16084c322d508f3f44 100644 (file)
@@ -13,6 +13,7 @@ Warning at file offsets 0xe0 to 0xe6: In multiple response set record: Syntax er
 │Variables│1│
 ╰─────────┴─╯
 
+                                               Variables
 ╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │    │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index e5a0854dc6be78fedfdd72fb9aad8f94bd395ed2..a43645718e5c3b71ebc2ebb0aed69e428020de8c 100644 (file)
@@ -15,6 +15,7 @@ Multiple response set $a has only one variable.
 │Variables│1│
 ╰─────────┴─╯
 
+                                               Variables
 ╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │    │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index fe138f630bfce43212e02ea7fae8dcab88108a56..9212037f06b9fd01d9f2ae5ba6f602f0b663a418 100644 (file)
@@ -13,6 +13,7 @@ Warning at file offsets 0xe0 to 0xe5: In multiple response set record: Invalid m
 │Variables│1│
 ╰─────────┴─╯
 
+                                               Variables
 ╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │    │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index d4c8ec03cb9c0458095c28363c14d05c58b0bdd2..a4535ccd209792d1217cd0ab2d8c517db408b49d 100644 (file)
@@ -13,6 +13,7 @@ Warning at file offsets 0xe0 to 0xec: In multiple response set record: Syntax er
 │Variables│1│
 ╰─────────┴─╯
 
+                                               Variables
 ╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │    │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 5c60f29d73e0e3a8b2b1bfd352c1814e23ee315b..beca7dd9f0b579bc865e45185a53c519ea3adcc5 100644 (file)
@@ -13,6 +13,7 @@ Warning at file offsets 0xe0 to 0xe5: In multiple response set record: Syntax er
 │Variables│1│
 ╰─────────┴─╯
 
+                                               Variables
 ╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │    │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 05d8d77537608a2b2243369c20c1cf44a00e59a0..bdaee73a02e0b289eb0bab14ba6f1debdb675744 100644 (file)
@@ -13,6 +13,7 @@ Warning at file offsets 0xe0 to 0xea: In multiple response set record: Syntax er
 │Variables│1│
 ╰─────────┴─╯
 
+                                               Variables
 ╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │    │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index fe138f630bfce43212e02ea7fae8dcab88108a56..9212037f06b9fd01d9f2ae5ba6f602f0b663a418 100644 (file)
@@ -13,6 +13,7 @@ Warning at file offsets 0xe0 to 0xe5: In multiple response set record: Invalid m
 │Variables│1│
 ╰─────────┴─╯
 
+                                               Variables
 ╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │    │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 4b0d62e2856b20e53e5caaddcf8067b90d88923a..c71d6c89492ef67863f44434d1c96adf0d0589e1 100644 (file)
@@ -13,6 +13,7 @@ Warning at file offsets 0xe0 to 0xe6: In multiple response set record: Invalid m
 │Variables│1│
 ╰─────────┴─╯
 
+                                               Variables
 ╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │    │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index b10e8d94b3639a7ad77dded8b95a84e3048b8e5a..3580af6a4b2dbddf8672ced9731e5eae688b1b7b 100644 (file)
@@ -12,3 +12,4 @@
 │Variables│                       0│
 ╰─────────┴────────────────────────╯
 
+                                            Variables
index 64940a388027eebbc65c0bf0c9e5a863de95a530..5359293d07a6ea939a778948bf2eea9e09aad65d 100644 (file)
@@ -15,6 +15,7 @@ This system file does not indicate its own character encoding.  For best results
 │Variables│4│
 ╰─────────┴─╯
 
+                                                 Variables
 ╭────────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │        │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index d0cc32ccf947d14652197897d1074a315e448f18..fb10a7bff6d0217dd590d61e19cfa1dcb29bf341 100644 (file)
@@ -12,6 +12,7 @@
 │Variables│                       5│
 ╰─────────┴────────────────────────╯
 
+                                               Variables
 ╭─────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │     │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├─────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 813c06e8347d1951fc637278ef4931cbadf24711..df8ecd7128962b6f4324d81ff41d43edcbbde705 100644 (file)
@@ -11,6 +11,7 @@
 │Variables│2│
 ╰─────────┴─╯
 
+                                               Variables
 ╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │    │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index b1c0844998ae8dd9aae0e7c63460f91c1573bbd9..b0754f43a02cf093c7f0264c6a465fc9550f992f 100644 (file)
@@ -11,6 +11,7 @@
 │Variables│1│
 ╰─────────┴─╯
 
+                                               Variables
 ╭─────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │     │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├─────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index e98ba62f71aa11b40e4b64238af4075b21c07cbd..9ec16fbd0798b77a3904313771c0f28fb33b32ad 100644 (file)
@@ -12,6 +12,7 @@
 │Variables│5│
 ╰─────────┴─╯
 
+                                                                                              Variables
 ╭────────────────────────────────────────────────────┬────────┬────────────────────────────────────────────────────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │                                                    │Position│                        Label                       │Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────────────────────────────────────────────────────┼────────┼────────────────────────────────────────────────────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index ba37a94432737c954f009fc7f0adf645ff4313c9..c91be624b3ddd5f332de69e7980173451f26109b 100644 (file)
@@ -13,6 +13,7 @@ At offsets 0xf4...0x114, record types 3 and 4 may not add value labels to one or
 │Variables│1│
 ╰─────────┴─╯
 
+                                               Variables
 ╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │    │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index a02e3abb6a947b7ad48c2ac694cd23a001cbb541..3d620f7241a77e5912197d4635f0cb0732845510 100644 (file)
@@ -16,6 +16,7 @@ Text string contains invalid bytes for UTF-8 encoding: "PSPP synthetic test file
 │Variables│                             4│
 ╰─────────┴──────────────────────────────╯
 
+                                             Variables
 ╭─┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├─┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 3d4be23ca949990bdf1306ea988a0e4a6e7684c6..bf775efd479cec714056fb7fdb6340b655c39acc 100644 (file)
@@ -13,6 +13,7 @@ Unknown extension record with subtype 30 at offset 0xe0, consisting of 1 1-byte
 │Variables│1│
 ╰─────────┴─╯
 
+                                               Variables
 ╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │    │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 5a45874292a833bb031fbebbff00c8cfc8699a6f..5d5a5c815923be8e173573b84535310aacfdc8be 100644 (file)
@@ -15,6 +15,7 @@ Warning at file offsets 0xfd to 0x10f: In file or variable attribute record: Att
 │Variables│1│
 ╰─────────┴─╯
 
+                                                 Variables
 ╭────────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │        │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 12cfebb0c6cf7e3e716dc77b1613d03d493d279b..ed9a324b8f0a427ae24eb3a86cdd70ee4e17d99a 100644 (file)
@@ -12,6 +12,7 @@
 │Variables│                       2│
 ╰─────────┴────────────────────────╯
 
+                                                                    Variables
 ╭──────────────────────────┬────────┬──────────────────────────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │                          │Position│           Label          │Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├──────────────────────────┼────────┼──────────────────────────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 8469f599a886c0663b062a5ca7e930b86de15e16..ceb9b71ee67df2baec8756cf151c01364a568205 100644 (file)
@@ -17,6 +17,7 @@ Warning at file offsets 0x160 to 0x168: In value label record: One or more varia
 │Variables│2│
 ╰─────────┴─╯
 
+                                               Variables
 ╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │    │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 54880f864805c74bd0f20fb35100b37835eb999f..67c64a991e011bec1a49f7e13043b108432eb45f 100644 (file)
@@ -13,6 +13,7 @@ Warning at file offsets 0x110 to 0x114: In value label record: One or more varia
 │Variables│1│
 ╰─────────┴─╯
 
+                                               Variables
 ╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │    │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 1230c0a499ad424a8b6dcc086b0143bfc230a75f..91ea4380dd7a9ff63d968be66138da96f2eefb7f 100644 (file)
@@ -15,6 +15,7 @@ This system file does not indicate its own character encoding.  For best results
 │Variables│1│
 ╰─────────┴─╯
 
+                                               Variables
 ╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │    │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 54bd0ec5f099a2b8e83e16f5194ff4e507fd6c48..8f865a90ee2972d7987c2f56e5bba8f40e43b4d5 100644 (file)
@@ -13,6 +13,7 @@
 │Variables│                      17│
 ╰─────────┴────────────────────────╯
 
+                                               Variables
 ╭─────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │     │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├─────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index a90f718cbf365eaf80994604e074ae6aae7ca2c9..898c8350f250b63b092cf6ed7b9ce50b5f93dd3d 100644 (file)
@@ -12,6 +12,7 @@
 │Variables│                      12│
 ╰─────────┴────────────────────────╯
 
+                                             Variables
 ╭─┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├─┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 97aa90c9ea60729e07468eae10b269d89059df73..e810dcd53f3f31a8345edfbd1176c48c1ec81955 100644 (file)
@@ -12,6 +12,7 @@
 │Variables│                      12│
 ╰─────────┴────────────────────────╯
 
+                                             Variables
 ╭─┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │ │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├─┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 264872394679a6b434e16ce71b31e8b024fc8b8c..6d6b7a751da55f5d7e6976461ae1e641134b7bcd 100644 (file)
@@ -13,6 +13,7 @@
 │Variables│                            21│
 ╰─────────┴──────────────────────────────╯
 
+                                                                                 Variables
 ╭────────────────────────────────┬────────┬────────────────────────────────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬───────────────────────────╮
 │                                │Position│              Label             │Measurement Level│ Role│Width│Alignment│Print Format│Write Format│       Missing Values      │
 ├────────────────────────────────┼────────┼────────────────────────────────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼───────────────────────────┤
index ce3c4cc80f35d093e230b624814b9259f39e9202..4bd3a7647e262aa4e18698225b94c4ed27f0c5d2 100644 (file)
@@ -15,6 +15,7 @@ Unknown role "6".
 │Variables│                       7│
 ╰─────────┴────────────────────────╯
 
+                                               Variables
 ╭─┬────────┬─────┬─────────────────┬─────────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │ │Position│Label│Measurement Level│   Role  │Width│Alignment│Print Format│Write Format│Missing Values│
 ├─┼────────┼─────┼─────────────────┼─────────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 982db894ea623e0880d2c61f30785677e3664e25..f257f41e7ef1410600d6d80b3403905437e0f1dd 100644 (file)
@@ -13,6 +13,7 @@
 │Variables│                      10│
 ╰─────────┴────────────────────────╯
 
+                                              Variables
 ╭──┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │  │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├──┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index bf63c319e6ae34dd082a1318f2208c4a77377461..1a86dade444679eb61f6a4de43e05f8d10b40e12 100644 (file)
@@ -17,6 +17,7 @@ Variable set "vs2" includes unknown variable foo.
 │Variables│                      10│
 ╰─────────┴────────────────────────╯
 
+                                              Variables
 ╭──┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │  │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├──┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 0723dc8d92dbe20cad981376a903de0abc8b371b..6ffdf78056ef83a85bec5347ddc437a3b1a36c96 100644 (file)
@@ -13,6 +13,7 @@ Warning at file offsets 0x110 to 0x118: In value label record: First variable in
 │Variables│2│
 ╰─────────┴─╯
 
+                                               Variables
 ╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │    │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 3024855129b62ebe9180ec779681baad9b04a27a..35b06625cb95187f6dcb55885ac3288d0ee7a68a 100644 (file)
@@ -13,6 +13,7 @@
 │Variables│                       2│
 ╰─────────┴────────────────────────╯
 
+                                                Variables
 ╭──────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │      │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├──────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index a77a5d4fdcc7b2f122620a7cf1a311ba9311d960..4e1bf58cad142e5505a9ac18f87b2e005590fed3 100644 (file)
@@ -13,6 +13,7 @@ File designates string variable STR1 (index 2) as weight variable, but weight va
 │Variables│2│
 ╰─────────┴─╯
 
+                                               Variables
 ╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │    │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index e4b2b8f5148de1fa88a4be5733990d42256c9076..dae8fb9619e538c81ea98a1c89a1a0b364139d4e 100644 (file)
@@ -13,6 +13,7 @@ File weight variable index 3 is invalid because it exceeds maximum variable inde
 │Variables│2│
 ╰─────────┴─╯
 
+                                               Variables
 ╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │    │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 9dc6cf3df7cc7ee544574117f55f60bc30bd3111..da6403f16b1e963ce5a91128f540e54219bef318 100644 (file)
@@ -13,6 +13,7 @@ File weight variable index 2 is invalid because it refers to long string continu
 │Variables│2│
 ╰─────────┴─╯
 
+                                               Variables
 ╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │    │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 51f13ca578b46d83b9ce6894e9208c7c0388ad0c..efae9c863ab75042972ff394dc2b30f11cb65672 100644 (file)
@@ -12,6 +12,7 @@
 │Variables│4│
 ╰─────────┴─╯
 
+                                                 Variables
 ╭─────────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │         │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├─────────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index b1cf952ad594f0f334310d3290bbbe3bf3c3ea97..3f908c6f309fda2d83db8efc87e835bdc140f870 100644 (file)
@@ -12,6 +12,7 @@
 │Variables│4│
 ╰─────────┴─╯
 
+                                                 Variables
 ╭─────────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │         │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├─────────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 46c6fbb2c328912d88822fca97ccda9f1e9d995d..9295cfe8889195ad29c75844c28c10cc229f72d4 100644 (file)
@@ -12,6 +12,7 @@
 │Variables│4│
 ╰─────────┴─╯
 
+                                                 Variables
 ╭─────────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │         │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├─────────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index c034000a09f96b1f83d62ef7bb919d8e13a714d6..40458838bd92acc841140ef048bed09c97489a07 100644 (file)
@@ -12,6 +12,7 @@
 │Variables│5│
 ╰─────────┴─╯
 
+                                               Variables
 ╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │    │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 4cc7d6ecae1cbb35d2c9efc3114a330201099b56..09746ba3fe9c389d0ba5e1538fe500b4186a6fcc 100644 (file)
@@ -12,6 +12,7 @@
 │Variables│5│
 ╰─────────┴─╯
 
+                                               Variables
 ╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │    │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 11e64a4529dbbbff0927aa202bd5848408625ae1..c6a3f0db22bc83f89f7adaedca45f0b670e7d508 100644 (file)
@@ -12,6 +12,7 @@
 │Variables│5│
 ╰─────────┴─╯
 
+                                               Variables
 ╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │    │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 7628df79a2b8557d237cdc118a133b7b3d149b9b..fa6fa29ac00ee9d4457df1e07c2fa2a513d826ee 100644 (file)
@@ -13,6 +13,7 @@ Warning at file offsets 0xe0 to 0xe8: In variable display record: Invalid variab
 │Variables│1│
 ╰─────────┴─╯
 
+                                               Variables
 ╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │    │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 783766c84ae5b2dc14b39bd29c67430554e66d48..5d76ed0661872ff9883aa7091bc44ffbc12483fb 100644 (file)
@@ -13,6 +13,7 @@ Warning at file offsets 0xe0 to 0xe8: In variable display record: Invalid variab
 │Variables│1│
 ╰─────────┴─╯
 
+                                               Variables
 ╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │    │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index cefc24fa1b4b1c8642ca0788ea0c998ec42a8079..3617b645db7fb85a748d7d1a4144d048e1c35fa9 100644 (file)
@@ -13,6 +13,7 @@ Warning at file offsets 0xe0 to 0xf0: In variable display record: Record contain
 │Variables│1│
 ╰─────────┴─╯
 
+                                               Variables
 ╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │    │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 06a93186ed2816ad4957c5f0b0df04546bd72a8e..e8fda3180c8d0c12efee8d1e82cb08a7087480fe 100644 (file)
@@ -13,6 +13,7 @@ Warning at file offsets 0xe0 to 0xf0: In extension record: variable display reco
 │Variables│1│
 ╰─────────┴─╯
 
+                                               Variables
 ╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │    │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 6e558910886b7c8d03ef0eed9eed2705a36b826f..b73def632a42fa2a1823eaf85f9f41c5690dd494 100644 (file)
@@ -17,6 +17,7 @@ System file specifies value 2.0 (0x1.0p1) as LOWEST but -1.7976931348623157e308
 │Variables│1│
 ╰─────────┴─╯
 
+                                               Variables
 ╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │    │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 7c01e4d7201437a31af33d122694414f2bff7a17..34858dc9219b31a116d83aa6c9c7e0175e8861f4 100644 (file)
@@ -13,6 +13,7 @@ File header claims 2 variable positions but 1 were read from file.
 │Variables│1│
 ╰─────────┴─╯
 
+                                               Variables
 ╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │    │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index d701ada126098b14ddb2117d473e4dee119524cc..463a44e39b819e56535d0fcd768d66333718fbcf 100644 (file)
@@ -13,6 +13,7 @@
 │Variables│                       2│
 ╰─────────┴────────────────────────╯
 
+                                                                    Variables
 ╭──────────────────────────┬────────┬──────────────────────────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │                          │Position│           Label          │Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├──────────────────────────┼────────┼──────────────────────────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 834891e4f6a26bdc237e152e6323b2c126086d91..66f4355225645887a59d5ddcbecbc5d44c20e5ae 100644 (file)
@@ -12,6 +12,7 @@
 │Variables│                       5│
 ╰─────────┴────────────────────────╯
 
+                                               Variables
 ╭─────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │     │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├─────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index c12460b4f5aa3a453676f5f1c28f9e2369f8a3c8..b5fabe9da74b618720bd571f6da40b8282a7829e 100644 (file)
@@ -14,6 +14,7 @@ Warning at file offsets 0x1c4 to 0x1dc: In ZLIB trailer: Block descriptor 0 repo
 │Variables│                       5│
 ╰─────────┴────────────────────────╯
 
+                                               Variables
 ╭─────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │     │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├─────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤
index 098e5345a5f1996c7b3ff20dc69c12c37f3ff3fd..a7a05a9cce1d382cb23dbc6e77fdf6d754d4d689 100644 (file)
@@ -15,6 +15,7 @@ Multiple response set $b has no variables.
 │Variables│1│
 ╰─────────┴─╯
 
+                                               Variables
 ╭────┬────────┬─────┬─────────────────┬─────┬─────┬─────────┬────────────┬────────────┬──────────────╮
 │    │Position│Label│Measurement Level│ Role│Width│Alignment│Print Format│Write Format│Missing Values│
 ├────┼────────┼─────┼─────────────────┼─────┼─────┼─────────┼────────────┼────────────┼──────────────┤