- [Inspecting Portable Files](invoking/pspp-show-por.md)
- [Inspecting SPSS/PC+ Files](invoking/pspp-show-pc.md)
- [Inspecting SPSS Viewer Files](invoking/pspp-show-spv.md)
+ - [Identifying Files](invoking/pspp-identify.md)
- [Decrypting Files](invoking/pspp-decrypt.md)
- [Output Driver Configuration](invoking/output.md)
--- /dev/null
+# Identifying Files
+
+The `pspp identify` command identifies file types. The syntax is:
+
+```
+pspp identify <FILE>
+```
+
+which reads `<FILE>` and identifies it based on its contents. The
+command prints one of the following on the terminal:
+
+* `sav`, for an SPSS system file, `sav (encrypted)` if the file is
+ encrypted.
+
+* `por`, for an SPSS portable file.
+
+* `pc+`, for an SPSS/PC+ data file.
+
+* `spv`, for an SPSS Viewer file, or `spv (encrypted)` if the file is
+ encrypted.
+
+* `sps`, for an SPSS syntax file, `sps (encrypted)` if the file is
+ encrypted, or `sps (low confidence)` if the file could be a syntax
+ file or another kind of text file.
+
+* `unknown`, for other kinds of files.
```
container
- :visibility=(visible | hidden)
+ :visibility=(visible | hidden)?
:page-break-before=(always)?
:text-align=(left | center)?
:width=dimension
```
container
- :visibility=(visible | hidden)
+ :visibility=(visible | hidden)?
:page-break-before=(always | auto | avoid | left | right | inherit)?
:text-align=(left | center)?
:width=dimension
* `visibility`
Whether the container's content is displayed. "Notes" tables are
- often hidden; other data is usually visible.
+ often hidden; other data is usually visible. The default is
+ `visible`.
* `page-break-before`
Whether to start the element at the beginning of a new page. This
Ok(None)
}
+
+ /// Returns a string for the typical extension associated with this kind of
+ /// file, without the leading `.`.
+ ///
+ /// Returns `pc+` for [FileType::Pc] files, even though that is not typical,
+ /// since these files are so unusual.
+ pub fn as_extension(&self) -> &'static str {
+ match self {
+ FileType::System { .. } => "sav",
+ FileType::Portable => "por",
+ FileType::Pc => "pc+",
+ FileType::Viewer { .. } => "spv",
+ FileType::Syntax { .. } => "sps",
+ }
+ }
}
#[cfg(test)]
--- /dev/null
+// PSPP - a program for statistical analysis.
+// Copyright (C) 2025 Free Software Foundation, Inc.
+//
+// This program is free software: you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free Software
+// Foundation, either version 3 of the License, or (at your option) any later
+// version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+// details.
+//
+// You should have received a copy of the GNU General Public License along with
+// this program. If not, see <http://www.gnu.org/licenses/>.
+
+use anyhow::Result;
+use clap::Args;
+use pspp::file::FileType;
+use std::path::PathBuf;
+
+/// Identify the type of a file.
+#[derive(Args, Clone, Debug)]
+pub struct Identify {
+ /// File to identify.
+ file: PathBuf,
+}
+
+impl Identify {
+ pub fn run(self) -> Result<()> {
+ match FileType::from_file(&self.file)? {
+ None => println!("unknown"),
+ Some(file_type) => {
+ print!("{}", file_type.as_extension());
+ if file_type.is_encrypted() {
+ print!(" (encrypted)");
+ }
+ if !file_type.is_confident() {
+ print!(" (low confidence)");
+ }
+ println!();
+ }
+ }
+ Ok(())
+ }
+}
-/* PSPP - a program for statistical analysis.
- * Copyright (C) 2023 Free Software Foundation, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+// PSPP - a program for statistical analysis.
+// Copyright (C) 2025 Free Software Foundation, Inc.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
use anyhow::Result;
use clap::{Parser, Subcommand};
use thiserror::Error as ThisError;
use crate::{
- convert::Convert, decrypt::Decrypt, show::Show, show_pc::ShowPc, show_por::ShowPor,
- show_spv::ShowSpv,
+ convert::Convert, decrypt::Decrypt, identify::Identify, show::Show, show_pc::ShowPc,
+ show_por::ShowPor, show_spv::ShowSpv,
};
mod convert;
mod decrypt;
+mod identify;
mod show;
mod show_pc;
mod show_por;
enum Command {
Convert(Convert),
Decrypt(Decrypt),
+ Identify(Identify),
Show(Show),
ShowPor(ShowPor),
ShowPc(ShowPc),
match self {
Command::Convert(convert) => convert.run(),
Command::Decrypt(decrypt) => decrypt.run(),
+ Command::Identify(identify) => identify.run(),
Command::Show(show) => show.run(),
Command::ShowPor(show_por) => show_por.run(),
Command::ShowPc(show_pc) => show_pc.run(),
.into_item()
.with_command_name(container_text.command_name)
.with_spv_info(SpvInfo::new(structure_member)),
+ ContainerContent::Model => new_error_item("models not yet implemented")
+ .with_spv_info(SpvInfo::new(structure_member).with_error()),
+ ContainerContent::Object => new_error_item("objects not yet implemented")
+ .with_spv_info(SpvInfo::new(structure_member).with_error()),
ContainerContent::Tree => new_error_item("trees not yet implemented")
.with_spv_info(SpvInfo::new(structure_member).with_error()),
};
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct Label {
- #[serde(rename = "$text")]
+ #[serde(default, rename = "$text")]
text: String,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct Container {
- #[serde(rename = "@visibility")]
+ #[serde(default, rename = "@visibility")]
visibility: Visibility,
#[serde(rename = "@page-break-before")]
#[serde(default)]
Inherit,
}
-#[derive(Deserialize, Debug)]
+#[derive(Deserialize, Debug, Default)]
#[serde(rename_all = "camelCase")]
enum Visibility {
+ #[default]
Visible,
Hidden,
}
Table(Table),
Text(ContainerText),
Graph(Graph),
- /*
- Model(Model),
- Object(Object),
- Image(Image),*/
+ Model,
+ Object,
+ //Image(Image),
Tree,
}