/SCALEMIN=COUNT
(data output)
- /CC{A,B,C,D,E}={'NPRE,PRE,SUF,NSUF','NPRE.PRE.SUF.NSUF'}
+ /CC{A,B,C,D,E}='STRING'
/DECIMAL={DOT,COMMA}
/FORMAT=FMT_SPEC
/LEADZERO={ON,OFF}
/TVARS={NAMES,LABELS,BOTH}
/TLOOK={NONE,FILE}
-(logging)
+(journal)
/JOURNAL={ON,OFF} ['FILE_NAME']
(system files)
/SCOMPRESSION={ON,OFF}
-(miscellaneous)
+(security)
/SAFER=ON
/LOCALE='STRING'
/MITERATE=NUMBER
/MNEST=NUMBER
-(settings not yet implemented, but accepted and ignored)
+(not yet implemented)
/BASETEXTDIRECTION={AUTOMATIC,RIGHTTOLEFT,LEFTTORIGHT}
/BLOCK='C'
/BOX={'XXX','XXXXXXXXXXX'}
# Data Input
+```
+SET
+ /BLANKS={SYSMIS,'.',number}
+ /DECIMAL={DOT,COMMA}
+ /FORMAT=FMT_SPEC
+ /EPOCH={AUTOMATIC,YEAR}
+ /RIB={NATIVE,MSBFIRST,LSBFIRST}
+```
+
The data input subcommands affect the way that data is read from data
files. The data input subcommands are:
# Interaction
+```
+SET
+ /MXERRS=MAX_ERRS
+ /MXWARNS=MAX_WARNINGS
+ /WORKSPACE=WORKSPACE_SIZE
+```
+
Interaction subcommands affect the way that PSPP interacts with an
online user. The interaction subcommands are
are issued, except a single initial warning advising you that
warnings will not be given. The default value is 100.
+# Syntax Execution
+
+```
+SET
+ /LOCALE='LOCALE'
+ /MXLOOPS=MAX_LOOPS
+ /SEED={RANDOM,SEED_VALUE}
+ /UNDEFINED={WARN,NOWARN}
+ /FUZZBITS=FUZZBITS
+ /SCALEMIN=COUNT
+```
+
Syntax execution subcommands control the way that PSPP commands
execute. The syntax execution subcommands are
# Data Output
+```
+SET
+ /CC{A,B,C,D,E}='STRING'
+ /DECIMAL={DOT,COMMA}
+ /FORMAT=FMT_SPEC
+ /LEADZERO={ON,OFF}
+ /MDISPLAY={TEXT,TABLES}
+ /SMALL=NUMBER
+ /WIB={NATIVE,MSBFIRST,LSBFIRST}
+```
+
Data output subcommands affect the format of output data. These
subcommands are
# Output Routing
+```
+SET
+ /ERRORS={ON,OFF,TERMINAL,LISTING,BOTH,NONE}
+ /MESSAGES={ON,OFF,TERMINAL,LISTING,BOTH,NONE}
+ /PRINTBACK={ON,OFF,TERMINAL,LISTING,BOTH,NONE}
+ /RESULTS={ON,OFF,TERMINAL,LISTING,BOTH,NONE}
+```
+
In the PSPP text-based interface, the output routing subcommands
affect where output is sent. The following values are allowed for each
of these subcommands:
# Output Driver
+```
+SET
+ /HEADERS={NO,YES,BLANK}
+ /LENGTH={NONE,N_LINES}
+ /WIDTH={NARROW,WIDTH,N_CHARACTERS}
+ /TNUMBERS={VALUES,LABELS,BOTH}
+ /TVARS={NAMES,LABELS,BOTH}
+ /TLOOK={NONE,FILE}
+```
+
Output driver option subcommands affect output drivers' settings.
These subcommands are:
# Journal
+```
+SET
+ /JOURNAL={ON,OFF} ['FILE_NAME']
+```
+
Journal subcommands affect logging of commands executed to external
files. These subcommands are
The journal is named `pspp.jnl` by default. A different name may
be specified.
+# System Files
+
+```
+SET
+ /SCOMPRESSION={ON,OFF}
+```
+
System file subcommands affect the default format of system files
produced by PSPP. These subcommands are
# Security
+```
+SET
+ /SAFER=ON
+ /LOCALE='STRING'
+```
+
Security subcommands affect the operations that commands are allowed
to perform. The security subcommands are
# Macros
+```
+SET
+ /MEXPAND={ON,OFF}
+ /MPRINT={ON,OFF}
+ /MITERATE=NUMBER
+ /MNEST=NUMBER
+```
+
The following subcommands affect the interpretation of macros. For
more information, see [Macro Settings](define.md#macro-settings).
# Not Yet Implemented
+```
+SET
+ /BASETEXTDIRECTION={AUTOMATIC,RIGHTTOLEFT,LEFTTORIGHT}
+ /BLOCK='C'
+ /BOX={'XXX','XXXXXXXXXXX'}
+ /CACHE={ON,OFF}
+ /CELLSBREAK=NUMBER
+ /COMPRESSION={ON,OFF}
+ /CMPTRANS={ON,OFF}
+ /HEADER={NO,YES,BLANK}
+```
+
The following subcommands are not yet implemented, but PSPP accepts
them and ignores the settings:
sync::Arc,
};
-use binrw::{BinRead, BinResult, Endian, Error as BinError, VecArgs, binread};
+use binrw::{BinRead, BinResult, Endian, Error as BinError, VecArgs, binread, io::TakeSeekExt};
use chrono::DateTime;
use displaydoc::Display;
use encoding_rs::{Encoding, WINDOWS_1252};
header: Header,
#[br(args(header.version))]
titles: Titles,
- #[br(parse_with(parse_counted), args(header.version))]
+ #[br(parse_with(parse_vec), args(header.version))]
footnotes: Vec<Footnote>,
#[br(args(header.version))]
areas: Areas,
- borders: Counted<Borders>,
- print_settings: Counted<PrintSettings>,
- #[br(dbg, if(header.version == Version::V3))]
- table_settings: Counted<TableSettings>,
+ #[br(parse_with(parse_counted))]
+ borders: Borders,
+ #[br(parse_with(parse_counted))]
+ print_settings: PrintSettings,
+ #[br(dbg, if(header.version == Version::V3), parse_with(parse_counted))]
+ table_settings: TableSettings,
#[br(if(header.version == Version::V1), temp)]
_ts: Option<Counted<Sponge>>,
#[br(args(header.version))]
formats: Formats,
- #[br(parse_with(parse_counted), args(header.version))]
+ #[br(parse_with(parse_vec), args(header.version))]
dimensions: Vec<Dimension>,
axes: Axes,
- #[br(parse_with(parse_counted), args(header.version))]
+ #[br(parse_with(parse_vec), args(header.version))]
cells: Vec<Cell>,
}
}
#[binrw::parser(reader, endian)]
-fn parse_counted<T, A>(inner: A, ...) -> BinResult<Vec<T>>
+fn parse_vec<T, A>(inner: A, ...) -> BinResult<Vec<T>>
where
for<'a> T: BinRead<Args<'a> = A>,
A: Clone,
#[br(big)]
#[derive(Debug)]
struct Borders {
- #[br(magic(1u32), parse_with(parse_counted))]
+ #[br(magic(1u32), parse_with(parse_vec))]
borders: Vec<Border>,
#[br(parse_with(parse_bool))]
show_alphabetic_markers: bool,
#[br(parse_with(parse_bool))]
footnote_marker_subscripts: bool,
- #[br(temp, parse_with(parse_bool))]
- _x6: bool,
- #[br(big)]
- sizing: Counted<Sizing>,
+ #[br(temp)]
+ _x6: u8,
+ #[br(big, parse_with(parse_counted))]
+ sizing: Sizing,
notes: U32String,
table_look: U32String,
#[br(temp)]
#[br(big)]
#[derive(Debug, Default)]
struct Sizing {
- #[br(parse_with(parse_counted))]
+ #[br(parse_with(parse_vec))]
row_breaks: Vec<u32>,
- #[br(parse_with(parse_counted))]
+ #[br(parse_with(parse_vec))]
column_breaks: Vec<u32>,
- #[br(parse_with(parse_counted))]
+ #[br(parse_with(parse_vec))]
row_keeps: Vec<(i32, i32)>,
- #[br(parse_with(parse_counted))]
+ #[br(parse_with(parse_vec))]
column_keeps: Vec<(i32, i32)>,
- #[br(parse_with(parse_counted))]
+ #[br(parse_with(parse_vec))]
row_point_keeps: Vec<[i32; 3]>,
- #[br(parse_with(parse_counted))]
+ #[br(parse_with(parse_vec))]
column_point_keeps: Vec<[i32; 3]>,
}
#[binread]
#[derive(Default)]
struct U32String {
- #[br(parse_with(parse_counted))]
+ #[br(parse_with(parse_vec))]
string: Vec<u8>,
}
#[binread]
struct CountedInner {
- #[br(parse_with(parse_counted))]
+ #[br(parse_with(parse_vec))]
data: Vec<u8>,
}
endian: binrw::Endian,
args: Self::Args<'_>,
) -> BinResult<Self> {
- let counted = CountedInner::read_options(reader, endian, ())?;
- let mut cursor = counted.cursor();
- let result = <T>::read_options(&mut cursor, Endian::Little, args)?;
- if cursor.position() < cursor.get_ref().len() as u64 {
+ let start = reader.stream_position()?;
+ let count = u32::read_options(reader, endian, ())? as u64;
+ let end = start + count;
+ let mut inner = reader.take_seek(count);
+ let result = <T>::read_options(&mut inner, Endian::Little, args)?;
+ let pos = inner.stream_position()?;
+ if pos < end {
+ let consumed = pos - start;
return Err(binrw::Error::Custom {
- pos: cursor.position(),
+ pos,
err: Box::new(format!(
- "counted data not exhausted (consumed {} bytes out of {})",
- cursor.position(),
- cursor.get_ref().len()
+ "counted data not exhausted (consumed {consumed} bytes out of {count})"
)),
});
}
}
}
+#[binrw::parser(reader, endian)]
+fn parse_counted<T, A>(args: A, ...) -> BinResult<T>
+where
+ for<'a> T: BinRead<Args<'a> = A>,
+ A: Clone,
+ T: 'static,
+{
+ Ok(<Counted<T>>::read_options(reader, endian, args)?.0)
+}
+
/// `BinRead` for `Option<T>` always requires the value to be there. This
/// instead tries to read it and falls back to None if there's no match.
#[derive(Clone, Debug)]
#[br(import(version: Version))]
#[derive(Debug)]
struct Formats {
- #[br(parse_with(parse_counted))]
+ #[br(parse_with(parse_vec))]
column_widths: Vec<i32>,
locale: U32String,
current_layer: i32,
}
fn x2(&self) -> Option<&X2> {
- self.v3.as_ref().map(|v3| &*v3.x1_x2.x2)
+ self.v3.as_ref().map(|v3| &v3.x1_x2.x2)
}
fn x3(&self) -> Option<&X3> {
- self.v3.as_ref().map(|v3| &*v3.x3)
+ self.v3.as_ref().map(|v3| &v3.x3)
}
fn charset(&self) -> Option<&U32String> {
#[derive(Debug)]
struct FormatsV3 {
#[br(dbg)]
- x1_x2: Counted<X1X2>,
- x3: Counted<X3>,
+ #[br(parse_with(parse_counted))]
+ x1_x2: X1X2,
+ #[br(parse_with(parse_counted))]
+ x3: X3,
}
#[binread]
#[derive(Debug)]
struct X1X2 {
x1: X1,
- x2: Counted<X2>,
+ #[br(parse_with(parse_counted))]
+ x2: X2,
}
#[binread]
#[br(little)]
#[derive(Debug)]
struct X2 {
- #[br(parse_with(parse_counted))]
+ #[br(parse_with(parse_vec))]
row_heights: Vec<i32>,
- #[br(parse_with(parse_counted))]
+ #[br(parse_with(parse_vec))]
style_map: Vec<(i64, i16)>,
- #[br(parse_with(parse_counted))]
+ #[br(parse_with(parse_vec))]
styles: Vec<StylePair>,
- tail: Counted<Optional<[u8; 8]>>,
+ #[br(parse_with(parse_counted))]
+ tail: Optional<[u8; 8]>,
}
#[binread]
y1: Y1,
#[br(dbg)]
small: f64,
+ #[br(magic = 1u8, temp)]
+ _one: (),
#[br(dbg)]
inner: Optional<X3Inner>,
y2: Y2,
#[br(little)]
#[derive(Debug)]
struct CustomCurrency {
- #[br(parse_with(parse_counted))]
+ #[br(parse_with(parse_vec))]
ccs: Vec<U32String>,
}
#[binread]
#[br(little)]
-#[br(return_unexpected_error, import(version: Version))]
+#[br( import(version: Version))]
#[derive(Debug)]
enum RawValue {
#[br(magic = 1u8)]
c: U32String,
},
Template {
- #[br(parse_with(parse_optional), args(version))]
+ #[br(dbg, parse_with(parse_optional), args(version))]
mods: Option<ValueMods>,
+ #[br(dbg)]
template: U32String,
- #[br(parse_with(parse_counted), args(version))]
+ #[br(parse_with(parse_vec), args(version))]
args: Vec<Argument>,
},
}
}
}
-#[binread]
-#[br(little)]
-#[br(import(version: Version))]
#[derive(Debug)]
-enum Argument {
- Singleton(#[br(magic(0u32), args(version))] Value),
- Multiple {
- #[br(magic(0u32), parse_with(parse_counted), args(version))]
- values: Vec<Value>,
- },
+struct Argument(Vec<Value>);
+
+impl BinRead for Argument {
+ type Args<'a> = (Version,);
+
+ fn read_options<R: Read + Seek>(
+ reader: &mut R,
+ endian: Endian,
+ (version,): (Version,),
+ ) -> BinResult<Self> {
+ let count = u32::read_options(reader, endian, ())? as usize;
+ dbg!(count);
+ if count == 0 {
+ Ok(Self(vec![Value::read_options(reader, endian, (version,))?]))
+ } else {
+ let zero = u32::read_options(reader, endian, ())?;
+ assert_eq!(zero, 0);
+ let values = <Vec<_>>::read_options(
+ reader,
+ endian,
+ VecArgs {
+ count,
+ inner: (version,),
+ },
+ )?;
+ Ok(Self(values))
+ }
+ }
}
impl Argument {
encoding: &'static Encoding,
footnotes: &pivot::Footnotes,
) -> Vec<pivot::Value> {
- match self {
- Argument::Singleton(value) => vec![value.decode(encoding, footnotes)],
- Argument::Multiple { values } => values
- .iter()
- .map(|value| value.decode(encoding, footnotes))
- .collect(),
- }
+ self.0
+ .iter()
+ .map(|value| value.decode(encoding, footnotes))
+ .collect()
}
}
#[br(little, import(version: Version))]
#[derive(Debug)]
struct ValueMods {
- #[br(parse_with(parse_counted))]
+ #[br(dbg, parse_with(parse_vec))]
refs: Vec<i16>,
- #[br(parse_with(parse_counted))]
+ #[br(dbg, parse_with(parse_vec))]
subscripts: Vec<U32String>,
#[br(if(version == Version::V1))]
v1: Option<Sponge>,
- #[br(if(version == Version::V3))]
- v3: Counted<Optional<(Counted<TemplateString>, StylePair)>>,
+ #[br(if(version == Version::V3), parse_with(parse_counted))]
+ v3: ValueModsV3,
+}
+
+#[binread]
+#[br(little)]
+#[derive(Debug, Default)]
+struct ValueModsV3 {
+ #[br(parse_with(parse_counted))]
+ template_string: Optional<TemplateString>,
+ style_pair: StylePair,
}
impl ValueMods {
fn decode(&self, encoding: &'static Encoding, footnotes: &pivot::Footnotes) -> ValueStyle {
- let style_pair = self.v3.as_ref().map(|v3| &v3.1);
- let font_style = style_pair
- .and_then(|style_pair| style_pair.font_style.as_ref())
- .map(|font_style| pivot::FontStyle {
- bold: font_style.bold,
- italic: font_style.italic,
- underline: font_style.underline,
- markup: false,
- font: font_style.typeface.decode(encoding),
- fg: font_style.fg,
- bg: font_style.bg,
- size: (font_style.size as i32) * 4 / 3,
- });
- let cell_style = style_pair
- .and_then(|style_pair| style_pair.cell_style.as_ref())
- .map(|cell_style| {
- pivot::CellStyle {
- horz_align: match cell_style.halign {
- 0 => Some(HorzAlign::Center),
- 2 => Some(HorzAlign::Left),
- 4 => Some(HorzAlign::Right),
- 6 => Some(HorzAlign::Decimal {
- offset: cell_style.decimal_offset,
- decimal: Decimal::Dot, /*XXX*/
- }),
- _ => None,
- },
- vert_align: match cell_style.valign {
- 0 => VertAlign::Middle,
- 3 => VertAlign::Bottom,
- _ => VertAlign::Top,
- },
- margins: enum_map! {
- Axis2::X => [cell_style.left_margin as i32, cell_style.right_margin as i32],
- Axis2::Y => [cell_style.top_margin as i32, cell_style.bottom_margin as i32],
- },
- }
- });
+ let font_style =
+ self.v3
+ .style_pair
+ .font_style
+ .as_ref()
+ .map(|font_style| pivot::FontStyle {
+ bold: font_style.bold,
+ italic: font_style.italic,
+ underline: font_style.underline,
+ markup: false,
+ font: font_style.typeface.decode(encoding),
+ fg: font_style.fg,
+ bg: font_style.bg,
+ size: (font_style.size as i32) * 4 / 3,
+ });
+ let cell_style = self.v3.style_pair.cell_style.as_ref().map(|cell_style| {
+ pivot::CellStyle {
+ horz_align: match cell_style.halign {
+ 0 => Some(HorzAlign::Center),
+ 2 => Some(HorzAlign::Left),
+ 4 => Some(HorzAlign::Right),
+ 6 => Some(HorzAlign::Decimal {
+ offset: cell_style.decimal_offset,
+ decimal: Decimal::Dot, /*XXX*/
+ }),
+ _ => None,
+ },
+ vert_align: match cell_style.valign {
+ 0 => VertAlign::Middle,
+ 3 => VertAlign::Bottom,
+ _ => VertAlign::Top,
+ },
+ margins: enum_map! {
+ Axis2::X => [cell_style.left_margin as i32, cell_style.right_margin as i32],
+ Axis2::Y => [cell_style.top_margin as i32, cell_style.bottom_margin as i32],
+ },
+ }
+ });
ValueStyle {
cell_style,
font_style,
}
fn template_id(&self, encoding: &'static Encoding) -> Option<String> {
self.v3
+ .template_string
.as_ref()
- .map(|v3| &*v3.0)
- .and_then(|ts| ts.id.as_ref())
+ .and_then(|template_string| template_string.id.as_ref())
.map(|s| s.decode(encoding))
}
}
#[br(little)]
#[derive(Debug)]
struct TemplateString {
- _sponge: Counted<Sponge>,
+ #[br(parse_with(parse_counted), temp)]
+ _sponge: Sponge,
#[br(parse_with(parse_optional))]
id: Option<U32String>,
}
#[binread]
#[br(little)]
-#[derive(Debug)]
+#[derive(Debug, Default)]
struct StylePair {
#[br(parse_with(parse_optional))]
font_style: Option<FontStyle>,
hide_all_labels: bool,
#[br(magic(1u8), temp)]
_dim_index: i32,
- #[br(parse_with(parse_counted), args(version))]
+ #[br(parse_with(parse_vec), args(version))]
categories: Vec<Category>,
}
#[derive(Debug)]
enum Child {
Leaf {
- #[br(magic(b"\0\0\0\x02\0\0\0"))]
+ #[br(magic(0u16), parse_with(parse_bool), temp)]
+ _x24: bool,
+ #[br(magic(b"\x02\0\0\0"))]
leaf_index: u32,
#[br(magic(0u32), temp)]
_tail: (),
merge: bool,
#[br(temp, magic(b"\0\x01"))]
_x23: i32,
- #[br(magic(-1i32), parse_with(parse_counted), args(version))]
+ #[br(magic(-1i32), parse_with(parse_vec), args(version))]
subcategories: Vec<Box<Category>>,
},
}
#[br(little, import(version: Version))]
#[derive(Debug)]
struct Cell {
+ #[br(dbg)]
index: u64,
#[br(if(version == Version::V1), temp)]
_zero: Optional<Zero>,