summaryrefslogtreecommitdiff
path: root/support/pkgcheck/src/utils.rs
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /support/pkgcheck/src/utils.rs
Initial commit
Diffstat (limited to 'support/pkgcheck/src/utils.rs')
-rw-r--r--support/pkgcheck/src/utils.rs396
1 files changed, 396 insertions, 0 deletions
diff --git a/support/pkgcheck/src/utils.rs b/support/pkgcheck/src/utils.rs
new file mode 100644
index 0000000000..6b2d0a8753
--- /dev/null
+++ b/support/pkgcheck/src/utils.rs
@@ -0,0 +1,396 @@
+use std::fs::read_dir;
+use std::fs::read_link;
+use std::os::unix::fs::PermissionsExt;
+use std::path::Path;
+use std::path::PathBuf;
+use walkdir::DirEntry;
+
+use regex::Regex;
+use std::borrow::Cow;
+use std::io;
+use std::process::Command;
+
+
+use std::fs;
+
+pub fn temp_file_endings() -> Vec<(&'static str, &'static str)> {
+ // https://github.com/github/gitignore/blob/master/TeX.gitignore
+ // http://hopf.math.purdue.edu/doc/html/suffixes.html
+ vec![
+ ("-blx.aux", "bibliography auxiliary file"),
+ ("-blx.bib", "bibliography auxiliary file"),
+ (".4ct", "htlatex related"),
+ (".4tc", "htlatex related"),
+ (".DS_Store", "Mac OS custom attribute file"),
+ (".acn", "glossaries related"),
+ (".acr", "glossaries related"),
+ (".alg", ""),
+ (".aux", "core latex auxiliary file"),
+ (".backup", "backup file"),
+ (".bak", "backup file"),
+ (".bbl", "bibliography auxiliary file"),
+ (".bcf", "bibliography auxiliary file"),
+ (".blg", "bibliography log file"),
+ (".brf", "hyperref related"),
+ (".cb", "core latex auxiliary file"),
+ (".cb2", "core latex auxiliary file"),
+ (".cpt", "cprotect related"),
+ (".dvi", "intermediate document"),
+ (".ent", ""),
+ (".fdb_latexmk", "latexmk related"),
+ (".fff", "endfloat related"),
+ (".fls", ""),
+ (".fmt", "core latex auxiliary file"),
+ (".fot", "core latex auxiliary file"),
+ (".gaux", "generated by gregoriotex"),
+ (".glg", "glossary related"),
+ (".glo", "glossary related"),
+ (".glog", "generated by gregoriotex"),
+ (".gls", "glossary related"),
+ (".glsdefs", "glossaries related"),
+ (".gtex", "generated by gregoriotex"),
+ (".idv", "htlatex related"),
+ (".idx", "makeidx related"),
+ (".ilg", "makeidx related"),
+ (".ind", "makeidx related"),
+ (".lg", "htlatex related"),
+ (".loa", "core latex auxiliary file (list of algorithms)"),
+ (".lod", "generated by easy-todo"),
+ (".lof", "core latex auxiliary file (list of figures)"),
+ (".log", "a log file for any flavor of TeX"),
+ (".lol", "core latex auxiliary file (list of listings)"),
+ (".los", "list of slides"),
+ (".lot", "core latex auxiliary file"),
+ (".lox", ""),
+ (".lyx#", "LyX related autosave file"),
+ (".maf", "generated by minitoc"),
+ (".mlc", "generated by minitoc"),
+ (".mlf", "generated by minitoc"),
+ (".mlt", "generated by minitoc"),
+ (".nav", "beamer related"),
+ (".nlg", ""),
+ (".nlo", ""),
+ (".nls", ""),
+ (".o", "C object file"),
+ (".out", "Core latex auxiliary file"),
+ (".pdfsync", "pdfsync related"),
+ (".pre", "beamer related"),
+ (".pyg", ""),
+ (".run.xml", ""),
+ (".sav", "used for saved data"),
+ (".snm", "beamer related"),
+ (".soc", ""),
+ (".spl", "elsarticle related"),
+ (".sta", "generated by standalone package"),
+ (".swp", "vim swap file"),
+ (".synctex", "synctex related"),
+ (".synctex(busy)", "synctex related"),
+ (".synctex.gz", "synctex related"),
+ (".synctex.gz(busy)", "synctex related"),
+ (".tdo", "generated by todonotes (list of todos)"),
+ (".thm", "amsthm related"),
+ (".tmb", "generated by thumbs package"),
+ (".tmp", "indicates a temporary file"),
+ (".toc", "core latex auxiliary file (table of contents)"),
+ (".trc", "htlatex related"),
+ (".ttt", "endfloat related"),
+ (".tuc", ""),
+ (".upa", "generated by the soulpos package"),
+ (".upb", "generated by the soulpos package"),
+ (".url", "generated by jurabib"),
+ (".vrb", "beamer related"),
+ (".w18", "temporary file for the ifplatform package"),
+ (".xdv", "intermediate document"),
+ (".xref", "htlatex related"),
+ ("~", "a file name ending with ~ (tilde) is temporary anyway"),
+ // ( ".lyx~", "LyX related backup file" ),
+ ]
+}
+
+pub fn regex_temporary_file_endings() -> Regex {
+ let mut rv = String::new();
+ let mut first_time = true;
+ for (p, _) in temp_file_endings() {
+ if first_time {
+ rv.push('(');
+ first_time = false;
+ } else {
+ rv.push('|');
+ }
+ let px = str::replace(p, ".", "\\.");
+
+ rv.push_str(&px);
+ }
+ rv.push_str(")$");
+ Regex::new(&rv).unwrap()
+}
+
+pub fn is_temporary_file(entry: &str) -> bool {
+ lazy_static! {
+ static ref RE: Regex = regex_temporary_file_endings();
+ }
+
+ RE.is_match(entry)
+}
+
+pub fn is_empty_directory(entry: &DirEntry) -> Result<bool, io::Error> {
+ let s = entry.path().to_str().unwrap();
+ match read_dir(s) {
+ // try to read the directory specified
+ Ok(contents) => Ok(contents.count() == 0),
+ Err(e) => Err(e),
+ }
+}
+
+pub fn is_unwanted_directory(entry: &str) -> bool {
+ let names = ["__MACOSX"];
+ for n in &names {
+ if *n == entry {
+ return true;
+ };
+ }
+
+ false
+}
+
+// is_hidden checks for files or directories starting with a dot
+pub fn is_hidden(entry: &str) -> bool {
+ entry.starts_with('.')
+}
+
+// is_hidden checks for files or directories starting with a dot
+pub fn is_hidden_directory(entry: &DirEntry) -> bool {
+ let fname = entry.file_name();
+
+ if fname == "." {
+ return false;
+ };
+ if fname == "./" {
+ return false;
+ };
+
+ fname.to_str().map(|s| s.starts_with('.')).unwrap_or(false)
+}
+
+pub fn get_symlink(entry: &DirEntry) -> Result<Option<PathBuf>, io::Error> {
+ let r = entry.path().to_str().unwrap();
+
+ match read_link(r) {
+ Ok(o) => {
+ let full_path = if o.is_absolute() {
+ o
+ } else {
+ // make the relative path absolute
+ let p = entry.path().parent().unwrap();
+ p.join(&o)
+ };
+ if full_path.exists() {
+ Ok(Some(full_path))
+ } else {
+ Ok(None)
+ }
+ }
+ Err(e) => Err(e),
+ }
+}
+
+pub fn _is_symlink_broken(entry: &DirEntry) -> Result<bool, io::Error> {
+ let r = entry.path().to_str().unwrap();
+
+ match read_link(r) {
+ Ok(o) => {
+ let full_path = if o.is_absolute() {
+ o
+ } else {
+ // make the relative path absolute
+ let p = entry.path().parent().unwrap();
+ p.join(&o)
+ };
+ Ok(!full_path.exists())
+ }
+ Err(e) => Err(e),
+ }
+}
+
+// Runs `pdfinfo` to check a PDF document. If `pdfinfo`
+// returns non zero we assume that the PDF document is
+// corrupted.
+pub fn is_pdf_ok(fname: &str) -> CmdReturn {
+ run_cmd("/usr/bin/pdfinfo", &[fname])
+}
+
+pub fn get_perms(path: &Path) -> u32 {
+ match path.metadata() {
+ Ok(p) => p.permissions().mode(),
+ Err(_e) => 0,
+ }
+}
+
+pub fn others_match(p: u32, m: u32) -> bool {
+ p == m
+}
+
+#[test]
+fn test_others_have() {
+ assert_eq!(others_have(0o600, 4), false);
+ assert_eq!(others_have(0o601, 4), false);
+ assert_eq!(others_have(0o602, 4), false);
+ assert_eq!(others_have(0o603, 4), false);
+ assert_eq!(others_have(0o604, 4), true);
+ assert_eq!(others_have(0o605, 4), true);
+ assert_eq!(others_have(0o606, 4), true);
+ assert_eq!(others_have(0o607, 4), true);
+}
+
+// It checks if a permission `p` has the bits
+// given in `m` set for others
+pub fn others_have(p: u32, m: u32) -> bool {
+ let p1 = p & 0o0007;
+ p1 & m == m
+}
+
+#[test]
+fn test_owner_has() {
+ assert_eq!(owner_has(0o000, 4), false);
+ assert_eq!(owner_has(0o100, 4), false);
+ assert_eq!(owner_has(0o200, 4), false);
+ assert_eq!(owner_has(0o300, 4), false);
+ assert_eq!(owner_has(0o400, 4), true);
+ assert_eq!(owner_has(0o505, 4), true);
+ assert_eq!(owner_has(0o605, 4), true);
+ assert_eq!(owner_has(0o705, 4), true);
+}
+
+// It checks if a permission `p` has the bits
+// given in `m` set for the owner.
+pub fn owner_has(p: u32, m: u32) -> bool {
+ let p1 = p & 0o7777;
+ let m1 = m << 6;
+ p1 & m1 == m1
+}
+
+#[allow(dead_code)]
+fn owner_match(p: u32, m: u32) -> bool {
+ let p1 = p & 0o0700;
+
+ let m1 = m << 6;
+ p1 == m1
+}
+
+// Formats a permission value to octal for output
+pub fn perms_to_string(p: u32) -> String {
+ format!("{:04o}", p & 0o7777)
+}
+
+pub struct CmdReturn {
+ pub status: bool,
+ pub output: Option<String>,
+}
+
+// Runs a command in a shell
+// If the command returns 0 stdout gets captured. Otherwise
+// stderr gets captured and returned.
+pub fn run_cmd(cmd: &str, argument: &[&str]) -> CmdReturn {
+ let output = Command::new(cmd)
+ .args(argument.iter())
+ .output()
+ .expect("Failed to execute process");
+
+ if output.status.success() {
+ CmdReturn {
+ status: true,
+ output: Some(String::from_utf8_lossy(&output.stdout).to_string()),
+ }
+ } else {
+ CmdReturn {
+ status: false,
+ output: Some(String::from_utf8_lossy(&output.stderr).to_string()),
+ }
+ }
+}
+
+// Sets permissions for a file or directory
+// Sample invocation: set_perms("somfile", 0o644);
+pub fn set_perms(entry: &str, p: u32) -> bool {
+ let ps = &format!("{:o}", p);
+ let rc = run_cmd("chmod", &["-v", ps, entry]);
+
+ if rc.status {
+ if let Some(op) = rc.output {
+ print!("{}", op);
+ }
+ true
+ } else {
+ false
+ }
+}
+
+// returns true if file is a directory and does exist
+// returns false otherwise
+pub fn exists_dir(file: &str) -> bool {
+ match fs::metadata(file) {
+ Ok(attr) => attr.is_dir(),
+ Err(_) => false,
+ }
+}
+
+// returns true if file is a regular file and does exist
+// returns false otherwise
+pub fn exists_file(file: &str) -> bool {
+ match fs::metadata(file) {
+ Ok(attr) => attr.is_file(),
+ Err(_) => false,
+ }
+}
+
+#[test]
+fn test_dirname() {
+ assert!(dirname("/etc/fstab") == Some("/etc"));
+ assert!(dirname("/etc/") == Some("/etc"));
+ assert!(dirname("/") == Some("/"));
+}
+
+#[allow(dead_code)]
+pub fn dirname(entry: &str) -> Option<&str> {
+ if entry.ends_with('/') {
+ if entry.len() == 1 {
+ return Some(entry);
+ }
+ return Some(&entry[..entry.len() - 1]);
+ }
+ let pos = entry.rfind('/');
+ match pos {
+ None => None,
+ Some(pos) => Some(&entry[..pos]),
+ }
+}
+
+#[test]
+fn test_filename() {
+ assert!(filename("/etc/fstab") == Some("fstab"));
+ assert!(filename("fstab") == Some("fstab"));
+ assert!(filename("../pkgcheck.rs/testdirs/fira.tds.zip") == Some("fira.tds.zip"));
+ assert!(filename("/etc/") == None);
+ assert!(filename("/") == None);
+}
+
+pub fn filename(entry: &str) -> Option<&str> {
+ if entry.ends_with('/') {
+ return None;
+ }
+
+ let pos = entry.rfind('/');
+ match pos {
+ None => Some(entry),
+ Some(pos) => Some(&entry[pos + 1..]),
+ }
+}
+
+// Found here: https://codereview.stackexchange.com/questions/98536/extracting-the-last-component-basename-of-a-filesystem-path
+pub fn basename(path: &str) -> Cow<str> {
+ let mut pieces = path.rsplitn(2, |c| c == '/' || c == '\\');
+ match pieces.next() {
+ Some(p) => p.into(),
+ None => path.into(),
+ }
+}